From d0e3628f26c42d3ace3c8ab66b6e17b92e56679a Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Wed, 14 Jun 2023 14:00:46 -0700 Subject: [PATCH 001/177] `Archmonger/reactpy-docs/main` to `reactive-python/reactpy/new-docs` --- .github/workflows/.hatch-run.yml | 59 - .github/workflows/check.yml | 45 - .github/workflows/codeql-analysis.yml | 71 - .github/workflows/deploy-docs.yml | 30 - .github/workflows/publish.yml | 20 - CHANGELOG.md | 199 ++ README.md | 73 +- docs/.gitignore | 4 - docs/Dockerfile | 40 - docs/LICENSE.md | 393 +++ docs/README.md | 20 - docs/docs_app/app.py | 59 - docs/docs_app/dev.py | 104 - docs/docs_app/examples.py | 177 - docs/docs_app/prod.py | 14 - .../css/quick_start/adding_styles.css | 4 + .../css/quick_start/displaying_data.css | 7 + .../sharing_data_between_components.css | 4 + .../css/quick_start/updating_the_screen.css | 4 + .../css/tutorial-tic-tac-toe/tic_tac_toe.css | 42 + .../{docs_app => examples/python}/__init__.py | 0 docs/examples/python/quick_start/__init__.py | 0 .../python/quick_start/adding_styles.py | 4 + .../quick_start/conditional_rendering.py | 22 + .../conditional_rendering_logical_and.py | 14 + .../conditional_rendering_ternary.py | 18 + .../creating_and_nesting_components.py | 21 + .../python/quick_start/displaying_data.py | 32 + docs/examples/python/quick_start/my_app.py | 12 + docs/examples/python/quick_start/my_button.py | 7 + .../python/quick_start/rendering_lists.py | 30 + .../quick_start/rendering_lists_list_items.py | 6 + .../quick_start/rendering_lists_products.py | 5 + .../quick_start/responding_to_events.py | 13 + .../sharing_data_between_components.py | 27 + .../sharing_data_between_components_button.py | 7 + ...ring_data_between_components_move_state.py | 20 + .../sharing_data_between_components_props.py | 22 + .../python/quick_start/updating_the_screen.py | 27 + .../quick_start/updating_the_screen_event.py | 12 + .../updating_the_screen_use_state.py | 5 + .../updating_the_screen_use_state_button.py | 8 + .../tutorial-tic-tac-toe/tic_tac_toe.py | 117 + docs/main.py | 9 - docs/overrides/main.html | 13 + docs/poetry.lock | 2269 ------------- docs/pyproject.toml | 23 - docs/requirements.txt | 8 + docs/source/_custom_js/README.md | 9 - docs/source/_custom_js/package-lock.json | 766 ----- docs/source/_custom_js/package.json | 20 - docs/source/_custom_js/rollup.config.js | 26 - docs/source/_custom_js/src/index.js | 94 - docs/source/_exts/async_doctest.py | 47 - docs/source/_exts/autogen_api_docs.py | 146 - docs/source/_exts/build_custom_js.py | 12 - docs/source/_exts/copy_vdom_json_schema.py | 17 - docs/source/_exts/custom_autosectionlabel.py | 80 - docs/source/_exts/patched_html_translator.py | 24 - docs/source/_exts/reactpy_example.py | 180 - docs/source/_exts/reactpy_view.py | 63 - .../_static/css/furo-theme-overrides.css | 6 - .../source/_static/css/larger-api-margins.css | 7 - docs/source/_static/css/larger-headings.css | 9 - docs/source/_static/css/reactpy-view.css | 43 - .../_static/css/sphinx-design-overrides.css | 14 - .../css/widget-output-css-overrides.css | 8 - .../_static/install-and-run-reactpy.gif | Bin 1049193 -> 0 bytes docs/source/about/changelog.rst | 1124 ------- docs/source/about/contributor-guide.rst | 331 -- docs/source/about/credits-and-licenses.rst | 16 - docs/source/conf.py | 330 -- .../_examples/adding_state_variable/data.json | 72 - .../_examples/adding_state_variable/main.py | 35 - .../_examples/isolated_state/data.json | 72 - .../_examples/isolated_state/main.py | 53 - .../multiple_state_variables/data.json | 72 - .../multiple_state_variables/main.py | 45 - .../when_variables_are_not_enough/data.json | 72 - .../when_variables_are_not_enough/main.py | 42 - .../components-with-state/index.rst | 351 -- .../_examples/dict_remove.py | 58 - .../_examples/dict_update.py | 44 - .../_examples/list_insert.py | 25 - .../_examples/list_re_order.py | 24 - .../_examples/list_remove.py | 44 - .../_examples/list_replace.py | 27 - .../_examples/moving_dot.py | 45 - .../_examples/moving_dot_broken.py | 43 - .../_examples/set_remove.py | 41 - .../_examples/set_update.py | 38 - .../dangers-of-mutability/index.rst | 569 ---- .../guides/adding-interactivity/index.rst | 210 -- .../_examples/delay_before_count_updater.py | 20 - .../_examples/delay_before_set_count.py | 20 - .../_examples/set_color_3_times.py | 27 - .../_examples/set_state_function.py | 24 - .../multiple-state-updates/index.rst | 109 - .../_examples/audio_player.py | 21 - .../_examples/button_async_handlers.py | 23 - .../_examples/button_does_nothing.py | 9 - .../_examples/button_handler_as_arg.py | 33 - .../_examples/button_prints_event.py | 12 - .../_examples/button_prints_message.py | 20 - .../prevent_default_event_actions.py | 18 - .../_examples/stop_event_propagation.py | 42 - .../responding-to-events/index.rst | 144 - .../_examples/delayed_print_after_set.py | 22 - .../_examples/print_chat_message.py | 44 - .../_examples/print_count_after_set.py | 18 - .../_examples/send_message.py | 35 - .../_examples/set_counter_3_times.py | 19 - .../_static/direct-state-change.png | Bin 181824 -> 0 bytes .../_static/reactpy-state-change.png | Bin 569027 -> 0 bytes .../state-as-a-snapshot/index.rst | 158 - .../html-with-reactpy/index.rst | 131 - .../guides/creating-interfaces/index.rst | 128 - .../sorted_and_filtered_todo_list.py | 32 - .../_examples/todo_from_list.py | 28 - .../_examples/todo_list_with_keys.py | 32 - .../rendering-data/index.rst | 297 -- .../_examples/bad_conditional_todo_list.py | 24 - .../_examples/good_conditional_todo_list.py | 21 - .../_examples/nested_photos.py | 25 - .../_examples/parametrized_photos.py | 25 - .../_examples/simple_photo.py | 15 - .../_examples/todo_list.py | 21 - .../_examples/wrap_in_div.py | 13 - .../_examples/wrap_in_fragment.py | 13 - .../your-first-components/index.rst | 134 - .../_examples/material_ui_button_no_action.py | 16 - .../_examples/material_ui_button_on_click.py | 30 - .../_examples/super_simple_chart/main.py | 32 - .../super_simple_chart/super-simple-chart.js | 82 - .../distributing-javascript.rst | 307 -- docs/source/guides/escape-hatches/index.rst | 14 - .../escape-hatches/javascript-components.rst | 146 - .../escape-hatches/using-a-custom-backend.rst | 9 - .../escape-hatches/using-a-custom-client.rst | 9 - .../_examples/debug_error_example.py | 20 - .../getting-started/_examples/hello_world.py | 9 - .../getting-started/_examples/run_fastapi.py | 22 - .../getting-started/_examples/run_flask.py | 22 - .../getting-started/_examples/run_sanic.py | 26 - .../_examples/run_starlette.py | 22 - .../getting-started/_examples/run_tornado.py | 31 - .../getting-started/_examples/sample_app.py | 3 - .../getting-started/_static/embed-doc-ex.html | 8 - .../_static/embed-reactpy-view/index.html | 31 - .../_static/embed-reactpy-view/main.py | 22 - .../_static/embed-reactpy-view/screenshot.png | Bin 13161 -> 0 bytes .../getting-started/_static/logo-django.svg | 38 - .../getting-started/_static/logo-jupyter.svg | 88 - .../getting-started/_static/logo-plotly.svg | 11 - .../_static/reactpy-in-jupyterlab.gif | Bin 551674 -> 0 bytes .../shared-client-state-server-slider.gif | Bin 98006 -> 0 bytes docs/source/guides/getting-started/index.rst | 123 - .../getting-started/installing-reactpy.rst | 121 - .../getting-started/running-reactpy.rst | 221 -- .../combining-contexts-and-reducers/index.rst | 6 - .../index.rst | 6 - .../how-to-structure-state/index.rst | 8 - docs/source/guides/managing-state/index.rst | 127 - .../_examples/filterable_list/data.json | 22 - .../_examples/filterable_list/main.py | 44 - .../_examples/synced_inputs/main.py | 23 - .../sharing-component-state/index.rst | 38 - .../index.rst | 6 - .../when-and-how-to-reset-state/index.rst | 8 - .../_static/idom-flow-diagram.svg | 383 --- .../_static/live-examples-in-docs.gif | Bin 182779 -> 0 bytes .../_static/mvc-flow-diagram.svg | 425 --- .../_static/npm-download-trends.png | Bin 64372 -> 0 bytes .../guides/understanding-reactpy/index.rst | 17 - .../layout-render-servers.rst | 8 - .../representing-html.rst | 76 - .../the-rendering-pipeline.rst | 10 - .../the-rendering-process.rst | 10 - .../what-are-components.rst | 8 - .../why-reactpy-needs-keys.rst | 8 - .../understanding-reactpy/writing-tests.rst | 8 - docs/source/index.rst | 206 -- .../_examples/character_movement/main.py | 73 - .../character_movement/static/bunny.png | Bin 474 -> 0 bytes .../source/reference/_examples/click_count.py | 13 - .../reference/_examples/material_ui_switch.py | 22 - .../reference/_examples/matplotlib_plot.py | 85 - .../reference/_examples/network_graph.py | 40 - .../source/reference/_examples/pigeon_maps.py | 46 - .../reference/_examples/simple_dashboard.py | 102 - docs/source/reference/_examples/slideshow.py | 20 - docs/source/reference/_examples/snake_game.py | 188 -- docs/source/reference/_examples/todo.py | 35 - .../_examples/use_reducer_counter.py | 27 - .../reference/_examples/use_state_counter.py | 26 - .../reference/_examples/victory_chart.py | 7 - .../reference/_static/vdom-json-schema.json | 106 - docs/source/reference/browser-events.rst | 65 - docs/source/reference/hooks-api.rst | 379 --- docs/source/reference/html-attributes.rst | 197 -- docs/source/reference/javascript-api.rst | 8 - docs/source/reference/specifications.rst | 170 - docs/src/about/changelog.md | 11 + docs/src/about/code.md | 3 + docs/src/about/community.md | 3 + docs/src/about/docs.md | 3 + docs/src/about/running-tests.md | 3 + docs/src/dictionary.txt | 28 + docs/src/index.md | 57 + .../learn/add-react-to-an-existing-project.md | 153 + .../src/learn/choosing-the-state-structure.md | 2866 ++++++++++++++++ ...municate-data-between-server-and-client.md | 3 + docs/src/learn/conditional-rendering.md | 576 ++++ .../learn/convert-between-vdom-and-html.md | 3 + docs/src/learn/creating-backends.md | 3 + docs/src/learn/creating-html-tags.md | 3 + .../src/learn/creating-vdom-event-handlers.md | 3 + docs/src/learn/editor-setup.md | 62 + .../extracting-state-logic-into-a-reducer.md | 2644 +++++++++++++++ docs/src/learn/get-started.md | 303 ++ .../importing-and-exporting-components.md | 382 +++ docs/src/learn/keeping-components-pure.md | 815 +++++ .../learn/lifecycle-of-reactive-effects.md | 2252 +++++++++++++ .../learn/manipulating-the-dom-with-refs.md | 1078 ++++++ docs/src/learn/manually-register-a-client.md | 3 + .../learn/passing-data-deeply-with-context.md | 1092 ++++++ .../src/learn/passing-props-to-a-component.md | 1080 ++++++ .../learn/preserving-and-resetting-state.md | 2027 +++++++++++ .../learn/python-in-psx-with-curly-braces.md | 591 ++++ .../queueing-a-series-of-state-updates.md | 560 +++ docs/src/learn/react-developer-tools.md | 85 + .../src/learn/reacting-to-input-with-state.md | 1179 +++++++ .../src/learn/referencing-values-with-refs.md | 569 ++++ .../src/learn/removing-effect-dependencies.md | 2421 +++++++++++++ docs/src/learn/render-and-commit.md | 201 ++ docs/src/learn/rendering-lists.md | 1272 +++++++ docs/src/learn/responding-to-events.md | 648 ++++ .../learn/reusing-logic-with-custom-hooks.md | 2519 ++++++++++++++ .../scaling-up-with-reducer-and-context.md | 1373 ++++++++ .../learn/separating-events-from-effects.md | 1888 +++++++++++ .../learn/sharing-state-between-components.md | 579 ++++ docs/src/learn/start-a-new-react-project.md | 133 + docs/src/learn/state-a-components-memory.md | 1652 +++++++++ docs/src/learn/state-as-a-snapshot.md | 472 +++ docs/src/learn/synchronizing-with-effects.md | 1550 +++++++++ docs/src/learn/thinking-in-react.md | 631 ++++ docs/src/learn/tutorial-material-ui.md | 3 + docs/src/learn/tutorial-react-bootstrap.md | 3 + docs/src/learn/tutorial-tic-tac-toe.md | 2996 +++++++++++++++++ docs/src/learn/updating-arrays-in-state.md | 1853 ++++++++++ docs/src/learn/updating-objects-in-state.md | 1569 +++++++++ docs/src/learn/vdom-mutations.md | 3 + docs/src/learn/writing-markup-with-psx.md | 326 ++ .../src/learn/you-might-not-need-an-effect.md | 1702 ++++++++++ docs/src/learn/your-first-component.md | 404 +++ docs/src/reference/client-api.md | 3 + docs/src/reference/common-events.md | 3 + docs/src/reference/common-props.md | 3 + docs/src/reference/common-types.md | 3 + docs/src/reference/django.md | 3 + docs/src/reference/fastapi.md | 3 + docs/src/reference/flask.md | 3 + docs/src/reference/jupyter.md | 3 + docs/src/reference/plotly-dash.md | 3 + docs/src/reference/protocol-structure.md | 3 + docs/src/reference/sanic.md | 3 + docs/src/reference/starlette.md | 3 + docs/src/reference/tornado.md | 3 + docs/src/reference/usage.md | 3 + docs/src/reference/use-callback.md | 3 + docs/src/reference/use-connection.md | 3 + docs/src/reference/use-context.md | 3 + docs/src/reference/use-debug-value.md | 3 + docs/src/reference/use-effect.md | 3 + docs/src/reference/use-location.md | 3 + docs/src/reference/use-memo.md | 3 + docs/src/reference/use-reducer.md | 3 + docs/src/reference/use-ref.md | 3 + docs/src/reference/use-scope.md | 3 + docs/src/reference/use-state.md | 3 + docs/src/static/css/extra.css | 323 ++ .../static/images/s_thinking-in-react_ui.png | Bin 0 -> 25981 bytes .../images/s_thinking-in-react_ui_outline.png | Bin 0 -> 83418 bytes docs/src/static/js/extra.js | 19 + mkdocs.yml | 178 + 285 files changed, 44339 insertions(+), 14644 deletions(-) delete mode 100644 .github/workflows/.hatch-run.yml delete mode 100644 .github/workflows/check.yml delete mode 100644 .github/workflows/codeql-analysis.yml delete mode 100644 .github/workflows/deploy-docs.yml delete mode 100644 .github/workflows/publish.yml create mode 100644 CHANGELOG.md delete mode 100644 docs/.gitignore delete mode 100644 docs/Dockerfile create mode 100644 docs/LICENSE.md delete mode 100644 docs/README.md delete mode 100644 docs/docs_app/app.py delete mode 100644 docs/docs_app/dev.py delete mode 100644 docs/docs_app/examples.py delete mode 100644 docs/docs_app/prod.py create mode 100644 docs/examples/css/quick_start/adding_styles.css create mode 100644 docs/examples/css/quick_start/displaying_data.css create mode 100644 docs/examples/css/quick_start/sharing_data_between_components.css create mode 100644 docs/examples/css/quick_start/updating_the_screen.css create mode 100644 docs/examples/css/tutorial-tic-tac-toe/tic_tac_toe.css rename docs/{docs_app => examples/python}/__init__.py (100%) create mode 100644 docs/examples/python/quick_start/__init__.py create mode 100644 docs/examples/python/quick_start/adding_styles.py create mode 100644 docs/examples/python/quick_start/conditional_rendering.py create mode 100644 docs/examples/python/quick_start/conditional_rendering_logical_and.py create mode 100644 docs/examples/python/quick_start/conditional_rendering_ternary.py create mode 100644 docs/examples/python/quick_start/creating_and_nesting_components.py create mode 100644 docs/examples/python/quick_start/displaying_data.py create mode 100644 docs/examples/python/quick_start/my_app.py create mode 100644 docs/examples/python/quick_start/my_button.py create mode 100644 docs/examples/python/quick_start/rendering_lists.py create mode 100644 docs/examples/python/quick_start/rendering_lists_list_items.py create mode 100644 docs/examples/python/quick_start/rendering_lists_products.py create mode 100644 docs/examples/python/quick_start/responding_to_events.py create mode 100644 docs/examples/python/quick_start/sharing_data_between_components.py create mode 100644 docs/examples/python/quick_start/sharing_data_between_components_button.py create mode 100644 docs/examples/python/quick_start/sharing_data_between_components_move_state.py create mode 100644 docs/examples/python/quick_start/sharing_data_between_components_props.py create mode 100644 docs/examples/python/quick_start/updating_the_screen.py create mode 100644 docs/examples/python/quick_start/updating_the_screen_event.py create mode 100644 docs/examples/python/quick_start/updating_the_screen_use_state.py create mode 100644 docs/examples/python/quick_start/updating_the_screen_use_state_button.py create mode 100644 docs/examples/python/tutorial-tic-tac-toe/tic_tac_toe.py delete mode 100644 docs/main.py create mode 100644 docs/overrides/main.html delete mode 100644 docs/poetry.lock delete mode 100644 docs/pyproject.toml create mode 100644 docs/requirements.txt delete mode 100644 docs/source/_custom_js/README.md delete mode 100644 docs/source/_custom_js/package-lock.json delete mode 100644 docs/source/_custom_js/package.json delete mode 100644 docs/source/_custom_js/rollup.config.js delete mode 100644 docs/source/_custom_js/src/index.js delete mode 100644 docs/source/_exts/async_doctest.py delete mode 100644 docs/source/_exts/autogen_api_docs.py delete mode 100644 docs/source/_exts/build_custom_js.py delete mode 100644 docs/source/_exts/copy_vdom_json_schema.py delete mode 100644 docs/source/_exts/custom_autosectionlabel.py delete mode 100644 docs/source/_exts/patched_html_translator.py delete mode 100644 docs/source/_exts/reactpy_example.py delete mode 100644 docs/source/_exts/reactpy_view.py delete mode 100644 docs/source/_static/css/furo-theme-overrides.css delete mode 100644 docs/source/_static/css/larger-api-margins.css delete mode 100644 docs/source/_static/css/larger-headings.css delete mode 100644 docs/source/_static/css/reactpy-view.css delete mode 100644 docs/source/_static/css/sphinx-design-overrides.css delete mode 100644 docs/source/_static/css/widget-output-css-overrides.css delete mode 100644 docs/source/_static/install-and-run-reactpy.gif delete mode 100644 docs/source/about/changelog.rst delete mode 100644 docs/source/about/contributor-guide.rst delete mode 100644 docs/source/about/credits-and-licenses.rst delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/data.json delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/data.json delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py delete mode 100644 docs/source/guides/adding-interactivity/components-with-state/index.rst delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_remove.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_remove.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_update.py delete mode 100644 docs/source/guides/adding-interactivity/dangers-of-mutability/index.rst delete mode 100644 docs/source/guides/adding-interactivity/index.rst delete mode 100644 docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py delete mode 100644 docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py delete mode 100644 docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py delete mode 100644 docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_state_function.py delete mode 100644 docs/source/guides/adding-interactivity/multiple-state-updates/index.rst delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/audio_player.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/button_async_handlers.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/button_does_nothing.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_event.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_message.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py delete mode 100644 docs/source/guides/adding-interactivity/responding-to-events/index.rst delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/delayed_print_after_set.py delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/print_chat_message.py delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/print_count_after_set.py delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/send_message.py delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/set_counter_3_times.py delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_static/direct-state-change.png delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/_static/reactpy-state-change.png delete mode 100644 docs/source/guides/adding-interactivity/state-as-a-snapshot/index.rst delete mode 100644 docs/source/guides/creating-interfaces/html-with-reactpy/index.rst delete mode 100644 docs/source/guides/creating-interfaces/index.rst delete mode 100644 docs/source/guides/creating-interfaces/rendering-data/_examples/sorted_and_filtered_todo_list.py delete mode 100644 docs/source/guides/creating-interfaces/rendering-data/_examples/todo_from_list.py delete mode 100644 docs/source/guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys.py delete mode 100644 docs/source/guides/creating-interfaces/rendering-data/index.rst delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/bad_conditional_todo_list.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/good_conditional_todo_list.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/nested_photos.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/parametrized_photos.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/simple_photo.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/todo_list.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_div.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_fragment.py delete mode 100644 docs/source/guides/creating-interfaces/your-first-components/index.rst delete mode 100644 docs/source/guides/escape-hatches/_examples/material_ui_button_no_action.py delete mode 100644 docs/source/guides/escape-hatches/_examples/material_ui_button_on_click.py delete mode 100644 docs/source/guides/escape-hatches/_examples/super_simple_chart/main.py delete mode 100644 docs/source/guides/escape-hatches/_examples/super_simple_chart/super-simple-chart.js delete mode 100644 docs/source/guides/escape-hatches/distributing-javascript.rst delete mode 100644 docs/source/guides/escape-hatches/index.rst delete mode 100644 docs/source/guides/escape-hatches/javascript-components.rst delete mode 100644 docs/source/guides/escape-hatches/using-a-custom-backend.rst delete mode 100644 docs/source/guides/escape-hatches/using-a-custom-client.rst delete mode 100644 docs/source/guides/getting-started/_examples/debug_error_example.py delete mode 100644 docs/source/guides/getting-started/_examples/hello_world.py delete mode 100644 docs/source/guides/getting-started/_examples/run_fastapi.py delete mode 100644 docs/source/guides/getting-started/_examples/run_flask.py delete mode 100644 docs/source/guides/getting-started/_examples/run_sanic.py delete mode 100644 docs/source/guides/getting-started/_examples/run_starlette.py delete mode 100644 docs/source/guides/getting-started/_examples/run_tornado.py delete mode 100644 docs/source/guides/getting-started/_examples/sample_app.py delete mode 100644 docs/source/guides/getting-started/_static/embed-doc-ex.html delete mode 100644 docs/source/guides/getting-started/_static/embed-reactpy-view/index.html delete mode 100644 docs/source/guides/getting-started/_static/embed-reactpy-view/main.py delete mode 100644 docs/source/guides/getting-started/_static/embed-reactpy-view/screenshot.png delete mode 100644 docs/source/guides/getting-started/_static/logo-django.svg delete mode 100644 docs/source/guides/getting-started/_static/logo-jupyter.svg delete mode 100644 docs/source/guides/getting-started/_static/logo-plotly.svg delete mode 100644 docs/source/guides/getting-started/_static/reactpy-in-jupyterlab.gif delete mode 100644 docs/source/guides/getting-started/_static/shared-client-state-server-slider.gif delete mode 100644 docs/source/guides/getting-started/index.rst delete mode 100644 docs/source/guides/getting-started/installing-reactpy.rst delete mode 100644 docs/source/guides/getting-started/running-reactpy.rst delete mode 100644 docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst delete mode 100644 docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst delete mode 100644 docs/source/guides/managing-state/how-to-structure-state/index.rst delete mode 100644 docs/source/guides/managing-state/index.rst delete mode 100644 docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json delete mode 100644 docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py delete mode 100644 docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py delete mode 100644 docs/source/guides/managing-state/sharing-component-state/index.rst delete mode 100644 docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst delete mode 100644 docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst delete mode 100644 docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg delete mode 100644 docs/source/guides/understanding-reactpy/_static/live-examples-in-docs.gif delete mode 100644 docs/source/guides/understanding-reactpy/_static/mvc-flow-diagram.svg delete mode 100644 docs/source/guides/understanding-reactpy/_static/npm-download-trends.png delete mode 100644 docs/source/guides/understanding-reactpy/index.rst delete mode 100644 docs/source/guides/understanding-reactpy/layout-render-servers.rst delete mode 100644 docs/source/guides/understanding-reactpy/representing-html.rst delete mode 100644 docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst delete mode 100644 docs/source/guides/understanding-reactpy/the-rendering-process.rst delete mode 100644 docs/source/guides/understanding-reactpy/what-are-components.rst delete mode 100644 docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst delete mode 100644 docs/source/guides/understanding-reactpy/writing-tests.rst delete mode 100644 docs/source/index.rst delete mode 100644 docs/source/reference/_examples/character_movement/main.py delete mode 100644 docs/source/reference/_examples/character_movement/static/bunny.png delete mode 100644 docs/source/reference/_examples/click_count.py delete mode 100644 docs/source/reference/_examples/material_ui_switch.py delete mode 100644 docs/source/reference/_examples/matplotlib_plot.py delete mode 100644 docs/source/reference/_examples/network_graph.py delete mode 100644 docs/source/reference/_examples/pigeon_maps.py delete mode 100644 docs/source/reference/_examples/simple_dashboard.py delete mode 100644 docs/source/reference/_examples/slideshow.py delete mode 100644 docs/source/reference/_examples/snake_game.py delete mode 100644 docs/source/reference/_examples/todo.py delete mode 100644 docs/source/reference/_examples/use_reducer_counter.py delete mode 100644 docs/source/reference/_examples/use_state_counter.py delete mode 100644 docs/source/reference/_examples/victory_chart.py delete mode 100644 docs/source/reference/_static/vdom-json-schema.json delete mode 100644 docs/source/reference/browser-events.rst delete mode 100644 docs/source/reference/hooks-api.rst delete mode 100644 docs/source/reference/html-attributes.rst delete mode 100644 docs/source/reference/javascript-api.rst delete mode 100644 docs/source/reference/specifications.rst create mode 100644 docs/src/about/changelog.md create mode 100644 docs/src/about/code.md create mode 100644 docs/src/about/community.md create mode 100644 docs/src/about/docs.md create mode 100644 docs/src/about/running-tests.md create mode 100644 docs/src/dictionary.txt create mode 100644 docs/src/index.md create mode 100644 docs/src/learn/add-react-to-an-existing-project.md create mode 100644 docs/src/learn/choosing-the-state-structure.md create mode 100644 docs/src/learn/communicate-data-between-server-and-client.md create mode 100644 docs/src/learn/conditional-rendering.md create mode 100644 docs/src/learn/convert-between-vdom-and-html.md create mode 100644 docs/src/learn/creating-backends.md create mode 100644 docs/src/learn/creating-html-tags.md create mode 100644 docs/src/learn/creating-vdom-event-handlers.md create mode 100644 docs/src/learn/editor-setup.md create mode 100644 docs/src/learn/extracting-state-logic-into-a-reducer.md create mode 100644 docs/src/learn/get-started.md create mode 100644 docs/src/learn/importing-and-exporting-components.md create mode 100644 docs/src/learn/keeping-components-pure.md create mode 100644 docs/src/learn/lifecycle-of-reactive-effects.md create mode 100644 docs/src/learn/manipulating-the-dom-with-refs.md create mode 100644 docs/src/learn/manually-register-a-client.md create mode 100644 docs/src/learn/passing-data-deeply-with-context.md create mode 100644 docs/src/learn/passing-props-to-a-component.md create mode 100644 docs/src/learn/preserving-and-resetting-state.md create mode 100644 docs/src/learn/python-in-psx-with-curly-braces.md create mode 100644 docs/src/learn/queueing-a-series-of-state-updates.md create mode 100644 docs/src/learn/react-developer-tools.md create mode 100644 docs/src/learn/reacting-to-input-with-state.md create mode 100644 docs/src/learn/referencing-values-with-refs.md create mode 100644 docs/src/learn/removing-effect-dependencies.md create mode 100644 docs/src/learn/render-and-commit.md create mode 100644 docs/src/learn/rendering-lists.md create mode 100644 docs/src/learn/responding-to-events.md create mode 100644 docs/src/learn/reusing-logic-with-custom-hooks.md create mode 100644 docs/src/learn/scaling-up-with-reducer-and-context.md create mode 100644 docs/src/learn/separating-events-from-effects.md create mode 100644 docs/src/learn/sharing-state-between-components.md create mode 100644 docs/src/learn/start-a-new-react-project.md create mode 100644 docs/src/learn/state-a-components-memory.md create mode 100644 docs/src/learn/state-as-a-snapshot.md create mode 100644 docs/src/learn/synchronizing-with-effects.md create mode 100644 docs/src/learn/thinking-in-react.md create mode 100644 docs/src/learn/tutorial-material-ui.md create mode 100644 docs/src/learn/tutorial-react-bootstrap.md create mode 100644 docs/src/learn/tutorial-tic-tac-toe.md create mode 100644 docs/src/learn/updating-arrays-in-state.md create mode 100644 docs/src/learn/updating-objects-in-state.md create mode 100644 docs/src/learn/vdom-mutations.md create mode 100644 docs/src/learn/writing-markup-with-psx.md create mode 100644 docs/src/learn/you-might-not-need-an-effect.md create mode 100644 docs/src/learn/your-first-component.md create mode 100644 docs/src/reference/client-api.md create mode 100644 docs/src/reference/common-events.md create mode 100644 docs/src/reference/common-props.md create mode 100644 docs/src/reference/common-types.md create mode 100644 docs/src/reference/django.md create mode 100644 docs/src/reference/fastapi.md create mode 100644 docs/src/reference/flask.md create mode 100644 docs/src/reference/jupyter.md create mode 100644 docs/src/reference/plotly-dash.md create mode 100644 docs/src/reference/protocol-structure.md create mode 100644 docs/src/reference/sanic.md create mode 100644 docs/src/reference/starlette.md create mode 100644 docs/src/reference/tornado.md create mode 100644 docs/src/reference/usage.md create mode 100644 docs/src/reference/use-callback.md create mode 100644 docs/src/reference/use-connection.md create mode 100644 docs/src/reference/use-context.md create mode 100644 docs/src/reference/use-debug-value.md create mode 100644 docs/src/reference/use-effect.md create mode 100644 docs/src/reference/use-location.md create mode 100644 docs/src/reference/use-memo.md create mode 100644 docs/src/reference/use-reducer.md create mode 100644 docs/src/reference/use-ref.md create mode 100644 docs/src/reference/use-scope.md create mode 100644 docs/src/reference/use-state.md create mode 100644 docs/src/static/css/extra.css create mode 100644 docs/src/static/images/s_thinking-in-react_ui.png create mode 100644 docs/src/static/images/s_thinking-in-react_ui_outline.png create mode 100644 docs/src/static/js/extra.js create mode 100644 mkdocs.yml diff --git a/.github/workflows/.hatch-run.yml b/.github/workflows/.hatch-run.yml deleted file mode 100644 index b312869e4..000000000 --- a/.github/workflows/.hatch-run.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: hatch-run - -on: - workflow_call: - inputs: - job-name: - required: true - type: string - hatch-run: - required: true - type: string - runs-on-array: - required: false - type: string - default: '["ubuntu-latest"]' - python-version-array: - required: false - type: string - default: '["3.x"]' - node-registry-url: - required: false - type: string - default: "" - secrets: - node-auth-token: - required: false - pypi-username: - required: false - pypi-password: - required: false - -jobs: - hatch: - name: ${{ format(inputs.job-name, matrix.python-version, matrix.runs-on) }} - strategy: - matrix: - python-version: ${{ fromJson(inputs.python-version-array) }} - runs-on: ${{ fromJson(inputs.runs-on-array) }} - runs-on: ${{ matrix.runs-on }} - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - registry-url: ${{ inputs.node-registry-url }} - - name: Pin NPM Version - run: npm install -g npm@8.19.3 - - name: Use Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install Python Dependencies - run: pip install hatch poetry - - name: Run Scripts - env: - NODE_AUTH_TOKEN: ${{ secrets.node-auth-token }} - PYPI_USERNAME: ${{ secrets.pypi-username }} - PYPI_PASSWORD: ${{ secrets.pypi-password }} - run: hatch run ${{ inputs.hatch-run }} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml deleted file mode 100644 index af768579c..000000000 --- a/.github/workflows/check.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: check - -on: - push: - branches: - - main - pull_request: - branches: - - main - schedule: - - cron: "0 0 * * 0" - -jobs: - test-py-cov: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "python-{0}" - hatch-run: "test-py" - lint-py: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "python-{0}" - hatch-run: "lint-py" - test-py-matrix: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "python-{0} {1}" - hatch-run: "test-py --no-cov" - runs-on-array: '["ubuntu-latest", "macos-latest", "windows-latest"]' - python-version-array: '["3.9", "3.10", "3.11"]' - test-docs: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "python-{0}" - hatch-run: "test-docs" - test-js: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "{1}" - hatch-run: "test-js" - lint-js: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "{1}" - hatch-run: "lint-js" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index b4f77ee00..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,71 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: codeql - -on: - push: - branches: [main] - pull_request: - # The branches below must be a subset of the branches above - branches: [main] - schedule: - - cron: "43 3 * * 3" - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ["javascript", "python"] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml deleted file mode 100644 index 7337f505b..000000000 --- a/.github/workflows/deploy-docs.yml +++ /dev/null @@ -1,30 +0,0 @@ -# This workflows will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: deploy-docs - -on: - push: - branches: - - "main" - tags: - - "*" - -jobs: - deploy-documentation: - runs-on: ubuntu-latest - steps: - - name: Check out src from Git - uses: actions/checkout@v2 - - name: Get history and tags for SCM versioning to work - run: | - git fetch --prune --unshallow - git fetch --depth=1 origin +refs/tags/*:refs/tags/* - - name: Login to Heroku Container Registry - run: echo ${{ secrets.HEROKU_API_KEY }} | docker login -u ${{ secrets.HEROKU_EMAIL }} --password-stdin registry.heroku.com - - name: Build Docker Image - run: docker build . --file docs/Dockerfile --tag registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web - - name: Push Docker Image - run: docker push registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web - - name: Deploy - run: HEROKU_API_KEY=${{ secrets.HEROKU_API_KEY }} heroku container:release web --app ${{ secrets.HEROKU_APP_NAME }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index e9271cbd5..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,20 +0,0 @@ -# This workflows will upload a Javscript Package using NPM to npmjs.org when a release is created -# For more information see: https://docs.github.com/en/actions/guides/publishing-nodejs-packages - -name: publish - -on: - release: - types: [published] - -jobs: - publish: - uses: ./.github/workflows/.hatch-run.yml - with: - job-name: "publish" - hatch-run: "publish" - node-registry-url: "https://registry.npmjs.org" - secrets: - node-auth-token: ${{ secrets.NODE_AUTH_TOKEN }} - pypi-username: ${{ secrets.PYPI_USERNAME }} - pypi-password: ${{ secrets.PYPI_PASSWORD }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..5ae0eb0af --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,199 @@ +# Changelog + +All notable changes to this project will be documented in this file. + + + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + + + + + + +## [Unreleased] + +- Nothing (yet) + +## [2.2.0] - 2022-12-28 + +### Added + +- Add `options: QueryOptions` parameter to `use_query` to allow for configuration of this hook. + +### Changed + +- By default, `use_query` will recursively prefetch all many-to-many or many-to-one relationships to prevent `SynchronousOnlyOperation` exceptions. + +### Removed + +- `django_idom.hooks._fetch_lazy_fields` has been deleted. The equivalent replacement is `django_idom.utils.django_query_postprocessor`. + +## [2.1.0] - 2022-11-01 + +### Changed + +- Minimum `channels` version is now `4.0.0`. + +### Fixed + +- Change type hint on `view_to_component` callable to have `request` argument be optional. +- Change type hint on `view_to_component` to represent it as a decorator with parenthesis (such as `@view_to_component(compatibility=True)`) + +### Security + +- Add note to docs about potential information exposure via `view_to_component` when using `compatibility=True`. + +## [2.0.1] - 2022-10-18 + +### Fixed + +- Ability to use `key=...` parameter on all prefabricated components. + +## [2.0.0] - 2022-10-17 + +### Added + +- `use_origin` hook for returning the browser's `location.origin`. + +### Changed + +- `view_to_component` now returns a `Callable`, instead of directly returning a `Component`. Check the docs for new usage info. +- `use_mutation` and `use_query` will now log any query failures. + +### Fixed + +- Allow `use_mutation` to have `refetch=None`, as the docs suggest is possible. +- `use_query` will now prefetch all fields to prevent `SynchronousOnlyOperation` exceptions. +- `view_to_component`, `django_css`, and `django_js` type hints will now display like normal functions. +- IDOM preloader no longer attempts to parse commented out IDOM components. +- Tests are now fully functional on Windows + +## [1.2.0] - 2022-09-19 + +### Added + +- `auth_required` decorator to prevent your components from rendering to unauthenticated users. +- `use_query` hook for fetching database values. +- `use_mutation` hook for modifying database values. +- `view_to_component` utility to convert legacy Django views to IDOM components. + +### Changed + +- Bumped the minimum IDOM version to 0.40.2 +- Testing suite now uses `playwright` instead of `selenium` + +### Fixed + +- IDOM preloader is no longer sensitive to whitespace within template tags. + +## [1.1.0] - 2022-07-01 + +### Added + +- `django_css` and `django_js` components to defer loading CSS & JS files until needed. + +### Changed + +- Bumped the minimum IDOM version to 0.39.0 + +## [1.0.0] - 2022-05-22 + +### Added + +- Django-specific hooks! `use_websocket`, `use_scope`, and `use_location` are now available within the `django_idom.hooks` module. +- Documentation has been placed into a formal docs webpage. +- Logging for when a component fails to import, or if no components were found within Django. + +### Changed + +- `idom_component` template tag has been renamed to `component` +- Bumped the minimum IDOM version to 0.38.0 + +### Removed + +- `websocket` parameter for components has been removed. Functionally, it is replaced with `django_idom.hooks.use_websocket`. + +## [0.0.5] - 2022-04-04 + +### Changed + +- Bumped the minimum IDOM version to 0.37.2 + +### Fixed + +- ModuleNotFoundError: No module named `idom.core.proto` caused by IDOM 0.37.2 + +## [0.0.4] - 2022-03-05 + +### Changed + +- Bumped the minimum IDOM version to 0.37.1 + +## [0.0.3] - 2022-02-19 + +### Changed + +- Bumped the minimum IDOM version to 0.36.3 + +## [0.0.2] - 2022-01-30 + +### Added + +- Ability to declare the HTML class of the top-level component `div` +- `name = ...` parameter to IDOM HTTP paths for use with `django.urls.reverse()` +- Cache versioning to automatically invalidate old web module files from the cache back-end +- Automatic pre-population of the IDOM component registry +- Type hinting for `IdomWebsocket` + +### Changed + +- Fetching web modules from disk and/or cache is now fully async +- Static files are now contained within a `django_idom/` parent folder +- Upgraded IDOM to version `0.36.0` +- Minimum Django version required is now `4.0` +- Minimum Python version required is now `3.8` + +### Removed + +- `IDOM_WEB_MODULES_PATH` has been replaced with Django `include(...)` +- `IDOM_WS_MAX_RECONNECT_DELAY` has been renamed to `IDOM_WS_MAX_RECONNECT_TIMEOUT` +- `idom_web_modules` cache back-end has been renamed to `idom` + +### Fixed + +- Increase test timeout values to prevent false positives +- Windows compatibility for building Django-IDOM + +### Security + +- Fixed potential directory traversal attack on the IDOM web modules URL + +## [0.0.1] - 2021-08-18 + +### Added + +- Support for IDOM within the Django + +[unreleased]: https://github.com/idom-team/django-idom/compare/2.2.0...HEAD +[2.2.0]: https://github.com/idom-team/django-idom/compare/2.1.0...2.2.0 +[2.1.0]: https://github.com/idom-team/django-idom/compare/2.0.1...2.1.0 +[2.0.1]: https://github.com/idom-team/django-idom/compare/2.0.0...2.0.1 +[2.0.0]: https://github.com/idom-team/django-idom/compare/1.2.0...2.0.0 +[1.2.0]: https://github.com/idom-team/django-idom/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/idom-team/django-idom/compare/1.0.0...1.1.0 +[1.0.0]: https://github.com/idom-team/django-idom/compare/0.0.5...1.0.0 +[0.0.5]: https://github.com/idom-team/django-idom/compare/0.0.4...0.0.5 +[0.0.4]: https://github.com/idom-team/django-idom/compare/0.0.3...0.0.4 +[0.0.3]: https://github.com/idom-team/django-idom/compare/0.0.2...0.0.3 +[0.0.2]: https://github.com/idom-team/django-idom/compare/0.0.1...0.0.2 +[0.0.1]: https://github.com/idom-team/django-idom/releases/tag/0.0.1 diff --git a/README.md b/README.md index 83241e19a..0086a306a 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,5 @@ -# ReactPy +Temporary branch being used to rewrite ReactPy's documentation. -

- - - - - - - - - - - - - - - -

+Many of these pages are in progress, and are using the [original ReactJS pages](https://beta.reactjs.org/learn) as placeholders. - -[ReactPy](https://reactpy.dev/) is a library for building user interfaces in Python without Javascript. ReactPy interfaces are made from components that look and behave similar to those found in [ReactJS](https://reactjs.org/). Designed with simplicity in mind, ReactPy can be used by those without web development experience while also being powerful enough to grow with your ambitions. - - - - - - - - - - - - - - - - -
Supported Backends
Built-inExternal
- - Flask, FastAPI, Sanic, Tornado - - - Django, - Jupyter, - Plotly-Dash -
- -# At a Glance - -To get a rough idea of how to write apps in ReactPy, take a look at this tiny _Hello World_ application. - -```python -from reactpy import component, html, run - -@component -def hello_world(): - return html.h1("Hello, World!") - -run(hello_world) -``` - -# Resources - -Follow the links below to find out more about this project. - -- [Try ReactPy (Jupyter Notebook)](https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb) -- [Documentation](https://reactpy.dev/) -- [GitHub Discussions](https://github.com/reactive-python/reactpy/discussions) -- [Discord](https://discord.gg/uNb5P4hA9X) -- [Contributor Guide](https://reactpy.dev/docs/about/contributor-guide.html) -- [Code of Conduct](https://github.com/reactive-python/reactpy/blob/main/CODE_OF_CONDUCT.md) +See live preview here: https://archmonger.github.io/reactpy-docs/ diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index ea38eebf8..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -build -source/_auto -source/_static/custom.js -source/vdom-json-schema.json diff --git a/docs/Dockerfile b/docs/Dockerfile deleted file mode 100644 index 76a8ad7ee..000000000 --- a/docs/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -FROM python:3.9 - -WORKDIR /app/ - -# Install NodeJS -# -------------- -RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - -RUN apt-get install -yq nodejs build-essential -RUN npm install -g npm@8.5.0 - -# Install Poetry -# -------------- -RUN pip install poetry - -# Create/Activate Python Venv -# --------------------------- -ENV VIRTUAL_ENV=/opt/venv -RUN python3 -m venv $VIRTUAL_ENV -ENV PATH="$VIRTUAL_ENV/bin:$PATH" -RUN pip install --upgrade pip - -# Copy Files -# ---------- -COPY LICENSE ./ -COPY src ./src -COPY docs ./docs -COPY branding ./branding - -# Install and Build Docs -# ---------------------- -WORKDIR /app/docs -RUN poetry install -RUN sphinx-build -v -W -b html source build - -# Define Entrypoint -# ----------------- -ENV PORT 5000 -ENV REACTPY_DEBUG_MODE=1 -ENV REACTPY_CHECK_VDOM_SPEC=0 -CMD python main.py diff --git a/docs/LICENSE.md b/docs/LICENSE.md new file mode 100644 index 000000000..d21a91a91 --- /dev/null +++ b/docs/LICENSE.md @@ -0,0 +1,393 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 1360bc825..000000000 --- a/docs/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# ReactPy's Documentation - -We provide two main ways to run the docs. Both use -[`nox`](https://pypi.org/project/nox/): - -- `nox -s docs` - displays the docs and rebuilds when files are modified. -- `nox -s docs-in-docker` - builds a docker image and runs the docs from there. - -If any changes to the core of the documentation are made (i.e. to non-`*.rst` files), -then you should run a manual test of the documentation using the `docs_in_docker` -session. - -If you wish to build and run the docs by hand you need to perform two commands, each -being run from the root of the repository: - -- `sphinx-build -b html docs/source docs/build` -- `python scripts/run_docs.py` - -The first command constructs the static HTML and any Javascript. The latter actually -runs the web server that serves the content. diff --git a/docs/docs_app/app.py b/docs/docs_app/app.py deleted file mode 100644 index 3fe4669ff..000000000 --- a/docs/docs_app/app.py +++ /dev/null @@ -1,59 +0,0 @@ -from logging import getLogger -from pathlib import Path - -from sanic import Sanic, response - -from docs_app.examples import get_normalized_example_name, load_examples -from reactpy import component -from reactpy.backend.sanic import Options, configure, use_request -from reactpy.core.types import ComponentConstructor - -THIS_DIR = Path(__file__).parent -DOCS_DIR = THIS_DIR.parent -DOCS_BUILD_DIR = DOCS_DIR / "build" - -REACTPY_MODEL_SERVER_URL_PREFIX = "/_reactpy" - -logger = getLogger(__name__) - - -REACTPY_MODEL_SERVER_URL_PREFIX = "/_reactpy" - - -@component -def Example(): - raw_view_id = use_request().get_args().get("view_id") - view_id = get_normalized_example_name(raw_view_id) - return _get_examples()[view_id]() - - -def _get_examples(): - if not _EXAMPLES: - _EXAMPLES.update(load_examples()) - return _EXAMPLES - - -def reload_examples(): - _EXAMPLES.clear() - _EXAMPLES.update(load_examples()) - - -_EXAMPLES: dict[str, ComponentConstructor] = {} - - -def make_app(name: str): - app = Sanic(name) - - app.static("/docs", str(DOCS_BUILD_DIR)) - - @app.route("/") - async def forward_to_index(_): - return response.redirect("/docs/index.html") - - configure( - app, - Example, - Options(url_prefix=REACTPY_MODEL_SERVER_URL_PREFIX), - ) - - return app diff --git a/docs/docs_app/dev.py b/docs/docs_app/dev.py deleted file mode 100644 index 5d661924d..000000000 --- a/docs/docs_app/dev.py +++ /dev/null @@ -1,104 +0,0 @@ -import asyncio -import os -import threading -import time -import webbrowser - -from sphinx_autobuild.cli import ( - Server, - _get_build_args, - _get_ignore_handler, - find_free_port, - get_builder, - get_parser, -) - -from docs_app.app import make_app, reload_examples -from reactpy.backend.sanic import serve_development_app -from reactpy.testing import clear_reactpy_web_modules_dir - -# these environment variable are used in custom Sphinx extensions -os.environ["REACTPY_DOC_EXAMPLE_SERVER_HOST"] = "127.0.0.1:5555" -os.environ["REACTPY_DOC_STATIC_SERVER_HOST"] = "" - - -def wrap_builder(old_builder): - # This is the bit that we're injecting to get the example components to reload too - - app = make_app("docs_dev_app") - - thread_started = threading.Event() - - def run_in_thread(): - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - server_started = asyncio.Event() - - async def set_thread_event_when_started(): - await server_started.wait() - thread_started.set() - - loop.run_until_complete( - asyncio.gather( - serve_development_app(app, "127.0.0.1", 5555, server_started), - set_thread_event_when_started(), - ) - ) - - threading.Thread(target=run_in_thread, daemon=True).start() - - thread_started.wait() - - def new_builder(): - clear_reactpy_web_modules_dir() - reload_examples() - old_builder() - - return new_builder - - -def main(): - # Mostly copied from https://github.com/executablebooks/sphinx-autobuild/blob/b54fb08afc5112bfcda1d844a700c5a20cd6ba5e/src/sphinx_autobuild/cli.py - parser = get_parser() - args = parser.parse_args() - - srcdir = os.path.realpath(args.sourcedir) - outdir = os.path.realpath(args.outdir) - if not os.path.exists(outdir): - os.makedirs(outdir) - - server = Server() - - build_args, pre_build_commands = _get_build_args(args) - builder = wrap_builder( - get_builder( - server.watcher, - build_args, - host=args.host, - port=args.port, - pre_build_commands=pre_build_commands, - ) - ) - - ignore_handler = _get_ignore_handler(args) - server.watch(srcdir, builder, ignore=ignore_handler) - for dirpath in args.additional_watched_dirs: - real_dirpath = os.path.realpath(dirpath) - server.watch(real_dirpath, builder, ignore=ignore_handler) - server.watch(outdir, ignore=ignore_handler) - - if not args.no_initial_build: - builder() - - # Find the free port - portn = args.port or find_free_port() - if args.openbrowser is True: - - def opener(): - time.sleep(args.delay) - webbrowser.open(f"http://{args.host}:{args.port}/index.html") - - threading.Thread(target=opener, daemon=True).start() - - server.serve(port=portn, host=args.host, root=outdir) diff --git a/docs/docs_app/examples.py b/docs/docs_app/examples.py deleted file mode 100644 index a71a0b111..000000000 --- a/docs/docs_app/examples.py +++ /dev/null @@ -1,177 +0,0 @@ -from __future__ import annotations - -from collections.abc import Iterator -from io import StringIO -from pathlib import Path -from traceback import format_exc -from typing import Callable - -import reactpy -from reactpy.types import ComponentType - -HERE = Path(__file__) -SOURCE_DIR = HERE.parent.parent / "source" -CONF_FILE = SOURCE_DIR / "conf.py" -RUN_ReactPy = reactpy.run - - -def load_examples() -> Iterator[tuple[str, Callable[[], ComponentType]]]: - for name in all_example_names(): - yield name, load_one_example(name) - - -def all_example_names() -> set[str]: - names = set() - for file in _iter_example_files(SOURCE_DIR): - path = file.parent if file.name == "main.py" else file - names.add("/".join(path.relative_to(SOURCE_DIR).with_suffix("").parts)) - return names - - -def load_one_example(file_or_name: Path | str) -> Callable[[], ComponentType]: - return lambda: ( - # we use a lambda to ensure each instance is fresh - _load_one_example(file_or_name) - ) - - -def get_normalized_example_name( - name: str, relative_to: str | Path | None = SOURCE_DIR -) -> str: - return "/".join( - _get_root_example_path_by_name(name, relative_to).relative_to(SOURCE_DIR).parts - ) - - -def get_main_example_file_by_name( - name: str, relative_to: str | Path | None = SOURCE_DIR -) -> Path: - path = _get_root_example_path_by_name(name, relative_to) - if path.is_dir(): - return path / "main.py" - else: - return path.with_suffix(".py") - - -def get_example_files_by_name( - name: str, relative_to: str | Path | None = SOURCE_DIR -) -> list[Path]: - path = _get_root_example_path_by_name(name, relative_to) - if path.is_dir(): - return [p for p in path.glob("*") if not p.is_dir()] - else: - path = path.with_suffix(".py") - return [path] if path.exists() else [] - - -def _iter_example_files(root: Path) -> Iterator[Path]: - for path in root.iterdir(): - if path.is_dir(): - if not path.name.startswith("_") or path.name == "_examples": - yield from _iter_example_files(path) - elif path.suffix == ".py" and path != CONF_FILE: - yield path - - -def _load_one_example(file_or_name: Path | str) -> ComponentType: - if isinstance(file_or_name, str): - file = get_main_example_file_by_name(file_or_name) - else: - file = file_or_name - - if not file.exists(): - raise FileNotFoundError(str(file)) - - print_buffer = _PrintBuffer() - - def capture_print(*args, **kwargs): - buffer = StringIO() - print(*args, file=buffer, **kwargs) - print_buffer.write(buffer.getvalue()) - - captured_component_constructor = None - - def capture_component(component_constructor): - nonlocal captured_component_constructor - captured_component_constructor = component_constructor - - reactpy.run = capture_component - try: - code = compile(file.read_text(), str(file), "exec") - exec( - code, - { - "print": capture_print, - "__file__": str(file), - "__name__": file.stem, - }, - ) - except Exception: - return _make_error_display(format_exc()) - finally: - reactpy.run = RUN_ReactPy - - if captured_component_constructor is None: - return _make_example_did_not_run(str(file)) - - @reactpy.component - def Wrapper(): - return reactpy.html.div(captured_component_constructor(), PrintView()) - - @reactpy.component - def PrintView(): - text, set_text = reactpy.hooks.use_state(print_buffer.getvalue()) - print_buffer.set_callback(set_text) - return ( - reactpy.html.pre({"class_name": "printout"}, text) - if text - else reactpy.html.div() - ) - - return Wrapper() - - -def _get_root_example_path_by_name(name: str, relative_to: str | Path | None) -> Path: - if not name.startswith("/") and relative_to is not None: - rel_path = Path(relative_to) - rel_path = rel_path.parent if rel_path.is_file() else rel_path - else: - rel_path = SOURCE_DIR - return rel_path.joinpath(*name.split("/")).resolve() - - -class _PrintBuffer: - def __init__(self, max_lines: int = 10): - self._callback = None - self._lines = () - self._max_lines = max_lines - - def set_callback(self, function: Callable[[str], None]) -> None: - self._callback = function - - def getvalue(self) -> str: - return "".join(self._lines) - - def write(self, text: str) -> None: - if len(self._lines) == self._max_lines: - self._lines = self._lines[1:] + (text,) - else: - self._lines += (text,) - if self._callback is not None: - self._callback(self.getvalue()) - - -def _make_example_did_not_run(example_name): - @reactpy.component - def ExampleDidNotRun(): - return reactpy.html.code(f"Example {example_name} did not run") - - return ExampleDidNotRun() - - -def _make_error_display(message): - @reactpy.component - def ShowError(): - return reactpy.html.pre(message) - - return ShowError() diff --git a/docs/docs_app/prod.py b/docs/docs_app/prod.py deleted file mode 100644 index 0acf12432..000000000 --- a/docs/docs_app/prod.py +++ /dev/null @@ -1,14 +0,0 @@ -import os - -from docs_app.app import make_app - -app = make_app("docs_prod_app") - - -def main() -> None: - app.run( - host="0.0.0.0", # noqa: S104 - port=int(os.environ.get("PORT", 5000)), - workers=int(os.environ.get("WEB_CONCURRENCY", 1)), - debug=bool(int(os.environ.get("DEBUG", "0"))), - ) diff --git a/docs/examples/css/quick_start/adding_styles.css b/docs/examples/css/quick_start/adding_styles.css new file mode 100644 index 000000000..4d02060e1 --- /dev/null +++ b/docs/examples/css/quick_start/adding_styles.css @@ -0,0 +1,4 @@ +/* In your CSS */ +.avatar { + border-radius: 50%; +} diff --git a/docs/examples/css/quick_start/displaying_data.css b/docs/examples/css/quick_start/displaying_data.css new file mode 100644 index 000000000..9f5a392c1 --- /dev/null +++ b/docs/examples/css/quick_start/displaying_data.css @@ -0,0 +1,7 @@ +.avatar { + border-radius: 50%; +} + +.large { + border: 4px solid gold; +} diff --git a/docs/examples/css/quick_start/sharing_data_between_components.css b/docs/examples/css/quick_start/sharing_data_between_components.css new file mode 100644 index 000000000..6081b7ef4 --- /dev/null +++ b/docs/examples/css/quick_start/sharing_data_between_components.css @@ -0,0 +1,4 @@ +button { + display: block; + margin-bottom: 5px; +} diff --git a/docs/examples/css/quick_start/updating_the_screen.css b/docs/examples/css/quick_start/updating_the_screen.css new file mode 100644 index 000000000..6081b7ef4 --- /dev/null +++ b/docs/examples/css/quick_start/updating_the_screen.css @@ -0,0 +1,4 @@ +button { + display: block; + margin-bottom: 5px; +} diff --git a/docs/examples/css/tutorial-tic-tac-toe/tic_tac_toe.css b/docs/examples/css/tutorial-tic-tac-toe/tic_tac_toe.css new file mode 100644 index 000000000..87876ec02 --- /dev/null +++ b/docs/examples/css/tutorial-tic-tac-toe/tic_tac_toe.css @@ -0,0 +1,42 @@ +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ""; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} diff --git a/docs/docs_app/__init__.py b/docs/examples/python/__init__.py similarity index 100% rename from docs/docs_app/__init__.py rename to docs/examples/python/__init__.py diff --git a/docs/examples/python/quick_start/__init__.py b/docs/examples/python/quick_start/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/examples/python/quick_start/adding_styles.py b/docs/examples/python/quick_start/adding_styles.py new file mode 100644 index 000000000..cdc9a7b99 --- /dev/null +++ b/docs/examples/python/quick_start/adding_styles.py @@ -0,0 +1,4 @@ +from reactpy import html + +# start +html.img({"class_name": "avatar"}) diff --git a/docs/examples/python/quick_start/conditional_rendering.py b/docs/examples/python/quick_start/conditional_rendering.py new file mode 100644 index 000000000..d89e097a4 --- /dev/null +++ b/docs/examples/python/quick_start/conditional_rendering.py @@ -0,0 +1,22 @@ +from reactpy import component, html + + +def admin_panel(): + return [] + + +def login_form(): + return [] + + +is_logged_in = True + + +# start +@component +def my_component(): + if is_logged_in: + content = admin_panel() + else: + content = login_form() + return html.div(content) diff --git a/docs/examples/python/quick_start/conditional_rendering_logical_and.py b/docs/examples/python/quick_start/conditional_rendering_logical_and.py new file mode 100644 index 000000000..07e752bba --- /dev/null +++ b/docs/examples/python/quick_start/conditional_rendering_logical_and.py @@ -0,0 +1,14 @@ +from reactpy import component, html + + +def admin_panel(): + return [] + + +is_logged_in = True + + +# start +@component +def my_component(): + return html.div(is_logged_in and admin_panel()) diff --git a/docs/examples/python/quick_start/conditional_rendering_ternary.py b/docs/examples/python/quick_start/conditional_rendering_ternary.py new file mode 100644 index 000000000..2490cb5f9 --- /dev/null +++ b/docs/examples/python/quick_start/conditional_rendering_ternary.py @@ -0,0 +1,18 @@ +from reactpy import component, html + + +def admin_panel(): + return [] + + +def login_form(): + return [] + + +is_logged_in = True + + +# start +@component +def my_component(): + return html.div(admin_panel() if is_logged_in else login_form()) diff --git a/docs/examples/python/quick_start/creating_and_nesting_components.py b/docs/examples/python/quick_start/creating_and_nesting_components.py new file mode 100644 index 000000000..0841e9514 --- /dev/null +++ b/docs/examples/python/quick_start/creating_and_nesting_components.py @@ -0,0 +1,21 @@ +from reactpy import component, html + + +@component +def my_button(): + return html.button("I'm a button!") + + +@component +def my_app(): + return html.div( + html.h1("Welcome to my app"), + my_button(), + ) + + +# end +if __name__ == "__main__": + from reactpy import run + + run(my_app) diff --git a/docs/examples/python/quick_start/displaying_data.py b/docs/examples/python/quick_start/displaying_data.py new file mode 100644 index 000000000..d4caa09c9 --- /dev/null +++ b/docs/examples/python/quick_start/displaying_data.py @@ -0,0 +1,32 @@ +from reactpy import component, html + +user = { + "name": "Hedy Lamarr", + "image_url": "https://i.imgur.com/yXOvdOSs.jpg", + "image_size": 90, +} + + +@component +def profile(): + return html.div( + html.h3(user["name"]), + html.img( + { + "class_name": "avatar", + "src": user["image_url"], + "alt": f"Photo of {user['name']}", + "style": { + "width": user["image_size"], + "height": user["image_size"], + }, + } + ), + ) + + +# end +if __name__ == "__main__": + from reactpy import run + + run(profile) diff --git a/docs/examples/python/quick_start/my_app.py b/docs/examples/python/quick_start/my_app.py new file mode 100644 index 000000000..1616d54d1 --- /dev/null +++ b/docs/examples/python/quick_start/my_app.py @@ -0,0 +1,12 @@ +from reactpy import component, html + +from .my_button import my_button + + +# start +@component +def my_app(): + return html.div( + html.h1("Welcome to my app"), + my_button(), + ) diff --git a/docs/examples/python/quick_start/my_button.py b/docs/examples/python/quick_start/my_button.py new file mode 100644 index 000000000..0016632ca --- /dev/null +++ b/docs/examples/python/quick_start/my_button.py @@ -0,0 +1,7 @@ +from reactpy import component, html + + +# start +@component +def my_button(): + return html.button("I'm a button!") diff --git a/docs/examples/python/quick_start/rendering_lists.py b/docs/examples/python/quick_start/rendering_lists.py new file mode 100644 index 000000000..35ec3e069 --- /dev/null +++ b/docs/examples/python/quick_start/rendering_lists.py @@ -0,0 +1,30 @@ +from reactpy import component, html + +products = [ + {"title": "Cabbage", "is_fruit": False, "id": 1}, + {"title": "Garlic", "is_fruit": False, "id": 2}, + {"title": "Apple", "is_fruit": True, "id": 3}, +] + + +@component +def shopping_list(): + list_items = [ + html.li( + { + "key": product["id"], + "style": {"color": "magenta" if product["is_fruit"] else "darkgreen"}, + }, + product["title"], + ) + for product in products + ] + + return html.ul(list_items) + + +# end +if __name__ == "__main__": + from reactpy import run + + run(shopping_list) diff --git a/docs/examples/python/quick_start/rendering_lists_list_items.py b/docs/examples/python/quick_start/rendering_lists_list_items.py new file mode 100644 index 000000000..43e108f36 --- /dev/null +++ b/docs/examples/python/quick_start/rendering_lists_list_items.py @@ -0,0 +1,6 @@ +from reactpy import html + +from .rendering_lists_products import products + +# start +list_items = [html.li({"key": product["id"]}, product["title"]) for product in products] diff --git a/docs/examples/python/quick_start/rendering_lists_products.py b/docs/examples/python/quick_start/rendering_lists_products.py new file mode 100644 index 000000000..c2d6a5e31 --- /dev/null +++ b/docs/examples/python/quick_start/rendering_lists_products.py @@ -0,0 +1,5 @@ +products = [ + {"title": "Cabbage", "id": 1}, + {"title": "Garlic", "id": 2}, + {"title": "Apple", "id": 3}, +] diff --git a/docs/examples/python/quick_start/responding_to_events.py b/docs/examples/python/quick_start/responding_to_events.py new file mode 100644 index 000000000..c893353cf --- /dev/null +++ b/docs/examples/python/quick_start/responding_to_events.py @@ -0,0 +1,13 @@ +from reactpy import component, html + + +# start +@component +def my_button(): + def handle_click(event): + print("You clicked me!") + + return html.button( + {"on_click": handle_click}, + "Click me", + ) diff --git a/docs/examples/python/quick_start/sharing_data_between_components.py b/docs/examples/python/quick_start/sharing_data_between_components.py new file mode 100644 index 000000000..7c4d9904d --- /dev/null +++ b/docs/examples/python/quick_start/sharing_data_between_components.py @@ -0,0 +1,27 @@ +from reactpy import component, html, use_state + + +@component +def my_app(): + count, set_count = use_state(0) + + def handle_click(event): + set_count(count + 1) + + return html.div( + html.h1("Counters that update together"), + my_button(count, handle_click), + my_button(count, handle_click), + ) + + +@component +def my_button(count, on_click): + return html.button({"on_click": on_click}, f"Clicked {count} times") + + +# end +if __name__ == "__main__": + from reactpy import run + + run(my_app) diff --git a/docs/examples/python/quick_start/sharing_data_between_components_button.py b/docs/examples/python/quick_start/sharing_data_between_components_button.py new file mode 100644 index 000000000..b96dadfa6 --- /dev/null +++ b/docs/examples/python/quick_start/sharing_data_between_components_button.py @@ -0,0 +1,7 @@ +from reactpy import component, html + + +# start +@component +def my_button(count, on_click): + return html.button({"on_click": on_click}, f"Clicked {count} times") diff --git a/docs/examples/python/quick_start/sharing_data_between_components_move_state.py b/docs/examples/python/quick_start/sharing_data_between_components_move_state.py new file mode 100644 index 000000000..a3c6126c2 --- /dev/null +++ b/docs/examples/python/quick_start/sharing_data_between_components_move_state.py @@ -0,0 +1,20 @@ +from reactpy import component, html, use_state + +# start +@component +def my_app(): + count, set_count = use_state(0) + + def handle_click(event): + set_count(count + 1) + + return html.div( + html.h1("Counters that update separately"), + my_button(), + my_button(), + ) + + +@component +def my_button(): + # ... we're moving code from here ... diff --git a/docs/examples/python/quick_start/sharing_data_between_components_props.py b/docs/examples/python/quick_start/sharing_data_between_components_props.py new file mode 100644 index 000000000..52294076a --- /dev/null +++ b/docs/examples/python/quick_start/sharing_data_between_components_props.py @@ -0,0 +1,22 @@ +from reactpy import component, html, use_state + + +# start +@component +def my_app(): + count, set_count = use_state(0) + + def handle_click(event): + set_count(count + 1) + + return html.div( + html.h1("Counters that update together"), + my_button(count, handle_click), + my_button(count, handle_click), + ) + # end + + +@component +def my_button(count, on_click): + ... diff --git a/docs/examples/python/quick_start/updating_the_screen.py b/docs/examples/python/quick_start/updating_the_screen.py new file mode 100644 index 000000000..906a0718f --- /dev/null +++ b/docs/examples/python/quick_start/updating_the_screen.py @@ -0,0 +1,27 @@ +from reactpy import component, html, use_state + + +@component +def my_app(): + return html.div( + html.h1("Counters that update separately"), + my_button(), + my_button(), + ) + + +@component +def my_button(): + count, set_count = use_state(0) + + def handle_click(event): + set_count(count + 1) + + return html.button({"on_click": handle_click}, f"Clicked {count} times") + + +# end +if __name__ == "__main__": + from reactpy import run + + run(my_app) diff --git a/docs/examples/python/quick_start/updating_the_screen_event.py b/docs/examples/python/quick_start/updating_the_screen_event.py new file mode 100644 index 000000000..2829c481c --- /dev/null +++ b/docs/examples/python/quick_start/updating_the_screen_event.py @@ -0,0 +1,12 @@ +from reactpy import component, html, use_state + + +# start +@component +def my_button(): + count, set_count = use_state(0) + + def handle_click(event): + set_count(count + 1) + + return html.button({"on_click": handle_click}, f"Clicked {count} times") diff --git a/docs/examples/python/quick_start/updating_the_screen_use_state.py b/docs/examples/python/quick_start/updating_the_screen_use_state.py new file mode 100644 index 000000000..010487c33 --- /dev/null +++ b/docs/examples/python/quick_start/updating_the_screen_use_state.py @@ -0,0 +1,5 @@ +from reactpy import use_state + +# end + +use_state() diff --git a/docs/examples/python/quick_start/updating_the_screen_use_state_button.py b/docs/examples/python/quick_start/updating_the_screen_use_state_button.py new file mode 100644 index 000000000..c331ee6c9 --- /dev/null +++ b/docs/examples/python/quick_start/updating_the_screen_use_state_button.py @@ -0,0 +1,8 @@ +from reactpy import component, use_state + + +# start +@component +def my_button(): + count, set_count = use_state(0) + # ... diff --git a/docs/examples/python/tutorial-tic-tac-toe/tic_tac_toe.py b/docs/examples/python/tutorial-tic-tac-toe/tic_tac_toe.py new file mode 100644 index 000000000..9a5fc40cc --- /dev/null +++ b/docs/examples/python/tutorial-tic-tac-toe/tic_tac_toe.py @@ -0,0 +1,117 @@ +from copy import deepcopy + +from reactpy import component, html, use_state + + +@component +def square(value, on_square_click): + return html.button( + {"className": "square", "on_click": on_square_click}, + value or "", + ) + + +@component +def board(x_is_next, squares, on_play): + def handle_click(i): + def event(_event): + if calculate_winner(squares) or squares[i]: + return + + next_squares = squares.copy() + next_squares[i] = "X" if x_is_next else "O" + on_play(next_squares) + + return event + + winner = calculate_winner(squares) + status = ( + f"Winner: {winner}" if winner else "Next player: " + ("X" if x_is_next else "O") + ) + + return html._( + html.div({"className": "status"}, status), + html.div( + {"className": "board-row"}, + square(squares[0], handle_click(0)), + square(squares[1], handle_click(1)), + square(squares[2], handle_click(2)), + ), + html.div( + {"className": "board-row"}, + square(squares[3], handle_click(3)), + square(squares[4], handle_click(4)), + square(squares[5], handle_click(5)), + ), + html.div( + {"className": "board-row"}, + square(squares[6], handle_click(6)), + square(squares[7], handle_click(7)), + square(squares[8], handle_click(8)), + ), + ) + + +@component +def game(): + history, set_history = use_state([[None] * 9]) + current_move, set_current_move = use_state(0) + x_is_next = current_move % 2 == 0 + current_squares = history[current_move] + + def handle_play(next_squares): + next_history = deepcopy(history[: current_move + 1]) + next_history.append(next_squares) + set_history(next_history) + set_current_move(len(next_history) - 1) + + def jump_to(next_move): + return lambda _event: set_current_move(next_move) + + moves = [] + for move, _squares in enumerate(history): + description = f"Go to move #{move}" if move > 0 else "Go to game start" + + moves.append( + html.li( + {"key": move}, + html.button({"on_click": jump_to(move)}, description), + ) + ) + + return html.div( + {"className": "game"}, + html.div( + {"className": "game-board"}, + board(x_is_next, current_squares, handle_play), + ), + html.div({"className": "game-info"}, html.ol(moves)), + ) + + +def calculate_winner(squares): + lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ] + for line in lines: + a, b, c = line + if not squares: + continue + if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]: + return squares[a] + + return None + + +# end +if __name__ == "__main__": + from reactpy import run + + run(game) diff --git a/docs/main.py b/docs/main.py deleted file mode 100644 index e3181f393..000000000 --- a/docs/main.py +++ /dev/null @@ -1,9 +0,0 @@ -import sys - -from docs_app import dev, prod - -if __name__ == "__main__": - if len(sys.argv) == 1: - prod.main() - else: - dev.main() diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..e70aa10c8 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block content %} +{{ super() }} + +{% if git_page_authors %} +
+ + Authors: {{ git_page_authors | default('enable mkdocs-git-authors-plugin') }} + +
+{% endif %} +{% endblock %} diff --git a/docs/poetry.lock b/docs/poetry.lock deleted file mode 100644 index 8e1daef24..000000000 --- a/docs/poetry.lock +++ /dev/null @@ -1,2269 +0,0 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. - -[[package]] -name = "aiofiles" -version = "23.1.0" -description = "File support for asyncio." -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, - {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, -] - -[[package]] -name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" -optional = false -python-versions = ">=3.6" -files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, -] - -[[package]] -name = "anyio" -version = "3.7.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"}, - {file = "anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce"}, -] - -[package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx (>=6.1.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] - -[[package]] -name = "asgiref" -version = "3.7.2" -description = "ASGI specs, helper code, and adapters" -optional = false -python-versions = ">=3.7" -files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} - -[package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] - -[[package]] -name = "babel" -version = "2.12.1" -description = "Internationalization utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, -] - -[[package]] -name = "beautifulsoup4" -version = "4.12.2" -description = "Screen-scraping library" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "certifi" -version = "2023.5.7" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.1.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, -] - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "colorlog" -version = "6.7.0" -description = "Add colours to the output of Python's logging module." -optional = false -python-versions = ">=3.6" -files = [ - {file = "colorlog-6.7.0-py2.py3-none-any.whl", hash = "sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662"}, - {file = "colorlog-6.7.0.tar.gz", hash = "sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -development = ["black", "flake8", "mypy", "pytest", "types-colorama"] - -[[package]] -name = "contourpy" -version = "1.0.7" -description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false -python-versions = ">=3.8" -files = [ - {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d"}, - {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab"}, - {file = "contourpy-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6"}, - {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803"}, - {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8"}, - {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0"}, - {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6"}, - {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd"}, - {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69"}, - {file = "contourpy-1.0.7-cp310-cp310-win32.whl", hash = "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3"}, - {file = "contourpy-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80"}, - {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71"}, - {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5"}, - {file = "contourpy-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414"}, - {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2"}, - {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02"}, - {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae"}, - {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc"}, - {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac"}, - {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566"}, - {file = "contourpy-1.0.7-cp311-cp311-win32.whl", hash = "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0"}, - {file = "contourpy-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350"}, - {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad"}, - {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772"}, - {file = "contourpy-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f"}, - {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98"}, - {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc"}, - {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd"}, - {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810"}, - {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c"}, - {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc"}, - {file = "contourpy-1.0.7-cp38-cp38-win32.whl", hash = "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66"}, - {file = "contourpy-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556"}, - {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4"}, - {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1"}, - {file = "contourpy-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5"}, - {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3"}, - {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50"}, - {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2"}, - {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a"}, - {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2"}, - {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967"}, - {file = "contourpy-1.0.7-cp39-cp39-win32.whl", hash = "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693"}, - {file = "contourpy-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd"}, - {file = "contourpy-1.0.7.tar.gz", hash = "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e"}, -] - -[package.dependencies] -numpy = ">=1.16" - -[package.extras] -bokeh = ["bokeh", "chromedriver", "selenium"] -docs = ["furo", "sphinx-copybutton"] -mypy = ["contourpy[bokeh]", "docutils-stubs", "mypy (==0.991)", "types-Pillow"] -test = ["Pillow", "matplotlib", "pytest"] -test-no-images = ["pytest"] - -[[package]] -name = "cycler" -version = "0.11.0" -description = "Composable style cycles" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, - {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, -] - -[[package]] -name = "docutils" -version = "0.17.1" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "fastapi" -version = "0.96.0" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.7" -files = [ - {file = "fastapi-0.96.0-py3-none-any.whl", hash = "sha256:b8e11fe81e81eab4e1504209917338e0b80f783878a42c2b99467e5e1019a1e9"}, - {file = "fastapi-0.96.0.tar.gz", hash = "sha256:71232d47c2787446991c81c41c249f8a16238d52d779c0e6b43927d3773dbe3c"}, -] - -[package.dependencies] -pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" -starlette = ">=0.27.0,<0.28.0" - -[package.extras] -all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"] -test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] - -[[package]] -name = "fastjsonschema" -version = "2.17.1" -description = "Fastest Python implementation of JSON schema" -optional = false -python-versions = "*" -files = [ - {file = "fastjsonschema-2.17.1-py3-none-any.whl", hash = "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0"}, - {file = "fastjsonschema-2.17.1.tar.gz", hash = "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3"}, -] - -[package.extras] -devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] - -[[package]] -name = "flask" -version = "2.1.3" -description = "A simple framework for building complex web applications." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Flask-2.1.3-py3-none-any.whl", hash = "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c"}, - {file = "Flask-2.1.3.tar.gz", hash = "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb"}, -] - -[package.dependencies] -click = ">=8.0" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -itsdangerous = ">=2.0" -Jinja2 = ">=3.0" -Werkzeug = ">=2.0" - -[package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] - -[[package]] -name = "flask-cors" -version = "3.0.10" -description = "A Flask extension adding a decorator for CORS support" -optional = false -python-versions = "*" -files = [ - {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, - {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, -] - -[package.dependencies] -Flask = ">=0.9" -Six = "*" - -[[package]] -name = "flask-sock" -version = "0.6.0" -description = "WebSocket support for Flask" -optional = false -python-versions = ">=3.6" -files = [ - {file = "flask-sock-0.6.0.tar.gz", hash = "sha256:435cf81bb497ac7622cd1dda554fbfa3e369e629daea0a1d21b73a24f1bd6229"}, - {file = "flask_sock-0.6.0-py3-none-any.whl", hash = "sha256:593fffb186928080a5b5b03d717efc56dac2d5ed690ce6bfff333b3597a2f518"}, -] - -[package.dependencies] -flask = ">=2" -simple-websocket = ">=0.5.1" - -[[package]] -name = "fonttools" -version = "4.39.4" -description = "Tools to manipulate font files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fonttools-4.39.4-py3-none-any.whl", hash = "sha256:106caf6167c4597556b31a8d9175a3fdc0356fdcd70ab19973c3b0d4c893c461"}, - {file = "fonttools-4.39.4.zip", hash = "sha256:dba8d7cdb8e2bac1b3da28c5ed5960de09e59a2fe7e63bb73f5a59e57b0430d2"}, -] - -[package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] -graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "scipy"] -lxml = ["lxml (>=4.0,<5)"] -pathops = ["skia-pathops (>=0.5.0)"] -plot = ["matplotlib"] -repacker = ["uharfbuzz (>=0.23.0)"] -symfont = ["sympy"] -type1 = ["xattr"] -ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.0.0)"] -woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] - -[[package]] -name = "furo" -version = "2022.4.7" -description = "A clean customisable Sphinx documentation theme." -optional = false -python-versions = ">=3.6" -files = [ - {file = "furo-2022.4.7-py3-none-any.whl", hash = "sha256:7f3e3d2fb977483590f8ecb2c2cd511bd82661b79c18efb24de9558bc9cdf2d7"}, - {file = "furo-2022.4.7.tar.gz", hash = "sha256:96204ab7cd047e4b6c523996e0279c4c629a8fc31f4f109b2efd470c17f49c80"}, -] - -[package.dependencies] -beautifulsoup4 = "*" -pygments = ">=2.7,<3.0" -sphinx = ">=4.0,<5.0" - -[[package]] -name = "greenlet" -version = "2.0.2" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -files = [ - {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, - {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, - {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, - {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, - {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, - {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, - {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, - {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, - {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, - {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, - {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, - {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, - {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, - {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, - {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, - {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, - {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, - {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, - {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, - {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, - {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, - {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, - {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, - {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, - {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, - {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, -] - -[package.extras] -docs = ["Sphinx", "docutils (<0.18)"] -test = ["objgraph", "psutil"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "html5tagger" -version = "1.3.0" -description = "Pythonic HTML generation/templating (no template files)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "html5tagger-1.3.0-py3-none-any.whl", hash = "sha256:ce14313515edffec8ed8a36c5890d023922641171b4e6e5774ad1a74998f5351"}, - {file = "html5tagger-1.3.0.tar.gz", hash = "sha256:84fa3dfb49e5c83b79bbd856ab7b1de8e2311c3bb46a8be925f119e3880a8da9"}, -] - -[[package]] -name = "httptools" -version = "0.5.0" -description = "A collection of framework independent HTTP protocol utils." -optional = false -python-versions = ">=3.5.0" -files = [ - {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51"}, - {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421"}, - {file = "httptools-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449"}, - {file = "httptools-0.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2"}, - {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde"}, - {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a"}, - {file = "httptools-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d"}, - {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e"}, - {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49"}, - {file = "httptools-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9"}, - {file = "httptools-0.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986"}, - {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0"}, - {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f"}, - {file = "httptools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1"}, - {file = "httptools-0.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f"}, - {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7"}, - {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60"}, - {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca"}, - {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324"}, - {file = "httptools-0.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86"}, - {file = "httptools-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b"}, - {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b"}, - {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281"}, - {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b"}, - {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25"}, - {file = "httptools-0.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5"}, - {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"}, - {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c"}, - {file = "httptools-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc"}, - {file = "httptools-0.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63"}, - {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d"}, - {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901"}, - {file = "httptools-0.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd"}, - {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6"}, - {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42"}, - {file = "httptools-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887"}, - {file = "httptools-0.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2"}, - {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142"}, - {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372"}, - {file = "httptools-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b"}, - {file = "httptools-0.5.0.tar.gz", hash = "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.6.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, - {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - -[[package]] -name = "importlib-resources" -version = "5.12.0" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[[package]] -name = "itsdangerous" -version = "2.1.2" -description = "Safely pass data to untrusted environments and back." -optional = false -python-versions = ">=3.7" -files = [ - {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, - {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, -] - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonpatch" -version = "1.32" -description = "Apply JSON-Patches (RFC 6902)" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "jsonpatch-1.32-py2.py3-none-any.whl", hash = "sha256:26ac385719ac9f54df8a2f0827bb8253aa3ea8ab7b3368457bcdb8c14595a397"}, - {file = "jsonpatch-1.32.tar.gz", hash = "sha256:b6ddfe6c3db30d81a96aaeceb6baf916094ffa23d7dd5fa2c13e13f8b6e600c2"}, -] - -[package.dependencies] -jsonpointer = ">=1.9" - -[[package]] -name = "jsonpointer" -version = "2.3" -description = "Identify specific nodes in a JSON document (RFC 6901)" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "jsonpointer-2.3-py2.py3-none-any.whl", hash = "sha256:51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9"}, - {file = "jsonpointer-2.3.tar.gz", hash = "sha256:97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a"}, -] - -[[package]] -name = "kiwisolver" -version = "1.4.4" -description = "A fast implementation of the Cassowary constraint solver" -optional = false -python-versions = ">=3.7" -files = [ - {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, - {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, - {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, - {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, - {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, - {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, - {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, - {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, - {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, - {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, - {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, - {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, - {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, - {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, - {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, - {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, - {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, - {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, - {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, - {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, - {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, -] - -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -optional = false -python-versions = "*" -files = [ - {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] - -[package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lxml" -version = "4.9.2" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -files = [ - {file = "lxml-4.9.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2"}, - {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892"}, - {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a"}, - {file = "lxml-4.9.2-cp27-cp27m-win32.whl", hash = "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de"}, - {file = "lxml-4.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3"}, - {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50"}, - {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975"}, - {file = "lxml-4.9.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c"}, - {file = "lxml-4.9.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a"}, - {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4"}, - {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4"}, - {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7"}, - {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184"}, - {file = "lxml-4.9.2-cp310-cp310-win32.whl", hash = "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda"}, - {file = "lxml-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab"}, - {file = "lxml-4.9.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9"}, - {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf"}, - {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380"}, - {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92"}, - {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1"}, - {file = "lxml-4.9.2-cp311-cp311-win32.whl", hash = "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33"}, - {file = "lxml-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd"}, - {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"}, - {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"}, - {file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"}, - {file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"}, - {file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e"}, - {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"}, - {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"}, - {file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"}, - {file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"}, - {file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45"}, - {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e"}, - {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b"}, - {file = "lxml-4.9.2-cp37-cp37m-win32.whl", hash = "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe"}, - {file = "lxml-4.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9"}, - {file = "lxml-4.9.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c"}, - {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f"}, - {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457"}, - {file = "lxml-4.9.2-cp38-cp38-win32.whl", hash = "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b"}, - {file = "lxml-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7"}, - {file = "lxml-4.9.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5"}, - {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5"}, - {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2"}, - {file = "lxml-4.9.2-cp39-cp39-win32.whl", hash = "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1"}, - {file = "lxml-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f"}, - {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c"}, - {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409"}, - {file = "lxml-4.9.2.tar.gz", hash = "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67"}, -] - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -name = "markupsafe" -version = "2.0.1" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.6" -files = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, -] - -[[package]] -name = "matplotlib" -version = "3.7.1" -description = "Python plotting package" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:95cbc13c1fc6844ab8812a525bbc237fa1470863ff3dace7352e910519e194b1"}, - {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:08308bae9e91aca1ec6fd6dda66237eef9f6294ddb17f0d0b3c863169bf82353"}, - {file = "matplotlib-3.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:544764ba51900da4639c0f983b323d288f94f65f4024dc40ecb1542d74dc0500"}, - {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d94989191de3fcc4e002f93f7f1be5da476385dde410ddafbb70686acf00ea"}, - {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99bc9e65901bb9a7ce5e7bb24af03675cbd7c70b30ac670aa263240635999a4"}, - {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb7d248c34a341cd4c31a06fd34d64306624c8cd8d0def7abb08792a5abfd556"}, - {file = "matplotlib-3.7.1-cp310-cp310-win32.whl", hash = "sha256:ce463ce590f3825b52e9fe5c19a3c6a69fd7675a39d589e8b5fbe772272b3a24"}, - {file = "matplotlib-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d7bc90727351fb841e4d8ae620d2d86d8ed92b50473cd2b42ce9186104ecbba"}, - {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:770a205966d641627fd5cf9d3cb4b6280a716522cd36b8b284a8eb1581310f61"}, - {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f67bfdb83a8232cb7a92b869f9355d677bce24485c460b19d01970b64b2ed476"}, - {file = "matplotlib-3.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bf092f9210e105f414a043b92af583c98f50050559616930d884387d0772aba"}, - {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89768d84187f31717349c6bfadc0e0d8c321e8eb34522acec8a67b1236a66332"}, - {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83111e6388dec67822e2534e13b243cc644c7494a4bb60584edbff91585a83c6"}, - {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a867bf73a7eb808ef2afbca03bcdb785dae09595fbe550e1bab0cd023eba3de0"}, - {file = "matplotlib-3.7.1-cp311-cp311-win32.whl", hash = "sha256:fbdeeb58c0cf0595efe89c05c224e0a502d1aa6a8696e68a73c3efc6bc354304"}, - {file = "matplotlib-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0bd19c72ae53e6ab979f0ac6a3fafceb02d2ecafa023c5cca47acd934d10be7"}, - {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:6eb88d87cb2c49af00d3bbc33a003f89fd9f78d318848da029383bfc08ecfbfb"}, - {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:cf0e4f727534b7b1457898c4f4ae838af1ef87c359b76dcd5330fa31893a3ac7"}, - {file = "matplotlib-3.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46a561d23b91f30bccfd25429c3c706afe7d73a5cc64ef2dfaf2b2ac47c1a5dc"}, - {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8704726d33e9aa8a6d5215044b8d00804561971163563e6e6591f9dcf64340cc"}, - {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4cf327e98ecf08fcbb82685acaf1939d3338548620ab8dfa02828706402c34de"}, - {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:617f14ae9d53292ece33f45cba8503494ee199a75b44de7717964f70637a36aa"}, - {file = "matplotlib-3.7.1-cp38-cp38-win32.whl", hash = "sha256:7c9a4b2da6fac77bcc41b1ea95fadb314e92508bf5493ceff058e727e7ecf5b0"}, - {file = "matplotlib-3.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:14645aad967684e92fc349493fa10c08a6da514b3d03a5931a1bac26e6792bd1"}, - {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:81a6b377ea444336538638d31fdb39af6be1a043ca5e343fe18d0f17e098770b"}, - {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:28506a03bd7f3fe59cd3cd4ceb2a8d8a2b1db41afede01f66c42561b9be7b4b7"}, - {file = "matplotlib-3.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c587963b85ce41e0a8af53b9b2de8dddbf5ece4c34553f7bd9d066148dc719c"}, - {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bf26ade3ff0f27668989d98c8435ce9327d24cffb7f07d24ef609e33d582439"}, - {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def58098f96a05f90af7e92fd127d21a287068202aa43b2a93476170ebd99e87"}, - {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f883a22a56a84dba3b588696a2b8a1ab0d2c3d41be53264115c71b0a942d8fdb"}, - {file = "matplotlib-3.7.1-cp39-cp39-win32.whl", hash = "sha256:4f99e1b234c30c1e9714610eb0c6d2f11809c9c78c984a613ae539ea2ad2eb4b"}, - {file = "matplotlib-3.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:3ba2af245e36990facf67fde840a760128ddd71210b2ab6406e640188d69d136"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3032884084f541163f295db8a6536e0abb0db464008fadca6c98aaf84ccf4717"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a2cb34336110e0ed8bb4f650e817eed61fa064acbefeb3591f1b33e3a84fd96"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b867e2f952ed592237a1828f027d332d8ee219ad722345b79a001f49df0936eb"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:57bfb8c8ea253be947ccb2bc2d1bb3862c2bccc662ad1b4626e1f5e004557042"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:438196cdf5dc8d39b50a45cb6e3f6274edbcf2254f85fa9b895bf85851c3a613"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e9cff1a58d42e74d01153360de92b326708fb205250150018a52c70f43c290"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d4725d70b7c03e082bbb8a34639ede17f333d7247f56caceb3801cb6ff703d"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:97cc368a7268141afb5690760921765ed34867ffb9655dd325ed207af85c7529"}, - {file = "matplotlib-3.7.1.tar.gz", hash = "sha256:7b73305f25eab4541bd7ee0b96d87e53ae9c9f1823be5659b806cd85786fe882"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} -kiwisolver = ">=1.0.1" -numpy = ">=1.20" -packaging = ">=20.0" -pillow = ">=6.2.0" -pyparsing = ">=2.3.1" -python-dateutil = ">=2.7" - -[[package]] -name = "multidict" -version = "6.0.4" -description = "multidict implementation" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "numpy" -version = "1.24.3" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"}, - {file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"}, - {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"}, - {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"}, - {file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"}, - {file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"}, - {file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"}, - {file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"}, - {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"}, - {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"}, - {file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"}, - {file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"}, - {file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"}, - {file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"}, - {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"}, - {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"}, - {file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"}, - {file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"}, - {file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"}, - {file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"}, - {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"}, - {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"}, - {file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"}, - {file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"}, - {file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"}, - {file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"}, - {file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"}, - {file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"}, -] - -[[package]] -name = "packaging" -version = "23.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "pillow" -version = "9.5.0" -description = "Python Imaging Library (Fork)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, - {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, - {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, - {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, - {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, - {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, - {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, - {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, - {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, - {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, - {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, - {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, - {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, - {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, - {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, - {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, - {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "playwright" -version = "1.34.0" -description = "A high-level API to automate web browsers" -optional = false -python-versions = ">=3.7" -files = [ - {file = "playwright-1.34.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:69bb9b3296e366a23a99277b4c7673cb54ce71a3f5d630f114f7701b61f98f25"}, - {file = "playwright-1.34.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:402d946631c8458436e099d7731bbf54cf79c9e62e3acae0ea8421e72616926b"}, - {file = "playwright-1.34.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:462251cda0fcbb273497d357dbe14b11e43ebceb0bac9b892beda041ff209aa9"}, - {file = "playwright-1.34.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a8ba124ea302596a03a66993cd500484fb255cbc10fe0757fa4d49f974267a80"}, - {file = "playwright-1.34.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf0cb6aac49d24335fe361868aea72b11f276a95e7809f1a5d1c69b4120c46ac"}, - {file = "playwright-1.34.0-py3-none-win32.whl", hash = "sha256:c50fef189d87243cc09ae0feb8e417fbe434359ccbcc863fb19ba06d46d31c33"}, - {file = "playwright-1.34.0-py3-none-win_amd64.whl", hash = "sha256:42e16c930e1e910461f4c551a72fc1b900f37124431bf2b6a6d9ddae70042db4"}, -] - -[package.dependencies] -greenlet = "2.0.2" -pyee = "9.0.4" - -[[package]] -name = "pydantic" -version = "1.10.8" -description = "Data validation and settings management using python type hints" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, - {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, - {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, - {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, - {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, - {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, - {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, - {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, - {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, - {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyee" -version = "9.0.4" -description = "A port of node.js's EventEmitter to python." -optional = false -python-versions = "*" -files = [ - {file = "pyee-9.0.4-py2.py3-none-any.whl", hash = "sha256:9f066570130c554e9cc12de5a9d86f57c7ee47fece163bbdaa3e9c933cfbdfa5"}, - {file = "pyee-9.0.4.tar.gz", hash = "sha256:2770c4928abc721f46b705e6a72b0c59480c4a69c9a83ca0b00bb994f1ea4b32"}, -] - -[package.dependencies] -typing-extensions = "*" - -[[package]] -name = "pygments" -version = "2.15.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] - -[[package]] -name = "reactpy" -version = "1.0.0" -description = "Reactive user interfaces with pure Python" -optional = false -python-versions = ">=3.9" -files = [] -develop = false - -[package.dependencies] -anyio = ">=3" -asgiref = ">=3" -colorlog = ">=6" -fastapi = {version = ">=0.63.0", optional = true, markers = "extra == \"fastapi\""} -fastjsonschema = ">=2.14.5" -flask = {version = "*", optional = true, markers = "extra == \"flask\""} -flask-cors = {version = "*", optional = true, markers = "extra == \"flask\""} -flask-sock = {version = "*", optional = true, markers = "extra == \"flask\""} -jsonpatch = ">=1.32" -lxml = ">=4" -markupsafe = {version = ">=1.1.1,<2.1", optional = true, markers = "extra == \"flask\""} -mypy-extensions = ">=0.4.3" -playwright = {version = "*", optional = true, markers = "extra == \"testing\""} -requests = ">=2" -sanic = {version = ">=21", optional = true, markers = "extra == \"sanic\""} -sanic-cors = {version = "*", optional = true, markers = "extra == \"sanic\""} -starlette = {version = ">=0.13.6", optional = true, markers = "extra == \"starlette\""} -tornado = {version = "*", optional = true, markers = "extra == \"tornado\""} -typing-extensions = ">=3.10" -uvicorn = {version = ">=0.19.0", extras = ["standard"], optional = true, markers = "extra == \"fastapi\" or extra == \"sanic\" or extra == \"starlette\""} - -[package.extras] -all = ["reactpy[fastapi,flask,sanic,starlette,testing,tornado]"] -fastapi = ["fastapi (>=0.63.0)", "uvicorn[standard] (>=0.19.0)"] -flask = ["flask", "flask-cors", "flask-sock", "markupsafe (>=1.1.1,<2.1)"] -sanic = ["sanic (>=21)", "sanic-cors", "uvicorn[standard] (>=0.19.0)"] -starlette = ["starlette (>=0.13.6)", "uvicorn[standard] (>=0.19.0)"] -testing = ["playwright"] -tornado = ["tornado"] - -[package.source] -type = "directory" -url = "../src/py/reactpy" - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "sanic" -version = "23.3.0" -description = "A web server and web framework that's written to go fast. Build fast. Run fast." -optional = false -python-versions = ">=3.7" -files = [ - {file = "sanic-23.3.0-py3-none-any.whl", hash = "sha256:7cafbd63da9c6c6d8aeb8cb4304addf8a274352ab812014386c63e55f474fbee"}, - {file = "sanic-23.3.0.tar.gz", hash = "sha256:b80ebc5c38c983cb45ae5ecc7a669a54c823ec1dff297fbd5f817b1e9e9e49af"}, -] - -[package.dependencies] -aiofiles = ">=0.6.0" -html5tagger = ">=1.2.1" -httptools = ">=0.0.10" -multidict = ">=5.0,<7.0" -sanic-routing = ">=22.8.0" -tracerite = ">=1.0.0" -ujson = {version = ">=1.35", markers = "sys_platform != \"win32\" and implementation_name == \"cpython\""} -uvloop = {version = ">=0.15.0", markers = "sys_platform != \"win32\" and implementation_name == \"cpython\""} -websockets = ">=10.0" - -[package.extras] -all = ["bandit", "beautifulsoup4", "black", "chardet (==3.*)", "coverage", "cryptography", "docutils", "enum-tools[sphinx]", "flake8", "isort (>=5.0.0)", "m2r2", "mistune (<2.0.0)", "mypy (>=0.901,<0.910)", "pygments", "pytest (==7.1.*)", "pytest-benchmark", "pytest-sanic", "sanic-testing (>=23.3.0)", "slotscheck (>=0.8.0,<1)", "sphinx (>=2.1.2)", "sphinx-rtd-theme (>=0.4.3)", "towncrier", "tox", "types-ujson", "uvicorn (<0.15.0)"] -dev = ["bandit", "beautifulsoup4", "black", "chardet (==3.*)", "coverage", "cryptography", "docutils", "flake8", "isort (>=5.0.0)", "mypy (>=0.901,<0.910)", "pygments", "pytest (==7.1.*)", "pytest-benchmark", "pytest-sanic", "sanic-testing (>=23.3.0)", "slotscheck (>=0.8.0,<1)", "towncrier", "tox", "types-ujson", "uvicorn (<0.15.0)"] -docs = ["docutils", "enum-tools[sphinx]", "m2r2", "mistune (<2.0.0)", "pygments", "sphinx (>=2.1.2)", "sphinx-rtd-theme (>=0.4.3)"] -ext = ["sanic-ext"] -http3 = ["aioquic"] -test = ["bandit", "beautifulsoup4", "black", "chardet (==3.*)", "coverage", "docutils", "flake8", "isort (>=5.0.0)", "mypy (>=0.901,<0.910)", "pygments", "pytest (==7.1.*)", "pytest-benchmark", "pytest-sanic", "sanic-testing (>=23.3.0)", "slotscheck (>=0.8.0,<1)", "types-ujson", "uvicorn (<0.15.0)"] - -[[package]] -name = "sanic-cors" -version = "2.2.0" -description = "A Sanic extension adding a decorator for CORS support. Based on flask-cors by Cory Dolphin." -optional = false -python-versions = "*" -files = [ - {file = "Sanic-Cors-2.2.0.tar.gz", hash = "sha256:f8d7515da4c8b837871d422c66314c4b5704396a78894b59c50e26aa72a95873"}, - {file = "Sanic_Cors-2.2.0-py2.py3-none-any.whl", hash = "sha256:c3b133ff1f0bb609a53db35f727f5c371dc4ebeb6be4cc2c37c19dd8b9301115"}, -] - -[package.dependencies] -packaging = ">=21.3" -sanic = ">=21.9.3" - -[[package]] -name = "sanic-routing" -version = "22.8.0" -description = "Core routing component for Sanic" -optional = false -python-versions = "*" -files = [ - {file = "sanic-routing-22.8.0.tar.gz", hash = "sha256:305729b4e0bf01f074044a2a315ff401fa7eeffb009eec1d2c81d35e1038ddfc"}, - {file = "sanic_routing-22.8.0-py3-none-any.whl", hash = "sha256:9a928ed9e19a36bc019223be90a5da0ab88cdd76b101e032510b6a7073c017e9"}, -] - -[[package]] -name = "simple-websocket" -version = "0.10.0" -description = "Simple WebSocket server and client for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "simple-websocket-0.10.0.tar.gz", hash = "sha256:82c0b0b1006d5490f09ff66392394d90dd758285635edad241e093e9a8abd3eb"}, - {file = "simple_websocket-0.10.0-py3-none-any.whl", hash = "sha256:fc1bc56c393a187e7268f8ab99da1a8e8da9b5dfb7769a2f3b8dada00067745b"}, -] - -[package.dependencies] -wsproto = "*" - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - -[[package]] -name = "soupsieve" -version = "2.4.1" -description = "A modern CSS selector implementation for Beautiful Soup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, -] - -[[package]] -name = "sphinx" -version = "4.5.0" -description = "Python documentation generator" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"}, - {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"}, -] - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.18" -imagesize = "*" -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] - -[[package]] -name = "sphinx-autobuild" -version = "2021.3.14" -description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." -optional = false -python-versions = ">=3.6" -files = [ - {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, - {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, -] - -[package.dependencies] -colorama = "*" -livereload = "*" -sphinx = "*" - -[package.extras] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "sphinx-autodoc-typehints" -version = "1.19.1" -description = "Type hints (PEP 484) support for the Sphinx autodoc extension" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx_autodoc_typehints-1.19.1-py3-none-any.whl", hash = "sha256:9be46aeeb1b315eb5df1f3a7cb262149895d16c7d7dcd77b92513c3c3a1e85e6"}, - {file = "sphinx_autodoc_typehints-1.19.1.tar.gz", hash = "sha256:6c841db55e0e9be0483ff3962a2152b60e79306f4288d8c4e7e86ac84486a5ea"}, -] - -[package.dependencies] -Sphinx = ">=4.5" - -[package.extras] -testing = ["covdefaults (>=2.2)", "coverage (>=6.3)", "diff-cover (>=6.4)", "nptyping (>=2.1.2)", "pytest (>=7.1)", "pytest-cov (>=3)", "sphobjinv (>=2)", "typing-extensions (>=4.1)"] -type-comments = ["typed-ast (>=1.5.2)"] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -description = "Add a copy button to each of your code cells." -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, - {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, -] - -[package.dependencies] -sphinx = ">=1.8" - -[package.extras] -code-style = ["pre-commit (==2.12.1)"] -rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] - -[[package]] -name = "sphinx-design" -version = "0.4.1" -description = "A sphinx extension for designing beautiful, view size responsive web components." -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx_design-0.4.1-py3-none-any.whl", hash = "sha256:23bf5705eb31296d4451f68b0222a698a8a84396ffe8378dfd9319ba7ab8efd9"}, - {file = "sphinx_design-0.4.1.tar.gz", hash = "sha256:5b6418ba4a2dc3d83592ea0ff61a52a891fe72195a4c3a18b2fa1c7668ce4708"}, -] - -[package.dependencies] -sphinx = ">=4,<7" - -[package.extras] -code-style = ["pre-commit (>=2.12,<3.0)"] -rtd = ["myst-parser (>=0.18.0,<2)"] -testing = ["myst-parser (>=0.18.0,<2)", "pytest (>=7.1,<8.0)", "pytest-cov", "pytest-regressions"] -theme-furo = ["furo (>=2022.06.04,<2022.07)"] -theme-pydata = ["pydata-sphinx-theme (>=0.9.0,<0.10.0)"] -theme-rtd = ["sphinx-rtd-theme (>=1.0,<2.0)"] -theme-sbt = ["sphinx-book-theme (>=0.3.0,<0.4.0)"] - -[[package]] -name = "sphinx-reredirects" -version = "0.1.2" -description = "Handles redirects for moved pages in Sphinx documentation projects" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinx_reredirects-0.1.2-py3-none-any.whl", hash = "sha256:3a22161771aadd448bb608a4fe7277252182a337af53c18372b7104531d71489"}, - {file = "sphinx_reredirects-0.1.2.tar.gz", hash = "sha256:a0e7213304759b01edc22f032f1715a1c61176fc8f167164e7a52b9feec9ac64"}, -] - -[package.dependencies] -sphinx = "*" - -[[package]] -name = "sphinx-resolve-py-references" -version = "0.1.0" -description = "Better python object resolution in Sphinx" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx_resolve_py_references-0.1.0-py2.py3-none-any.whl", hash = "sha256:ccf44a6b62d75c3a568285f4e1815734088c1a7cab7bbb7935bb22fbf0d78bc2"}, - {file = "sphinx_resolve_py_references-0.1.0.tar.gz", hash = "sha256:0f87c06b29ec128964aee2e40d170d1d3c0e5f4955b2618a89ca724f42385372"}, -] - -[package.dependencies] -sphinx = "*" - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.4" -description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, - {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.1" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, - {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxext-opengraph" -version = "0.8.2" -description = "Sphinx Extension to enable OGP support" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinxext-opengraph-0.8.2.tar.gz", hash = "sha256:45a693b6704052c426576f0a1f630649c55b4188bc49eb63e9587e24a923db39"}, - {file = "sphinxext_opengraph-0.8.2-py3-none-any.whl", hash = "sha256:6a05bdfe5176d9dd0a1d58a504f17118362ab976631213cd36fb44c4c40544c9"}, -] - -[package.dependencies] -matplotlib = "*" -sphinx = ">=4.0" - -[[package]] -name = "starlette" -version = "0.27.0" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.7" -files = [ - {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, - {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] - -[[package]] -name = "tornado" -version = "6.3.2" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -optional = false -python-versions = ">= 3.8" -files = [ - {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"}, - {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"}, - {file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"}, - {file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"}, - {file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"}, -] - -[[package]] -name = "tracerite" -version = "1.1.0" -description = "Human-readable HTML tracebacks for Python exceptions" -optional = false -python-versions = "*" -files = [ - {file = "tracerite-1.1.0-py3-none-any.whl", hash = "sha256:4cccac04db05eeeabda45e72b57199e147fa2f73cf64d89cfd625df321bd2ab6"}, - {file = "tracerite-1.1.0.tar.gz", hash = "sha256:041dab8fd4bb405f73506293ac7438a2d311e5f9044378ba7d9a6540392f9e4b"}, -] - -[package.dependencies] -html5tagger = ">=1.2.1" - -[[package]] -name = "typing-extensions" -version = "4.6.3" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, -] - -[[package]] -name = "ujson" -version = "5.7.0" -description = "Ultra fast JSON encoder and decoder for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "ujson-5.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5eba5e69e4361ac3a311cf44fa71bc619361b6e0626768a494771aacd1c2f09b"}, - {file = "ujson-5.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aae4d9e1b4c7b61780f0a006c897a4a1904f862fdab1abb3ea8f45bd11aa58f3"}, - {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2e43ccdba1cb5c6d3448eadf6fc0dae7be6c77e357a3abc968d1b44e265866d"}, - {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54384ce4920a6d35fa9ea8e580bc6d359e3eb961fa7e43f46c78e3ed162d56ff"}, - {file = "ujson-5.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24ad1aa7fc4e4caa41d3d343512ce68e41411fb92adf7f434a4d4b3749dc8f58"}, - {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:afff311e9f065a8f03c3753db7011bae7beb73a66189c7ea5fcb0456b7041ea4"}, - {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e80f0d03e7e8646fc3d79ed2d875cebd4c83846e129737fdc4c2532dbd43d9e"}, - {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:137831d8a0db302fb6828ee21c67ad63ac537bddc4376e1aab1c8573756ee21c"}, - {file = "ujson-5.7.0-cp310-cp310-win32.whl", hash = "sha256:7df3fd35ebc14dafeea031038a99232b32f53fa4c3ecddb8bed132a43eefb8ad"}, - {file = "ujson-5.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:af4639f684f425177d09ae409c07602c4096a6287027469157bfb6f83e01448b"}, - {file = "ujson-5.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b0f2680ce8a70f77f5d70aaf3f013d53e6af6d7058727a35d8ceb4a71cdd4e9"}, - {file = "ujson-5.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a19fd8e7d8cc58a169bea99fed5666023adf707a536d8f7b0a3c51dd498abf"}, - {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6abb8e6d8f1ae72f0ed18287245f5b6d40094e2656d1eab6d99d666361514074"}, - {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8cd622c069368d5074bd93817b31bdb02f8d818e57c29e206f10a1f9c6337dd"}, - {file = "ujson-5.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14f9082669f90e18e64792b3fd0bf19f2b15e7fe467534a35ea4b53f3bf4b755"}, - {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7ff6ebb43bc81b057724e89550b13c9a30eda0f29c2f506f8b009895438f5a6"}, - {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f7f241488879d91a136b299e0c4ce091996c684a53775e63bb442d1a8e9ae22a"}, - {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5593263a7fcfb934107444bcfba9dde8145b282de0ee9f61e285e59a916dda0f"}, - {file = "ujson-5.7.0-cp311-cp311-win32.whl", hash = "sha256:26c2b32b489c393106e9cb68d0a02e1a7b9d05a07429d875c46b94ee8405bdb7"}, - {file = "ujson-5.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed24406454bb5a31df18f0a423ae14beb27b28cdfa34f6268e7ebddf23da807e"}, - {file = "ujson-5.7.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18679484e3bf9926342b1c43a3bd640f93a9eeeba19ef3d21993af7b0c44785d"}, - {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee295761e1c6c30400641f0a20d381633d7622633cdf83a194f3c876a0e4b7e"}, - {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b738282e12a05f400b291966630a98d622da0938caa4bc93cf65adb5f4281c60"}, - {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00343501dbaa5172e78ef0e37f9ebd08040110e11c12420ff7c1f9f0332d939e"}, - {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c0d1f7c3908357ee100aa64c4d1cf91edf99c40ac0069422a4fd5fd23b263263"}, - {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a5d2f44331cf04689eafac7a6596c71d6657967c07ac700b0ae1c921178645da"}, - {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16b2254a77b310f118717715259a196662baa6b1f63b1a642d12ab1ff998c3d7"}, - {file = "ujson-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:6faf46fa100b2b89e4db47206cf8a1ffb41542cdd34dde615b2fc2288954f194"}, - {file = "ujson-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ff0004c3f5a9a6574689a553d1b7819d1a496b4f005a7451f339dc2d9f4cf98c"}, - {file = "ujson-5.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:75204a1dd7ec6158c8db85a2f14a68d2143503f4bafb9a00b63fe09d35762a5e"}, - {file = "ujson-5.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7312731c7826e6c99cdd3ac503cd9acd300598e7a80bcf41f604fee5f49f566c"}, - {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b9dc5a90e2149643df7f23634fe202fed5ebc787a2a1be95cf23632b4d90651"}, - {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a6961fc48821d84b1198a09516e396d56551e910d489692126e90bf4887d29"}, - {file = "ujson-5.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b01a9af52a0d5c46b2c68e3f258fdef2eacaa0ce6ae3e9eb97983f5b1166edb6"}, - {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7316d3edeba8a403686cdcad4af737b8415493101e7462a70ff73dd0609eafc"}, - {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ee997799a23227e2319a3f8817ce0b058923dbd31904761b788dc8f53bd3e30"}, - {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dda9aa4c33435147262cd2ea87c6b7a1ca83ba9b3933ff7df34e69fee9fced0c"}, - {file = "ujson-5.7.0-cp38-cp38-win32.whl", hash = "sha256:bea8d30e362180aafecabbdcbe0e1f0b32c9fa9e39c38e4af037b9d3ca36f50c"}, - {file = "ujson-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:c96e3b872bf883090ddf32cc41957edf819c5336ab0007d0cf3854e61841726d"}, - {file = "ujson-5.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6411aea4c94a8e93c2baac096fbf697af35ba2b2ed410b8b360b3c0957a952d3"}, - {file = "ujson-5.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d3b3499c55911f70d4e074c626acdb79a56f54262c3c83325ffb210fb03e44d"}, - {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341f891d45dd3814d31764626c55d7ab3fd21af61fbc99d070e9c10c1190680b"}, - {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f242eec917bafdc3f73a1021617db85f9958df80f267db69c76d766058f7b19"}, - {file = "ujson-5.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3af9f9f22a67a8c9466a32115d9073c72a33ae627b11de6f592df0ee09b98b6"}, - {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a3d794afbf134df3056a813e5c8a935208cddeae975bd4bc0ef7e89c52f0ce0"}, - {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:800bf998e78dae655008dd10b22ca8dc93bdcfcc82f620d754a411592da4bbf2"}, - {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b5ac3d5c5825e30b438ea92845380e812a476d6c2a1872b76026f2e9d8060fc2"}, - {file = "ujson-5.7.0-cp39-cp39-win32.whl", hash = "sha256:cd90027e6d93e8982f7d0d23acf88c896d18deff1903dd96140613389b25c0dd"}, - {file = "ujson-5.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:523ee146cdb2122bbd827f4dcc2a8e66607b3f665186bce9e4f78c9710b6d8ab"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e87cec407ec004cf1b04c0ed7219a68c12860123dfb8902ef880d3d87a71c172"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bab10165db6a7994e67001733f7f2caf3400b3e11538409d8756bc9b1c64f7e8"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b522be14a28e6ac1cf818599aeff1004a28b42df4ed4d7bc819887b9dac915fc"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7592f40175c723c032cdbe9fe5165b3b5903604f774ab0849363386e99e1f253"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ed22f9665327a981f288a4f758a432824dc0314e4195a0eaeb0da56a477da94d"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:adf445a49d9a97a5a4c9bb1d652a1528de09dd1c48b29f79f3d66cea9f826bf6"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64772a53f3c4b6122ed930ae145184ebaed38534c60f3d859d8c3f00911eb122"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35209cb2c13fcb9d76d249286105b4897b75a5e7f0efb0c0f4b90f222ce48910"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90712dfc775b2c7a07d4d8e059dd58636bd6ff1776d79857776152e693bddea6"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0e4e8981c6e7e9e637e637ad8ffe948a09e5434bc5f52ecbb82b4b4cfc092bfb"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581c945b811a3d67c27566539bfcb9705ea09cb27c4be0002f7a553c8886b817"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d36a807a24c7d44f71686685ae6fbc8793d784bca1adf4c89f5f780b835b6243"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b4257307e3662aa65e2644a277ca68783c5d51190ed9c49efebdd3cbfd5fa44"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea7423d8a2f9e160c5e011119741682414c5b8dce4ae56590a966316a07a4618"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c592eb91a5968058a561d358d0fef59099ed152cfb3e1cd14eee51a7a93879e"}, - {file = "ujson-5.7.0.tar.gz", hash = "sha256:e788e5d5dcae8f6118ac9b45d0b891a0d55f7ac480eddcb7f07263f2bcf37b23"}, -] - -[[package]] -name = "urllib3" -version = "2.0.2" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, - {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "uvicorn" -version = "0.22.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvicorn-0.22.0-py3-none-any.whl", hash = "sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"}, - {file = "uvicorn-0.22.0.tar.gz", hash = "sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvloop" -version = "0.17.0" -description = "Fast implementation of asyncio event loop on top of libuv" -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"}, - {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c"}, - {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d"}, - {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"}, - {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa"}, - {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811"}, - {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c"}, - {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e"}, - {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539"}, - {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4"}, - {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05"}, - {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376"}, - {file = "uvloop-0.17.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b"}, - {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8"}, - {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62"}, - {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d"}, - {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667"}, - {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738"}, - {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20"}, - {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f"}, - {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595"}, - {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578"}, - {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474"}, - {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b"}, - {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c"}, - {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8"}, - {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c"}, - {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9"}, - {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded"}, - {file = "uvloop-0.17.0.tar.gz", hash = "sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1"}, -] - -[package.extras] -dev = ["Cython (>=0.29.32,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] - -[[package]] -name = "watchfiles" -version = "0.19.0" -description = "Simple, modern and high performance file watching and code reload in python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "watchfiles-0.19.0-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:91633e64712df3051ca454ca7d1b976baf842d7a3640b87622b323c55f3345e7"}, - {file = "watchfiles-0.19.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b6577b8c6c8701ba8642ea9335a129836347894b666dd1ec2226830e263909d3"}, - {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:18b28f6ad871b82df9542ff958d0c86bb0d8310bb09eb8e87d97318a3b5273af"}, - {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fac19dc9cbc34052394dbe81e149411a62e71999c0a19e1e09ce537867f95ae0"}, - {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:09ea3397aecbc81c19ed7f025e051a7387feefdb789cf768ff994c1228182fda"}, - {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0376deac92377817e4fb8f347bf559b7d44ff556d9bc6f6208dd3f79f104aaf"}, - {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c75eff897786ee262c9f17a48886f4e98e6cfd335e011c591c305e5d083c056"}, - {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb5d45c4143c1dd60f98a16187fd123eda7248f84ef22244818c18d531a249d1"}, - {file = "watchfiles-0.19.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:79c533ff593db861ae23436541f481ec896ee3da4e5db8962429b441bbaae16e"}, - {file = "watchfiles-0.19.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3d7d267d27aceeeaa3de0dd161a0d64f0a282264d592e335fff7958cc0cbae7c"}, - {file = "watchfiles-0.19.0-cp37-abi3-win32.whl", hash = "sha256:176a9a7641ec2c97b24455135d58012a5be5c6217fc4d5fef0b2b9f75dbf5154"}, - {file = "watchfiles-0.19.0-cp37-abi3-win_amd64.whl", hash = "sha256:945be0baa3e2440151eb3718fd8846751e8b51d8de7b884c90b17d271d34cae8"}, - {file = "watchfiles-0.19.0-cp37-abi3-win_arm64.whl", hash = "sha256:0089c6dc24d436b373c3c57657bf4f9a453b13767150d17284fc6162b2791911"}, - {file = "watchfiles-0.19.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cae3dde0b4b2078f31527acff6f486e23abed307ba4d3932466ba7cdd5ecec79"}, - {file = "watchfiles-0.19.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f3920b1285a7d3ce898e303d84791b7bf40d57b7695ad549dc04e6a44c9f120"}, - {file = "watchfiles-0.19.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9afd0d69429172c796164fd7fe8e821ade9be983f51c659a38da3faaaaac44dc"}, - {file = "watchfiles-0.19.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68dce92b29575dda0f8d30c11742a8e2b9b8ec768ae414b54f7453f27bdf9545"}, - {file = "watchfiles-0.19.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:5569fc7f967429d4bc87e355cdfdcee6aabe4b620801e2cf5805ea245c06097c"}, - {file = "watchfiles-0.19.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5471582658ea56fca122c0f0d0116a36807c63fefd6fdc92c71ca9a4491b6b48"}, - {file = "watchfiles-0.19.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b538014a87f94d92f98f34d3e6d2635478e6be6423a9ea53e4dd96210065e193"}, - {file = "watchfiles-0.19.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20b44221764955b1e703f012c74015306fb7e79a00c15370785f309b1ed9aa8d"}, - {file = "watchfiles-0.19.0.tar.gz", hash = "sha256:d9b073073e048081e502b6c6b0b88714c026a1a4c890569238d04aca5f9ca74b"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - -[[package]] -name = "websockets" -version = "11.0.3" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, - {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, - {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, - {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, - {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, - {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, - {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, - {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, - {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, - {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, - {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, - {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, - {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, - {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, - {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, - {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, - {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, - {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, - {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, - {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, - {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, - {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, - {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, - {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, - {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, - {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, - {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, - {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, - {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, - {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, - {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, - {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, - {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, - {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, - {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, - {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, - {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, - {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, - {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, - {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, - {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, - {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, - {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, - {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, - {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, - {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, - {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, - {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, - {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, - {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, - {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, - {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, - {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, - {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, - {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, - {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, - {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, - {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, - {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, - {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, - {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, - {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, - {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, - {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, - {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, - {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, - {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, - {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, - {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, - {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, -] - -[[package]] -name = "werkzeug" -version = "2.1.2" -description = "The comprehensive WSGI web application library." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"}, - {file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"}, -] - -[package.extras] -watchdog = ["watchdog"] - -[[package]] -name = "wsproto" -version = "1.2.0" -description = "WebSockets state-machine based protocol implementation" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, - {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, -] - -[package.dependencies] -h11 = ">=0.9.0,<1" - -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "629118cfac10f1dab4c39c6ccd50bd69ca68a7fc05dd2baf1d020082d6b19e4e" diff --git a/docs/pyproject.toml b/docs/pyproject.toml deleted file mode 100644 index d2f47c577..000000000 --- a/docs/pyproject.toml +++ /dev/null @@ -1,23 +0,0 @@ -[tool.poetry] -name = "docs" -version = "0.0.0" -description = "docs" -authors = ["rmorshea "] -readme = "README.md" - -[tool.poetry.dependencies] -python = "^3.9" -reactpy = { path = "../src/py/reactpy", extras = ["starlette", "sanic", "fastapi", "flask", "tornado", "testing"], develop = false } -furo = "2022.04.07" -sphinx = "*" -sphinx-autodoc-typehints = "*" -sphinx-copybutton = "*" -sphinx-autobuild = "*" -sphinx-reredirects = "*" -sphinx-design = "*" -sphinx-resolve-py-references = "*" -sphinxext-opengraph = "*" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..9ae8fcf17 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,8 @@ +mkdocs +mkdocs-git-revision-date-localized-plugin +mkdocs-material +mkdocs-include-markdown-plugin +linkcheckmd +mkdocs-spellcheck[all] +mkdocs-git-authors-plugin +mkdocs-minify-plugin diff --git a/docs/source/_custom_js/README.md b/docs/source/_custom_js/README.md deleted file mode 100644 index 4d5d75dc2..000000000 --- a/docs/source/_custom_js/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Custom Javascript for ReactPy's Docs - -Build the javascript with - -``` -npm run build -``` - -This will drop a javascript bundle into `../_static/custom.js` diff --git a/docs/source/_custom_js/package-lock.json b/docs/source/_custom_js/package-lock.json deleted file mode 100644 index 98cbb7014..000000000 --- a/docs/source/_custom_js/package-lock.json +++ /dev/null @@ -1,766 +0,0 @@ -{ - "name": "reactpy-docs-example-loader", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "reactpy-docs-example-loader", - "version": "1.0.0", - "dependencies": { - "@reactpy/client": "file:../../../src/js/packages/@reactpy/client" - }, - "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.1", - "@rollup/plugin-replace": "^3.0.0", - "prettier": "^2.2.1", - "rollup": "^2.35.1" - } - }, - "../../../src/client/packages/@reactpy/client": { - "version": "0.3.1", - "integrity": "sha512-pIK5eNwFSHKXg7ClpASWFVKyZDYxz59MSFpVaX/OqJFkrJaAxBuhKGXNTMXmuyWOL5Iyvb/ErwwDRxQRzMNkfQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "event-to-object": "^0.1.2", - "json-pointer": "^0.6.2" - }, - "devDependencies": { - "@types/json-pointer": "^1.0.31", - "@types/react": "^17.0", - "@types/react-dom": "^17.0", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "react": ">=16 <18", - "react-dom": ">=16 <18" - } - }, - "../../../src/client/packages/client": { - "name": "@reactpy/client", - "version": "0.2.0", - "extraneous": true, - "license": "MIT", - "dependencies": { - "event-to-object": "^0.1.0", - "json-pointer": "^0.6.2" - }, - "devDependencies": { - "@types/json-pointer": "^1.0.31", - "@types/react": "^17.0", - "@types/react-dom": "^17.0", - "prettier": "^3.0.0-alpha.6", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "react": ">=16 <18", - "react-dom": ">=16 <18" - } - }, - "../../../src/js/packages/@reactpy/client": { - "version": "0.3.1", - "license": "MIT", - "dependencies": { - "event-to-object": "^0.1.2", - "json-pointer": "^0.6.2" - }, - "devDependencies": { - "@types/json-pointer": "^1.0.31", - "@types/react": "^17.0", - "@types/react-dom": "^17.0", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "react": ">=16 <18", - "react-dom": ">=16 <18" - } - }, - "node_modules/@reactpy/client": { - "resolved": "../../../src/js/packages/@reactpy/client", - "link": true - }, - "node_modules/@rollup/plugin-commonjs": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", - "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^2.38.3" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.1.tgz", - "integrity": "sha512-6QKtRevXLrmEig9UiMYt2fSvee9TyltGRfw+qSs6xjUnxwjOzTOqy+/Lpxsgjb8mJn1EQNbCDAvt89O4uzL5kw==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^2.42.0" - } - }, - "node_modules/@rollup/plugin-node-resolve/node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz", - "integrity": "sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/pluginutils/node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "node_modules/@types/estree": { - "version": "0.0.48", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", - "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", - "dev": true - }, - "node_modules/@types/node": { - "version": "15.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", - "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rollup": { - "version": "2.52.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.1.tgz", - "integrity": "sha512-/SPqz8UGnp4P1hq6wc9gdTqA2bXQXGx13TtoL03GBm6qGRI6Hm3p4Io7GeiHNLl0BsQAne1JNYY+q/apcY933w==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - }, - "dependencies": { - "@reactpy/client": { - "version": "file:../../../src/js/packages/@reactpy/client", - "requires": { - "@types/json-pointer": "^1.0.31", - "@types/react": "^17.0", - "@types/react-dom": "^17.0", - "event-to-object": "^0.1.2", - "json-pointer": "^0.6.2", - "typescript": "^4.9.5" - } - }, - "@rollup/plugin-commonjs": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", - "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" - }, - "dependencies": { - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - } - } - }, - "@rollup/plugin-node-resolve": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.1.tgz", - "integrity": "sha512-6QKtRevXLrmEig9UiMYt2fSvee9TyltGRfw+qSs6xjUnxwjOzTOqy+/Lpxsgjb8mJn1EQNbCDAvt89O4uzL5kw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "dependencies": { - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - } - } - }, - "@rollup/plugin-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz", - "integrity": "sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - } - } - }, - "@types/estree": { - "version": "0.0.48", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", - "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", - "dev": true - }, - "@types/node": { - "version": "15.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", - "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "rollup": { - "version": "2.52.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.1.tgz", - "integrity": "sha512-/SPqz8UGnp4P1hq6wc9gdTqA2bXQXGx13TtoL03GBm6qGRI6Hm3p4Io7GeiHNLl0BsQAne1JNYY+q/apcY933w==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } -} diff --git a/docs/source/_custom_js/package.json b/docs/source/_custom_js/package.json deleted file mode 100644 index 78d72b961..000000000 --- a/docs/source/_custom_js/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "reactpy-docs-example-loader", - "version": "1.0.0", - "description": "simple javascript client for ReactPy's documentation", - "main": "index.js", - "scripts": { - "build": "rollup --config", - "format": "prettier --ignore-path .gitignore --write ." - }, - "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.1", - "@rollup/plugin-replace": "^3.0.0", - "prettier": "^2.2.1", - "rollup": "^2.35.1" - }, - "dependencies": { - "@reactpy/client": "file:../../../src/js/packages/@reactpy/client" - } -} diff --git a/docs/source/_custom_js/rollup.config.js b/docs/source/_custom_js/rollup.config.js deleted file mode 100644 index 48dd535cf..000000000 --- a/docs/source/_custom_js/rollup.config.js +++ /dev/null @@ -1,26 +0,0 @@ -import resolve from "@rollup/plugin-node-resolve"; -import commonjs from "@rollup/plugin-commonjs"; -import replace from "@rollup/plugin-replace"; - -export default { - input: "src/index.js", - output: { - file: "../_static/custom.js", - format: "esm", - }, - plugins: [ - resolve(), - commonjs(), - replace({ - "process.env.NODE_ENV": JSON.stringify("production"), - preventAssignment: true, - }), - ], - onwarn: function (warning) { - if (warning.code === "THIS_IS_UNDEFINED") { - // skip warning where `this` is undefined at the top level of a module - return; - } - console.warn(warning.message); - }, -}; diff --git a/docs/source/_custom_js/src/index.js b/docs/source/_custom_js/src/index.js deleted file mode 100644 index 505adedd0..000000000 --- a/docs/source/_custom_js/src/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import { SimpleReactPyClient, mount } from "@reactpy/client"; - -let didMountDebug = false; - -export function mountWidgetExample( - mountID, - viewID, - reactpyServerHost, - useActivateButton, -) { - let reactpyHost, reactpyPort; - if (reactpyServerHost) { - [reactpyHost, reactpyPort] = reactpyServerHost.split(":", 2); - } else { - reactpyHost = window.location.hostname; - reactpyPort = window.location.port; - } - - const client = new SimpleReactPyClient({ - serverLocation: { - url: `${window.location.protocol}//${reactpyHost}:${reactpyPort}`, - route: "/", - query: `?view_id=${viewID}`, - }, - }); - - const mountEl = document.getElementById(mountID); - let isMounted = false; - triggerIfInViewport(mountEl, () => { - if (!isMounted) { - activateView(mountEl, client, useActivateButton); - isMounted = true; - } - }); -} - -function activateView(mountEl, client, useActivateButton) { - if (!useActivateButton) { - mount(mountEl, client); - return; - } - - const enableWidgetButton = document.createElement("button"); - enableWidgetButton.appendChild(document.createTextNode("Activate")); - enableWidgetButton.setAttribute("class", "enable-widget-button"); - - enableWidgetButton.addEventListener("click", () => - fadeOutElementThenCallback(enableWidgetButton, () => { - { - mountEl.removeChild(enableWidgetButton); - mountEl.setAttribute("class", "interactive widget-container"); - mountWithLayoutServer(mountEl, serverInfo); - } - }), - ); - - function fadeOutElementThenCallback(element, callback) { - { - var op = 1; // initial opacity - var timer = setInterval(function () { - { - if (op < 0.001) { - { - clearInterval(timer); - element.style.display = "none"; - callback(); - } - } - element.style.opacity = op; - element.style.filter = "alpha(opacity=" + op * 100 + ")"; - op -= op * 0.5; - } - }, 50); - } - } - - mountEl.appendChild(enableWidgetButton); -} - -function triggerIfInViewport(element, callback) { - const observer = new window.IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - callback(); - } - }, - { - root: null, - threshold: 0.1, // set offset 0.1 means trigger if at least 10% of element in viewport - }, - ); - - observer.observe(element); -} diff --git a/docs/source/_exts/async_doctest.py b/docs/source/_exts/async_doctest.py deleted file mode 100644 index 96024d488..000000000 --- a/docs/source/_exts/async_doctest.py +++ /dev/null @@ -1,47 +0,0 @@ -from doctest import DocTest, DocTestRunner -from textwrap import indent -from typing import Any - -from sphinx.application import Sphinx -from sphinx.ext.doctest import DocTestBuilder -from sphinx.ext.doctest import setup as doctest_setup - -test_template = """ -import asyncio as __test_template_asyncio - -async def __test_template__main(): - - {test} - - globals().update(locals()) - -__test_template_asyncio.run(__test_template__main()) -""" - - -class TestRunnerWrapper: - def __init__(self, runner: DocTestRunner): - self._runner = runner - - def __getattr__(self, name: str) -> Any: - return getattr(self._runner, name) - - def run(self, test: DocTest, *args: Any, **kwargs: Any) -> Any: - for ex in test.examples: - ex.source = test_template.format(test=indent(ex.source, " ").strip()) - return self._runner.run(test, *args, **kwargs) - - -class AsyncDoctestBuilder(DocTestBuilder): - @property - def test_runner(self) -> DocTestRunner: - return self._test_runner - - @test_runner.setter - def test_runner(self, value: DocTestRunner) -> None: - self._test_runner = TestRunnerWrapper(value) - - -def setup(app: Sphinx) -> None: - doctest_setup(app) - app.add_builder(AsyncDoctestBuilder, override=True) diff --git a/docs/source/_exts/autogen_api_docs.py b/docs/source/_exts/autogen_api_docs.py deleted file mode 100644 index b95d85a99..000000000 --- a/docs/source/_exts/autogen_api_docs.py +++ /dev/null @@ -1,146 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Collection, Iterator -from pathlib import Path - -from sphinx.application import Sphinx - -HERE = Path(__file__).parent -SRC = HERE.parent.parent.parent / "src" -PYTHON_PACKAGE = SRC / "py" / "reactpy" / "reactpy" - -AUTO_DIR = HERE.parent / "_auto" -AUTO_DIR.mkdir(exist_ok=True) - -API_FILE = AUTO_DIR / "apis.rst" - -# All valid RST section symbols - it shouldn't be realistically possible to exhaust them -SECTION_SYMBOLS = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" - -AUTODOC_TEMPLATE_WITH_MEMBERS = """\ -.. automodule:: {module} - :members: - :ignore-module-all: -""" - -AUTODOC_TEMPLATE_WITHOUT_MEMBERS = """\ -.. automodule:: {module} - :ignore-module-all: -""" - -TITLE = """\ -========== -Python API -========== -""" - - -def generate_api_docs(): - content = [TITLE] - - for file in walk_python_files(PYTHON_PACKAGE, ignore_dirs={"__pycache__"}): - if file.name == "__init__.py": - if file.parent != PYTHON_PACKAGE: - content.append(make_package_section(file)) - else: - content.append(make_module_section(file)) - - API_FILE.write_text("\n".join(content)) - - -def make_package_section(file: Path) -> str: - parent_dir = file.parent - symbol = get_section_symbol(parent_dir) - section_name = f"``{parent_dir.name}``" - module_name = get_module_name(parent_dir) - return ( - section_name - + "\n" - + (symbol * len(section_name)) - + "\n" - + AUTODOC_TEMPLATE_WITHOUT_MEMBERS.format(module=module_name) - ) - - -def make_module_section(file: Path) -> str: - symbol = get_section_symbol(file) - section_name = f"``{file.stem}``" - module_name = get_module_name(file) - return ( - section_name - + "\n" - + (symbol * len(section_name)) - + "\n" - + AUTODOC_TEMPLATE_WITH_MEMBERS.format(module=module_name) - ) - - -def get_module_name(path: Path) -> str: - return ".".join(path.with_suffix("").relative_to(PYTHON_PACKAGE.parent).parts) - - -def get_section_symbol(path: Path) -> str: - rel_path = path.relative_to(PYTHON_PACKAGE) - rel_path_parts = rel_path.parts - if len(rel_path_parts) > len(SECTION_SYMBOLS): - msg = f"package structure is too deep - ran out of section symbols: {rel_path}" - raise RuntimeError(msg) - return SECTION_SYMBOLS[len(rel_path_parts) - 1] - - -def walk_python_files(root: Path, ignore_dirs: Collection[str]) -> Iterator[Path]: - """Iterate over Python files - - We yield in a particular order to get the correction title section structure. Given - a directory structure of the form:: - - project/ - __init__.py - /package - __init__.py - module_a.py - module_b.py - - We yield the files in this order:: - - project/__init__.py - project/package/__init__.py - project/package/module_a.py - project/module_b.py - - In this way we generate the section titles in the appropriate order:: - - project - ======= - - project.package - --------------- - - project.package.module_a - ------------------------ - - """ - for path in sorted( - root.iterdir(), - key=lambda path: ( - # __init__.py files first - int(not path.name == "__init__.py"), - # then directories - int(not path.is_dir()), - # sort by file name last - path.name, - ), - ): - if path.is_dir(): - if (path / "__init__.py").exists() and path.name not in ignore_dirs: - yield from walk_python_files(path, ignore_dirs) - elif path.suffix == ".py": - yield path - - -def setup(app: Sphinx) -> None: - if sys.platform == "win32" and sys.version_info[:2] == (3, 7): - return None - generate_api_docs() - return None diff --git a/docs/source/_exts/build_custom_js.py b/docs/source/_exts/build_custom_js.py deleted file mode 100644 index 97857ba74..000000000 --- a/docs/source/_exts/build_custom_js.py +++ /dev/null @@ -1,12 +0,0 @@ -import subprocess -from pathlib import Path - -from sphinx.application import Sphinx - -SOURCE_DIR = Path(__file__).parent.parent -CUSTOM_JS_DIR = SOURCE_DIR / "_custom_js" - - -def setup(app: Sphinx) -> None: - subprocess.run("npm install", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607 - subprocess.run("npm run build", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607 diff --git a/docs/source/_exts/copy_vdom_json_schema.py b/docs/source/_exts/copy_vdom_json_schema.py deleted file mode 100644 index 38fc171ac..000000000 --- a/docs/source/_exts/copy_vdom_json_schema.py +++ /dev/null @@ -1,17 +0,0 @@ -import json -from pathlib import Path - -from sphinx.application import Sphinx - -from reactpy.core.vdom import VDOM_JSON_SCHEMA - - -def setup(app: Sphinx) -> None: - schema_file = Path(__file__).parent.parent / "vdom-json-schema.json" - current_schema = json.dumps(VDOM_JSON_SCHEMA, indent=2, sort_keys=True) - - # We need to make this check because the autoreload system for the docs checks - # to see if the file has changed to determine whether to re-build. Thus we should - # only write to the file if its contents will be different. - if not schema_file.exists() or schema_file.read_text() != current_schema: - schema_file.write_text(current_schema) diff --git a/docs/source/_exts/custom_autosectionlabel.py b/docs/source/_exts/custom_autosectionlabel.py deleted file mode 100644 index 92ff5e2df..000000000 --- a/docs/source/_exts/custom_autosectionlabel.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Mostly copied from sphinx.ext.autosectionlabel - -See Sphinx BSD license: -https://github.com/sphinx-doc/sphinx/blob/f9968594206e538f13fa1c27c065027f10d4ea27/LICENSE -""" - -from __future__ import annotations - -from fnmatch import fnmatch -from typing import Any, cast - -from docutils import nodes -from docutils.nodes import Node -from sphinx.application import Sphinx -from sphinx.domains.std import StandardDomain -from sphinx.locale import __ -from sphinx.util import logging -from sphinx.util.nodes import clean_astext - -logger = logging.getLogger(__name__) - - -def get_node_depth(node: Node) -> int: - i = 0 - cur_node = node - while cur_node.parent != node.document: - cur_node = cur_node.parent - i += 1 - return i - - -def register_sections_as_label(app: Sphinx, document: Node) -> None: - docname = app.env.docname - - for pattern in app.config.autosectionlabel_skip_docs: - if fnmatch(docname, pattern): - return None - - domain = cast(StandardDomain, app.env.get_domain("std")) - for node in document.traverse(nodes.section): - if ( - app.config.autosectionlabel_maxdepth - and get_node_depth(node) >= app.config.autosectionlabel_maxdepth - ): - continue - labelid = node["ids"][0] - - title = cast(nodes.title, node[0]) - ref_name = getattr(title, "rawsource", title.astext()) - if app.config.autosectionlabel_prefix_document: - name = nodes.fully_normalize_name(docname + ":" + ref_name) - else: - name = nodes.fully_normalize_name(ref_name) - sectname = clean_astext(title) - - if name in domain.labels: - logger.warning( - __("duplicate label %s, other instance in %s"), - name, - app.env.doc2path(domain.labels[name][0]), - location=node, - type="autosectionlabel", - subtype=docname, - ) - - domain.anonlabels[name] = docname, labelid - domain.labels[name] = docname, labelid, sectname - - -def setup(app: Sphinx) -> dict[str, Any]: - app.add_config_value("autosectionlabel_prefix_document", False, "env") - app.add_config_value("autosectionlabel_maxdepth", None, "env") - app.add_config_value("autosectionlabel_skip_docs", [], "env") - app.connect("doctree-read", register_sections_as_label) - - return { - "version": "builtin", - "parallel_read_safe": True, - "parallel_write_safe": True, - } diff --git a/docs/source/_exts/patched_html_translator.py b/docs/source/_exts/patched_html_translator.py deleted file mode 100644 index e2f8ed9a4..000000000 --- a/docs/source/_exts/patched_html_translator.py +++ /dev/null @@ -1,24 +0,0 @@ -from sphinx.util.docutils import is_html5_writer_available -from sphinx.writers.html import HTMLTranslator -from sphinx.writers.html5 import HTML5Translator - - -class PatchedHTMLTranslator( - HTML5Translator if is_html5_writer_available() else HTMLTranslator -): - def starttag(self, node, tagname, *args, **attrs): - if ( - tagname == "a" - and "target" not in attrs - and ( - "external" in attrs.get("class", "") - or "external" in attrs.get("classes", []) - ) - ): - attrs["target"] = "_blank" - attrs["ref"] = "noopener noreferrer" - return super().starttag(node, tagname, *args, **attrs) - - -def setup(app): - app.set_translator("html", PatchedHTMLTranslator) diff --git a/docs/source/_exts/reactpy_example.py b/docs/source/_exts/reactpy_example.py deleted file mode 100644 index c6b054c07..000000000 --- a/docs/source/_exts/reactpy_example.py +++ /dev/null @@ -1,180 +0,0 @@ -from __future__ import annotations - -import re -from pathlib import Path -from typing import Any - -from docs_app.examples import ( - SOURCE_DIR, - get_example_files_by_name, - get_normalized_example_name, -) -from docutils.parsers.rst import directives -from docutils.statemachine import StringList -from sphinx.application import Sphinx -from sphinx.util.docutils import SphinxDirective -from sphinx_design.tabs import TabSetDirective - - -class WidgetExample(SphinxDirective): - has_content = False - required_arguments = 1 - _next_id = 0 - - option_spec = { - "result-is-default-tab": directives.flag, - "activate-button": directives.flag, - } - - def run(self): - example_name = get_normalized_example_name( - self.arguments[0], - # only used if example name starts with "/" - self.get_source_info()[0], - ) - - show_linenos = "linenos" in self.options - live_example_is_default_tab = "result-is-default-tab" in self.options - activate_result = "activate-button" not in self.options - - ex_files = get_example_files_by_name(example_name) - if not ex_files: - src_file, line_num = self.get_source_info() - msg = f"Missing example named {example_name!r} referenced by document {src_file}:{line_num}" - raise ValueError(msg) - - labeled_tab_items: list[tuple[str, Any]] = [] - if len(ex_files) == 1: - labeled_tab_items.append( - ( - "main.py", - _literal_include( - path=ex_files[0], - linenos=show_linenos, - ), - ) - ) - else: - for path in sorted( - ex_files, key=lambda p: "" if p.name == "main.py" else p.name - ): - labeled_tab_items.append( - ( - path.name, - _literal_include( - path=path, - linenos=show_linenos, - ), - ) - ) - - result_tab_item = ( - "🚀 result", - _interactive_widget( - name=example_name, - with_activate_button=not activate_result, - ), - ) - if live_example_is_default_tab: - labeled_tab_items.insert(0, result_tab_item) - else: - labeled_tab_items.append(result_tab_item) - - return TabSetDirective( - "WidgetExample", - [], - {}, - _make_tab_items(labeled_tab_items), - self.lineno - 2, - self.content_offset, - "", - self.state, - self.state_machine, - ).run() - - -def _make_tab_items(labeled_content_tuples): - tab_items = "" - for label, content in labeled_content_tuples: - tab_items += _tab_item_template.format( - label=label, - content=content.replace("\n", "\n "), - ) - return _string_to_nested_lines(tab_items) - - -def _literal_include(path: Path, linenos: bool): - try: - language = { - ".py": "python", - ".js": "javascript", - ".json": "json", - }[path.suffix] - except KeyError: - msg = f"Unknown extension type {path.suffix!r}" - raise ValueError(msg) from None - - return _literal_include_template.format( - name=str(path.relative_to(SOURCE_DIR)), - language=language, - options=_join_options(_get_file_options(path)), - ) - - -def _join_options(option_strings: list[str]) -> str: - return "\n ".join(option_strings) - - -OPTION_PATTERN = re.compile(r"#\s:[\w-]+:.*") - - -def _get_file_options(file: Path) -> list[str]: - options = [] - - for line in file.read_text().split("\n"): - if not line.strip(): - continue - if not line.startswith("#"): - break - if not OPTION_PATTERN.match(line): - continue - option_string = line[1:].strip() - if option_string: - options.append(option_string) - - return options - - -def _interactive_widget(name, with_activate_button): - return _interactive_widget_template.format( - name=name, - activate_button_opt=":activate-button:" if with_activate_button else "", - ) - - -_tab_item_template = """ -.. tab-item:: {label} - - {content} -""" - - -_interactive_widget_template = """ -.. reactpy-view:: {name} - {activate_button_opt} -""" - - -_literal_include_template = """ -.. literalinclude:: /{name} - :language: {language} - {options} -""" - - -def _string_to_nested_lines(content): - return StringList(content.split("\n")) - - -def setup(app: Sphinx) -> None: - app.add_directive("reactpy", WidgetExample) diff --git a/docs/source/_exts/reactpy_view.py b/docs/source/_exts/reactpy_view.py deleted file mode 100644 index 7a2bf85a4..000000000 --- a/docs/source/_exts/reactpy_view.py +++ /dev/null @@ -1,63 +0,0 @@ -import os -import sys - -print(sys.path) - -from docs_app.examples import get_normalized_example_name -from docutils.nodes import raw -from docutils.parsers.rst import directives -from sphinx.application import Sphinx -from sphinx.util.docutils import SphinxDirective - -_REACTPY_EXAMPLE_HOST = os.environ.get("REACTPY_DOC_EXAMPLE_SERVER_HOST", "") -_REACTPY_STATIC_HOST = os.environ.get("REACTPY_DOC_STATIC_SERVER_HOST", "/docs").rstrip( - "/" -) - - -class IteractiveWidget(SphinxDirective): - has_content = False - required_arguments = 1 - _next_id = 0 - - option_spec = { - "activate-button": directives.flag, - "margin": float, - } - - def run(self): - IteractiveWidget._next_id += 1 - container_id = f"reactpy-widget-{IteractiveWidget._next_id}" - view_id = get_normalized_example_name( - self.arguments[0], - # only used if example name starts with "/" - self.get_source_info()[0], - ) - return [ - raw( - "", - f""" -
-
- -
- """, - format="html", - ) - ] - - -def setup(app: Sphinx) -> None: - app.add_directive("reactpy-view", IteractiveWidget) diff --git a/docs/source/_static/css/furo-theme-overrides.css b/docs/source/_static/css/furo-theme-overrides.css deleted file mode 100644 index a258e025e..000000000 --- a/docs/source/_static/css/furo-theme-overrides.css +++ /dev/null @@ -1,6 +0,0 @@ -.sidebar-container { - width: 18em; -} -.sidebar-brand-text { - display: none; -} diff --git a/docs/source/_static/css/larger-api-margins.css b/docs/source/_static/css/larger-api-margins.css deleted file mode 100644 index f8b75d592..000000000 --- a/docs/source/_static/css/larger-api-margins.css +++ /dev/null @@ -1,7 +0,0 @@ -:is(.data, .function, .class, .exception).py { - margin-top: 3em; -} - -:is(.attribute, .method).py { - margin-top: 1.8em; -} diff --git a/docs/source/_static/css/larger-headings.css b/docs/source/_static/css/larger-headings.css deleted file mode 100644 index 297ab7202..000000000 --- a/docs/source/_static/css/larger-headings.css +++ /dev/null @@ -1,9 +0,0 @@ -h1, -h2, -h3, -h4, -h5, -h6 { - margin-top: 1.5em !important; - font-weight: 900 !important; -} diff --git a/docs/source/_static/css/reactpy-view.css b/docs/source/_static/css/reactpy-view.css deleted file mode 100644 index 56df74970..000000000 --- a/docs/source/_static/css/reactpy-view.css +++ /dev/null @@ -1,43 +0,0 @@ -.interactive { - -webkit-transition: 0.1s ease-out; - -moz-transition: 0.1s ease-out; - -o-transition: 0.1s ease-out; - transition: 0.1s ease-out; -} -.widget-container { - padding: 15px; - overflow: auto; - background-color: var(--color-code-background); - min-height: 75px; -} - -.widget-container .printout { - margin-top: 20px; - border-top: solid 2px var(--color-foreground-border); - padding-top: 20px; -} - -.widget-container > div { - width: 100%; -} - -.enable-widget-button { - padding: 10px; - color: #ffffff !important; - text-transform: uppercase; - text-decoration: none; - background: #526cfe; - border: 2px solid #526cfe !important; - transition: all 0.1s ease 0s; - box-shadow: 0 5px 10px var(--color-foreground-border); -} -.enable-widget-button:hover { - color: #526cfe !important; - background: #ffffff; - transition: all 0.1s ease 0s; -} -.enable-widget-button:focus { - outline: 0 !important; - transform: scale(0.98); - transition: all 0.1s ease 0s; -} diff --git a/docs/source/_static/css/sphinx-design-overrides.css b/docs/source/_static/css/sphinx-design-overrides.css deleted file mode 100644 index 767d9d16c..000000000 --- a/docs/source/_static/css/sphinx-design-overrides.css +++ /dev/null @@ -1,14 +0,0 @@ -.sd-card-body { - display: flex; - flex-direction: column; - align-items: stretch; -} - -.sd-tab-content .highlight pre { - max-height: 700px; - overflow: auto; -} - -.sd-card-title .sd-badge { - font-size: 1em; -} diff --git a/docs/source/_static/css/widget-output-css-overrides.css b/docs/source/_static/css/widget-output-css-overrides.css deleted file mode 100644 index 7ddf1a792..000000000 --- a/docs/source/_static/css/widget-output-css-overrides.css +++ /dev/null @@ -1,8 +0,0 @@ -.widget-container h1, -.widget-container h2, -.widget-container h3, -.widget-container h4, -.widget-container h5, -.widget-container h6 { - margin: 0 !important; -} diff --git a/docs/source/_static/install-and-run-reactpy.gif b/docs/source/_static/install-and-run-reactpy.gif deleted file mode 100644 index 49d431341636da4a56becf7fdfb24f3fa7fc6685..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1049193 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≨410>X{bt&~$CMkjVXhw%e65(e9SQd1+k~@UKmdvrUiRMF-tD>@ zWX&AizhN8U&*QNEH516!I(K_VlP_#w+#iU567cb0PV=Cj7mb4=uo9@~({wrm?^BM_J8qo7hX|??ZSs=n4!BHhVJf?Vd!o| zK)SoTySt>MLApy&8U%zPrBgaYN9lD&Z#(FsfyI+ZUK=B+k*joOBW7*%KKSdB>#$BfnQNkCbjnnYhPeY)dJM1Frg#9s0(E&X0JiVH8L}kbz z0u%YxC^WFZXT@~Rp@h$PeU{#IcAaq`EEV0W+ z7qiey7#6>ZM(x$!yW*t(A8pm#HeSR0c!-88y5tIr*4%cm$h8Jj_n!1B7dTRK8kNdS zhd`1k`F3SM#TW*sfShj6{OqFPT84?>M3b|a-+teV16_#wYvt@4$~L3RYlZ>&4eCCx zw8=!((2A(%%!nN_PyPOlI-J0>o6tc9hzw&N(IjYmO#I+WT2}GgJohU(;uoiwfRaCw z3dQ^cPRf*aWI~R_h*7Ls38PrG)vEy_V1T8tf;WpDs{jx z0N&#Q0(=d`{+2-gF%VgFes8Xy@ulrYNHB=fX3C@WjIRXWO#slnPF}BOU>qSz-#+#C zxQQOk;Q|dVd05Ia#q%rXVzGHp<5ANnw5obqCiE%sI6{+{2w#RcS?n~3ZuTw5$%l%7 z0efYH+V7#9mf5GUZ_*)!8sXXN!h=b8jOr!P9Nm|*y)4(IP6FR9%}9ryfB*4I%~GcM zj>1E-$^iY4PVY&6zV1MUN66)^EsPJ1QppXI@4mEX(maJq&LB^f`L{|(urs%TfzxZ4 zgv=$0Q(GH;KhpoFS-5$b{@$v@@h+z?67NWwp|TuXo&I*+?S}r zx$?tXaA8dvlRzpRm^fW>@Ioyw0MFCo2=;7L2W0phSfIrDr3igjdVGQPvSV2fvr_z5 zi_uC62 z@jO|VpT?}G7kYNoC;|By>ANK8l7i3j=Q}BEl0JORJtI1tm8?qin*IKi#jZ}egMU5( zyKh&R&0UEWOd%Z%lIe$P6u7f1g5vqYu_dh09f;pJ$LJ}eM9zgyy4a?w7avm8Rn5oCi$J`^KnX_O}L?TP9q!)_1gH_ z4G5j)0uZzAO%QVQ!I8xr^Dsa3&T6catXaoEA~FLdhlaN{ggL92zKWg$(zp?jGe$4+ zg<4Fle)Jk_?`0d(-N*7w_H8(o0?;ynEmzMfPbc%&wrdPt{_fY=4wBcI9H!k{ex1Rh z`w12#E^-3WcwL6gRDsN$^viOgc~rK zX?PhkTZkxgLAeP@;|jx7M(TUmlB?=8t`7O}beE`sm#}bg=vpH}^?iK`1-VYyIAiUt z3q|h`ru0i=_Wk0=?~Vbf&r5i}aENuJa5en*GOCQ89o!)!MfUnB4|tV*KSFN}GA<$| zy>K&T1EvN;#ps&cdZx}iVxfKUZKP;&z>Fe{ajtM2yoj3G&&mRg0)(E1mwwdZ0@oYv z>a3H*p3{HT^$dpYHw2Ee(i67>L4%(*JJsTUf^XfXr^s`S(!1Je8~BiOlcTMJUHOI* zlgpyCGfskcyi{*XL^zHh2jOK-NuaXhPv(F*=&$Mb9g=#{n(-Iv?^PhB>z0GPyw^Mo z;$>hT_s{A6$ebS}%)q#YBIQL6-ajx+0j}1~%h_-TR`U@A$Fm2vW-SjuG=E&W=tM3! zcn?|7Ux&cN7YQ%%nz3^o*j7D=&u5rH_l5}jWhgHT^d_CliGMGmg>1NM2|I;mIEy-c zfq=#}E%v(ZfLfD&g2w?8Laeq!p?{cj90N{gAj4vT< zS82h@nvY>0!#?0Dl)_c1eZ0nB7bB8cjn~IhbUfWfq1=}}m7VTm+Xho6S z?54zQ5*8bt+j4&*pUq|TdiPb2RhAk9SJ4RObVKRl* zRJKhYE0g_UbqIt-%TX@! zjvFhu$2mTZ(5&x*ZOQ0C>gfzLQ?-e><)mLX)o#Hj`aD4*7Dz7J%wwy0HaLz= z$RlOL|5zuuN_O{g&PB#)%3EUio_Ai>PS9BKZp;&dtub7&29d_E6R9@|MFlIyMP^&= zPIgmwjLvl_HE!Z$)eh;*5Nk~8pWO95&ubwQYm;uuz|gg6bO8(ES%dBN-2csOGSj>T z^l$Fd##$Hfpur0BBq`()ng}$#E$P1|+sX?4OxUQ6SM9k2zq8++aWK86E|9?qGCv?7 zDbVpLS2Gu0(Gsjfx&NO#h#{NLsfsf+m)i=c;j4Uqd|H-;_X*l3R!mCxL%$_qYdlh{ z4$r!Xvj^ko^PX}^KKv}=FgczdD4kn=^eUA?-@(|BrD%VJ-n`gAt1XeSq&o9U`eDo2 zRlpi9x9!!4xS@g+pW2r@u8q$ul}GbP24dBJGlFIoMrYlc>=V+acd#&Fe?}I$l;}}I zSuKp7Q+GvBhFustny09bW=oPu>sD7q*5)Res5&gS&bT$zcU1Qu^RM-)GN(Gc+n_qe z3(oC1^^_O*ZE~oITLORYaNDnpey%7Yy4%6+gLdR{UY^kNj`P*e7LIfZPs)$WJJI(; zza73BJHbEtfAVc_1#=S&&XphHt zqO~Tw$CUn~31^E|rQ4a_Rj`DdYQD_joJ8Pl*CKDLAOcq>Ods0c_xM^W1T8qnJ#@KY zVrsVio(-eBA>OBM1rTR(PVaGv7aB*`C7%a5PS zMXR*R5~a90%R3PbyL1d4jmR2+2aY8srbqgHsD|MZ6Ry#t;D%8G^GFu7_v$mSfIGRT z>HZM#zz!R!YzA&Do|KQttJgD%ZfbJCz=+{DsC$&y7pE!S7O~3aAmiOQ3-S~)Nc1y% zLgL4lk+UihvdH|p$)uUd6z@sB@W~6z3EB~bkmv~y>xoy-6CBaX?uhRSU#woiBqw9C z<%%WKJs4H6|BO$`Dsql~c`l3qxR>Woru6M~gt-?3t|Kk%<2>_Y7|CdN>}B|pIw4p9 z(iY?~yg2viTG?9^`9fsKBMGw}%01h3yP7K63kcnUYB57|H#OCGF$IVR%3rrQ!Lz0q z@(61qLux@1l2v-Uf?-e`a`R|_xoQwEO1ct|?jx%62sSNVtS=s_k!Y-^A#yV~^$Qis z^r)KRh)Uykm2$~}*XGkrz{o!I1eU%+9Cx7rJk@mGq|O6oNwltLaUlJ5#fY`on6=t2 znHb?C(9IoLWD3cdH(FqdCiW5NAsYR-r|kJ*bUR$*lymj~p_Z86pu7v6TclDc#8Np^ zp8Fy&mF1)fRFnH7I2yY)?vbc`{88b@lIRl{{;Z~Xzclwfcn&x=XK1bXv^jUH2ft>U zLb=xjzMa#sreVpC)EE@F$Zya_MUS7w;v@ z(%EIEzecHRt5XPXS$lDFA+b$@K=LYWN}-5@z8Fi-G6L3lLiKswH>6x-Q8I)4zTTA| z-_XQfYvB>kZ`MZGwJ-@8f;z;xD|Oh#PJurEpAL>FeeVMPk6QZ1Q-|mt#XP;2YD-(} zWS($W#|mHDryjf(IA7%{_&tGu1cHunstx&wvY9N zqv+QrA_+yP{%uXfs0B&E3#|1)`YI2>icmp#`OG+m&$14a*7U9N+hWbaWcZsIeG4wQ zQuY_6JIyx@TKBr9pVd7mC6w{+yDM-A`8QrC5?UqdUg{_*9jNuPSYcx5C@$yYn(q!Br#USur( z*~8E%prAS>`YX2@IrGhQ(je4E!pG*@vA_IwZC=Z2r6+cHg;HXbJqAf5_}l67CZ1th zyQWn~`zx&Q9)L13zdUCTXb0grTM)c9l@U*nY{pwiREX^2-U*yV& z3+suC_&J}CR)qXBgZMP&LhMb@HR}FpN`DM44Qp{{{E}2gs%S_$k;TgFB(>*rHc}+Z z@e&@6GOHdP(jpX_I%5&&n403VaSG@k7}IzcDSy_QJvmRLQPy*Wo>MSWT**nw5|K$= zNzgl)GFZDxc%A42SuPWbSZ9*n@qas0tKW=mjO=65AvI~1V$d?6^;^&wjcZw7YUNgL zg_vogv2$BK+vu&&W-Fo?Zmn-x%CvHkGG{42S8rp+P(=09V&e=Q;F?h2E)+rO4ieD< z>9rfr>{8YD<`?wF8;HO0j?dvQdk?67cj1n@jhAE28+!(v&whjXUb�X!Vl~*%$d% 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

vEE#O=V!c!%q_9GaoV+6Et%`p&b&f_Ce{7dbUF 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á~*8) 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

$W-|LfLzSn2d2`8t4m;bXQbwn$PTg#-R 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℘GiKSWx z1B%LKBoS;N8e}HFT(mlvlo1aUlO0bE)!}w0KML&vP{H&}1rDG0(a$5FNm*e1%!nq` zv3FxkEbPhf4RS{5flTlG{r)kNBq<+ ze(~)cCbi+Q(Px{ne5D-26O$$v@9ZF_VyBf5kG=k|BhrYW2w&38#-sU@mwG5dL9g3H z{)PWo03{1(Qh`T$2K3owvzAIXWfv%QG2{hp55 zl2_MwvCgw;u!#Z~Zega-Fm%f$x_T{^?__@HKByY$bY7XaaG);Q8xu zbW|5c^S)OP6Po(yS8w;S1v1LqAI=nEfOhI3)Eamm^5l-X-5B(|K9+-YqXeazy+kLM z@|ixpMLU@uhVuI&OZK(B%J1C_cyOKZ)Rcr`s?VZIHby(kz_RKBm8G+WRtH@-Yg`^p z^Rew{PE4`*RtUAW2`*Q&3@q8RNMt$SvJewv8>$s5g|MdbPi9aNo6C>YS<4!pYVh?nNc<9AZK$Ej=TIgTnYZ5xCGOOPkcwQKVP%qQ^Kas~Sr;ipv%c(f zr9o)ws<%8g+#3yuIg3;X#T5?Nh}Gct&kUYwDLs2;_yS)pOk|cqk?4)OUL4f*qgpS% zIF%mHys^`_v?9x)NoKmIAUR;t+kCO$Fuf-ELx%9lEM@|&rlmnM6PG}SO0~cH18~_h zwH^fiXlgC-yMLIs2wH{Y3&iwsh-U;Vh~E;Ltf`<@#;2|2J!e>fq9}?8#4Q91ZCapN z;x$(qlk4e553C?u;bM#h!q(-Bwz(MgqDJvA%keR#?yHQG6>_SJTSBFjyrj&a^k+27UbG7}QTnt0~79MCo&Yc}nn*$rePeiQxVSR~U5t@sZ1N*`;Mi zK@$phmMbT(^5F~+ir+m|YHZt6D{kyERt?Kz^AZyb>Wi+mrbV1mhvhNzMyH+_rAt5d zRbHB}xQ8gq+V9DD{S;4(`^(JfzC`VM5C%J4Jw`m3xww=>yvwxheQ#L2;?XYj>tyiQ z^l+3C|6E^s?~@yUMLho|(GHZd4c(D)#`JuQah>uBmKRaFqa*e^A5Y<1^=4T6KH=e&s!Nn*-X{#G40mPi()h?e!`0n5798mWRgweX+I5~H@HL@e zHg@^&JUOi7L1oF$^6|v;h3-T8&{Hzv?&o3h_I#Pt`ssz#Xz@_lak)r>w-wZM`~4O? zipfNt!N^c^ya--sKVRoXh_M3@*CbMp%Alp2%m*B8do_mb0f>K7)gR%|$zbGY9`|H{ zk#g7wwCe{Tc-^XJFL5Tn?9ttjetgahTu&&{b_)$G?FuD405B3;$*CJoCugL_zaq9L zZxpC=2{LB-P{}{c&VGt29yDL~oue-eOy-RwV!)<177GjYNG&A+VWZjg2zAVhaKExM zY$+$hX|?+kyrFWCGr0t|zw+KnrK3;{Ul_{;H$=**_*WCTK!oZgry+9iVzUL#N4t&5 z`elblql#+1{)>&%@fLPKq-K3?CuYaGqS#kAe2Gk6$SDj{Yj;uodCP8IKrIxKG8oIo zyw4RsMas$|p%-nK%AQlJVJ`7@svB4?R1i$}tW?o* z6wdI2xjAyXC4f1H46HsUjL)jmRaGlYI?kQaf&&ckW1s;^Xgs?*1j*2(rYXjmA&MZm z+8IAwh+R-D^jHp05f=04&lZUhov%;8UuT_4Z^AqU^EOYR}cca)0xg(kyu?J75IDa(gNUIuPE)nW;H?u6PhWk(#&p8x#Vp~!4 zNtDAdQw_$xzS!8z(DYdzf9F$ff%x<`0%QGxF2i1=jgq4_G{g?Mg`E)F#lBe|MQ$A& z&L>tkMY8fd1GdmE=n3Jw>NHV2zoAQuNs$l*8k3=$7y4{232cBNiE(tWFRa%n{y}%b z*R>S9Y;xTKe>VthMqh*9bdE|lbYTxC&hpjgeb6?~hF$Mx4sa1XB7TA?{k^P6pNaz` zmNpW+xVOBju`d#1MiafDcQlkw35W)X^fIOxn2hl|orAfp8F7K_K-e$&{~1a5jFL_T z<&{uNmTAKwQza9kp@q$@8yW!=g@mF;#a3*tB)qId4Pg z!&(~tBIFY&@75{P{JW}Zk`AAF-ZB*OHWK(e!h#1Chf0IG6KZnF1jX`;kVdG_W6 zhF^RJm1I1C_bd#oXxt@RDLt!~__)>-eNWbhH_et?$-dEu1Q33!n4?IvG4i(vCxM+e^~e|n)G z|2uky@{>!QYW{h~z+ZV1w8*e*)1?F2_-=?{;%Z`M;UfLnAM1f)zquz5R~66w4lN4a zE};W2vpBygdAgE~{F3P>KMB}p8_Dy?kmN6ZE`GlJ`C>JtWPweO{|wmVNsz7vv& z|Ix<V@hnry{odluYP-HMf`?-T<0F{)NZPFWoZ&c+$-s4p&fRqoz|MNFTHwRq#|u|mCa{+Ekac+ zHM@6eY6Y#k%5sq8c)j{FNJXd@a`Cc1sI*ftwAZmznf))0$x0`su%afj5_f64tki%{ z>_D_vt>yN&MrzduPX!+uq*rY}33nF_vx=jak}5l z9G|Q3CO{=-Tdgfe<%nO&9*hjMjwz(+-VafZ@Iqe0hZ$;-+QUjSC2Hl)eH9JrX1T+m zR~=C#(&Z(C^3MJFBqQqGs@mJiAKRH7n5iS2xtnN4nz;)OeHBNAqk6T6>Ml@Qs zM>@C*>q2Y4g1hprM!Ewcg8%@AJ1`~=00`h-0@;9ok8d7HN&pNn`2Uv=001yx8T1MS zfT96fP+_9N!mbb~oYQPDaYGJ;&!nEKT3_53!g=c!eS2m(5QD&d*O2LMHJHM#UoD(z zF;bhxZ#A5&_N9Cb#nMzM;hthKk|7;ZOyh6~5w>IV)ed+JAI}6csMKyGdnnhvN3Q|( zu)T{SAK6~Q0qRLr@>K>zR7%yto7gN(ON%K!su59*i0|RjccP~&U-c?DrGUz`RZR-y z<&TW8h=txnCUUbJVS9Z07G~=9+(k~q4~_|j1a&RvR$;k%#nMJg^|onMI!$#-rv;L> zagH5gjK8Q8XROAenW>2*MD*AnuP4Q2DTcj>HcsavCs(&%n=lAJZ}rjX+V{r=$CC|( z=VJ0!#CrcusaueT^YZDk1u{IUr`S4Px1OKU<9|TYnn;6iNr5=xH*P&q{?oOH?2T|p~X>b8+N*}9N(^S zPzy$&7a@eoMu{P?uTPlNdBG*IWY(eGI?S@=R|?pgQhG#s+7^2x>Y}U}Am;Ki<`)V) zL462?r58U^^;aAyJ5!(0U6JU!WEYa;>a>XKWu3pUwn`=Oj+%s8Lfy-)#fT#laUQei z*wjsDa8;xL>}j0rvYEXutj$_6ArQAdF?xM z^^q0Kl`&8sUZc*2aF3x&EML$q%dmBXea>`roczefayNp@wh)BJZ&|e7$A6M4q(KZW zDumVe99*O9jK#5%@M7_{;$&|qdQzHJ{Bn>08R$&BSObmzIOm2RkzDi7?7}YEvG$QM z7Sq7Q;c%n4EoTMZ$=w98>QKAx+C#G5k8Nke)PQf`5RLpk3bZW>5~>N86WqS4g1HN@ zdsIpPm^2a)C%5~H>Vct|1im(8>t2qD=K!w4AH+OQpB;1nh=y*+dM|`;I-T>dyjtKf zNDZsG1|!MeG;335yEJkOMCgp|@IuTW(Q)B&z<|JS^DpKA2pjr%q8nrUgAYKWBWIjj zj@x#kEQlO=-F$mE^67gGiGj~BUum5Rbr7f565(3Q^dz>ixs@vCFcotT`}nBdMlUoN zfXiFQh4cyySH^t75z=JG3|PNoHc^c>_9t1 zVmgb!yAJo2JXob9TvfwSO;8!2u1jH1`6w^#G?ZWIiV0~47_tWG03_B!IqLSou&OSh z=pu+nZLlYaK?J5iR!r zY56YupsGSWg88t1M03JlwZk0DdkuYPoL_DMlke zJnLw~gst*_5LK!K@CqAnHxs2q{fPz|*XLxnTs!@0M^TJewq@OpGj| zsV*GlB#DlV4Qe&FyfB&;G_E;bk!P>uHuk3{y^M4zsU<>+GG65|SC|OJBUJ^g!ND;L zDv~HZxdJ1EAKxZNLvX-R@jW%u5HkTBrU6#J#vL??mQAgR^N7D-1DLc{9*NZrTW zxDiT-f4ZghibEADF;xxA9@73GK4xQfPG1((NbbYvMC@lCFLO5cX&2i2;_^P?bGkB= zwa$?5C8ALx4qFRjBW2c_Wjc6tj+b4y%luk@fjN@pv+d_}fd?kkAwpPV!I)lRiN8ML z>;P<8mGX2;T}ldFJI`CSI_^%V@WB`elzc<$WT#YvL7O@+!N%UrwzN)`55-|fe#YaJ zcDVBLZK4V1ZJJk}4XNEfo0Zp-<4(!Qr7O*0~ql?xU{P}&>JU3^}i%R`$8S78U6bi`v z-GGy2cPM-oLAQx3#FqH!CE<~UH8&Y13t0uBt}~3tDf)8*BaMsJQYPHFkk}?LHV#|; zC9SVH$%b?n@oezuNbjZq%Fi_*WMh@khp~N&=Ti*5e*XJ?D|m^;ATF8NoPf|RLXnlm zkEJ$|`Hl$(F`G1Cg$ekhT z#Os9e{WD+8430a1+-vh;j2LRxCA5nONIArXYUEyf5+v_coVRqK6S3qV4TxYbwf!i< zv(2F9t}B&U9|>!unk^2$c>SZdkAH`XtnIXs#Cw_c32B>Eb-ovsrSuUXGsag3v%ve= zo`bpM3Ha--cVy-V3Nu>C#qZ+s1f7pi;9Vws0gycJ0`Y4bNFf$)zENz)W-I_{IyGbK zWlm2U3IBUPXK8Va>!Ta)(C!a0iuCPmL|{E91(N-&J3vmapceJGx`~ZB$q0o8>|7_N z>(FjgpH#aRP%!!V(ze6zm$~8UlDpHEZ(G?d37&4%{S{`FRh@hrFv4%vh>&r8Yqpvf z?s>wxgd>0i>1PWmmKpX__2xK^%5sA0`~PWIH6GIN)LT~+p{IT#zEdt?&1Pd-k8F9 zF^O63eyju^P2-4Dk;49;J3qsvw&@#=UgG^SIm{;Z{r~&~1iL0{@XR0Wbve?eNyNV} z>k9%;7&eVIu^$eTyxL&~6Mi%E_RGMQz|-&e0gE&Ft*Q|npX4z7kABeI`P=QF6&NPc z4{-}x=d%riTIlX3^?|>l*;=mj-kU2MY`KX_1Myx0Fla*;Ap)n`Tg$8`Y=72qA=yUj*M*%C}!srswwDY1P(hlIUIoZH4T5*_>b)*Y-q$fzgsyKuN ztIHL<5stML#=1pfc?+{vg{5`5vWI%E?oiyZ5P3wpmMBHA26Hialc}QS?HDtz8pF#1l*wR;+2& zxFVDsk$V(|Snw0_x37!L=8Rwj`p*RHAeyXX9fR=4EzaF~{8?%II=h#zeN>WIuxS#* ztH^|F5+=?`Y#R~Ed2^h)IwLG##Bwt!O4a5|q)pR4-H|WDS|m)>H`KOS^ez(?sTvAA zllxEXri-%_iuJow!M#pgVp0vO2&&!)26p1U?!>A>v)h-#2`dqh1O%C7r2$J}GIy)^ zs#mxtxshUPvfpHKEej%s;;qk9)(u@`f?Nw~Scpl%Z0NL@#`y+h;E)DxNs+!w@p8!UJ;3)(bLc5& zL5Zhp7^l|mq_+byic)b(go#j}Gp?RzzRAkyz08n_O5+~Pide*5n$`GDqDk2llBANI z{}!YoM=(L6C5s)Ju$ZcR_Z*8MmQEMjbSUFdf8C^um+MI-xeg|dz$E#>Xi-NP$B$V? zjG4fozU)TDz7DqH$2sE1oPG;~-EbV*WX++~q)gYbr&6&`*KsUFDU3T)tSFyX48mry zX=DMOPz#Lz%yDjfcGy|x`bn6<1^0`3DHa85lvozx>Af@gAsk2~Gen@z1M{nAGwP|^ zEWs;%*kT&~%w<-qZd#E;PNkYbH6`j%9yiGh*{{mS6ez%Q&s%KGTg5I}8p=!1DvNE|ro|NF{o|*bEV*IC&E5r-m zOoYmpDo2+B&z{@+zB2bK&5`rcTXcOD1s+&`G%A+?kr;(;LaS~T#tIlkg=HgW)HZi8 zyXaDoe1(sn1uo=&jdG=E07iJE6T91!H--ewL7bsDd~fcJyDrMz?^#1b%1nmKl1yL^ za#e=KnPdH#g{>#WY*5r)sCS}lDIF+J>_K&)IaGDd^FnlxD0GSBN`b0P_UiVmQ#6nD8Ldn8alh+ zvmRz1$s~O1+&p%TG(~Qy5iY#b9*Jda<{Cs+)J@ODXs{VzW^E3}C=;2N347+q8w z%p$x+n204SMi+qDYTedcmqxNE8i$h+{0G-Ju5+@2 zLP+SNx*1*cZBw#iI|nw4h1m0lA86DIkaE7mCN8N%_J;Is@0ka;l+#ZSc9;uZcnZG@9G`$K^y-2fO z<$zw*++OweUf}@#K=sc57T>Vesiba+T)2z}k)Bi^L2Ta>8WQHwUdz~Si_vbL+}_k+ zikdD`)=}6;F%rXETGk+t7mToek}Qyc+>0R@Kr&?qX?-4))%aqc$}X$&_q5zx5|i~=xZ#n;)c1XYyTwGwCF5(*Xi5FR3?>p(K> z#+R`rL>*EI3>xz}7vmU{lUXGa>K=3!5(Reh0?&BQ853?wo;*$A`!mglo}OJe7jWYd zw3a3dv-%LZ#gAFfKs*D~c_Cw5tLM`#V|JGIn8^9%J0GjY=$&Y+EeR{Ml0JSbmvxaQ ziy(-Q8_L%tdsDMmqzE%$U;IrmK6x~l8aQ*bGVaGi^JR6+3p{*HGa*aclzY6yvpQ%( zJgj@Z)X%fr?lM6ST#?m!OpUBGe;cnqX`pCZ3)+KKO_&Xu-BRjWw6^0kVz1O^Kd@kO2#nmQ zhA^vvfXX~q3Kil8N^@FIQWYh)4UR_FElahwKFH&3tZ^(seL!nrUTZ`W`Xb)?;%Ub3 z99S_PV3=!fH#f9@?|(P2A!oXRtj-WD72<@0mNgl+dg?d*H@2o;Z1a3vQhL2T@NZm@ zc6OO|CIN3*<#a+WZbdbJ`-}89nHR%lajQ%9D;n`TmiarWs{=#Q<2|%1LSM2E6^63O z02WF{^0pxUIt(oUMkMJ^B_Hc2`zE%ZU!JhoZ+T;`A#geMSCY{pk-xd*bIP7C+l+(H zNf#KEUaxUI2{iU$gson`XsGm!^V$BrE9w0V7Pn?xg}dM9P4!L68F`bS>cgioZ#{Wt zz54z6mOtl^!&nKB`Ou0o{(oX;&ZpXyZJqM&mpB zysLIDyM_caJReUC-yL`7?Y?-uY7u{=wXwZ~zx(v#>0G?Yw9Kx_$K8$(D}2%?58N=; zi0y1qPi;(#i!;LCb$;hAROG&HUE!|Zf=n@r#2#Q|m9}TvJs5r&BmtYQ$yjdd;m%Iv z%U}i`A1?O%H;%SZ-mj{>kqEbVpH~~s_Flce902T4*R3UVzdyz=3Ua8SE}M4`Fse{s zd7Ahad4~IosJqH82U09&We|P1{`|^2m#uHOUirJ zqfo3~`@R;NO3Lv62wIj3{}Hs1+a9)z0I1RU%K+6r-XYv@PU#N);0k(;`mH(VF4P=QY`h__xe(@D2`Pc!B9x(f9O@?2DnIl#G;U1qw3d)c#qXj{87e@1WWwh|3;0)O$#gRed@%d4Y)o*o|u zGoGKAUHdNXjn=$+&ihUL>WDK7j@HiodVadu6aLBn;q86)LCW)=4^Q6toK78$yw`bm z=W~7Yp-1JH>`8x0LgxdH)MWdv_8=pj!L~1R3H*O>SqP~ju<$8|a)Q{6eMt48)O19e z7_$Q#SrTWJ9WnRU$-XjpJYp{!xe~+==+iw@q}A}!4OSwz@Zt!Py%$J=c_dCJDiX@i zZiEUM=_$rYwIA$=wP{zP3Yrryakvl9mbev#HU$Jye0?2{G~k=rHDZ;6ew;n{l7IP; zn0B@2$`#C|lM0=x!bE*W`8a}TYQ8xpg*>gVl93vGBY0K?%H^Hi(dja80oAvYrWMbP&6$Gj16t zRJUL=Ria~rndTzeVID@R+XnbSO(t{W*EnwiNV@(cwkw zc)ybL8}olLU~d~F6Q=8b(>zlU493!}J|D<`ZMe7(W_jr_P~epgRH%n56fze`5Wj5M zh!BdFCw4tmvsJ6OIr;rlOM4PP;QxCG$1UUTmqbfK7yK}woff7jXqO)- zI#}}lw8I}!@gE;dv7p<6svW&(`;uhco)zFOR0Hj z0+pU1?882y#apfN?qqzs?$UInZvL$NJsv0^#+e9Zcc#bwW2JC1HV`6KQ}oOJhs1bo zqRcUz!K5>!fG)l+M6lF?_yu|T9?MkJrZ|xLst`*&6h$BGyZJYIzS*RmFGhQNp1jXD zn^ndJVua{nSMNc}w&P&2t`tjtwf`nCe=fyg&%|L4LrUIECA#C81UXYzmd+R0qE zPFQ&8XAO~*;M{gb1%R%kKrEHw_`^Ek#CvJgZo5oEbz#QB4S7^882#z367SQ`Ad_z4 zB3%np#PR0%A4s+oPqC6GC}*NnqIMBl(bIJS!lgtk@x9Jd1pcZ_sjPKX#99FTFkU?= zAdhR2qFw`Ojly55@1;+o*^&yaANI1uyy>}p%fQYgvXQE z6`cgdQ~P8HVqMXU^?jD;UEqr%k=vvl2sskdQf25deB9VS<{~sIe{`-lw+9>(25zv9 z@_%%S#Kfxf`%?*0=eHtuuInQ$%lWBNZR673aJIN`^s@FLs{2J=DM}D;e>to zZW%dFZi9V0Rk-Tm7)t!3bUzY5psu7^WC~aS5i-y!t++!#?fvTqH|%taZ-RW>;^W$_ zUga63Sp!w}xvZ+fnI{ak@Xo~{t{6c_arvDIBXRN`cOT+h#-s45rTI5ULa`dFE{o*= zB%h3y#k=*m_Dq!@wE04rzoh24H-og1zNF?~XCnaCl7RD@fp&}_SM<|;f4!iRlm)T& zv^T$+h!59n&r7o_zl$w->Z^=R1Zc zN4P@NDQyF!4p+sD%d+&$J!=1oy?0Y(v+P#kE4A`qnAmp`>|UX2vYDSxkvSs}6B~A0 zGCbKd19AC5g7hE$cY0m2SMh=|N3Km16e6Kp6wU?g;jnf4pIlB=>5fp$)G_XV>zu!7 zuiv~?j+m_r7qw=(t+)_*Y=a^^WR-r*?Y~RhkXrK6amFX)BB3{6Lo57!~9#YRD5%oB|x5M?O01~ z9X&j1)9svx?Q_$tLT8!fGhy0a+Pqa!codx@T-oZka*v>gI1PCh3_N1Z#&5yj;0tmm zDW$~XMBh1L;hpk?vlx$sPmzP>4X%!@qby?G&S`&nugp-f$xx6WTsR6tWFu-+C{!%5 zi=iE;l_=#k1N?E;3J~l0(J6o~XYV#S=)l-f|5?bC2OlV&%{3UAd) zx*P#=ts|m&TT`O|vPWQ1Bcp$BwsG{fKeqRP&2?i`|c{&M16Eg@21N zgc}sv83vzJ3c2b77^#s&4hd(f*;fu#xT$HNzR6ptM9h4KF9fNxM}44Br>zS0-lrlg z#78akzYcwTCXZgfk8Cy`ig3gFpo^Pz2CbS^vo3rbk3a%_DZTuI7gc+jt$SvChoeJ3 zrZ`7WeN}rYj8Cx;%o-H@Hdwu?^mDavzmtEkwnYS63pBzsg6j;+*zH3FjK3~6xqVr~ zA(!mr0`mSg!ZWi8upX>-G&-KzP6vosqvq@#t$Q}A9O5f&WU3q%lrzPxHKU1Ph6kog zjG;k{O{cb_5#qw>k3hJ@fwd%{+F-cPAeIL^@c_qgWzkq&-PkgB&$ik4t{3(!Nmy(U z2YexG)CkmCNfxd9(bqg&X)+oC$|jbKTj=Gy48^=Mn3xnBuL#mHsnT8_)pFY(2Ov1h zZz=D(V;&ZDR*+-!7`pRjgU(XA@7a4UHFbU{aNm_qj8x~-vWGYfMhJ^BaRc{ zvuKJk>`#Kz!im)9%o88HbW+Xm3ZOwW0EnFP^d1nH>Kpy-lD7%j4DeMi${jfbJ*{mW zMBn93cEg@>kisikri2S4#S z))ysPiC+`M8+ZWHJqveR$0vmX-a!Fex`aoJb3U_%udfY%wdh!ziKS5(8t)j`jYYPb zrq7B$3y*yMOiQ-{Iv0i<4dgM5mJ4<{nV$=qIZ^u&f**r04|UA^x+w{y@CBkNB8j)o zAEne-D5DRaO6p|z(cf@F2zc<)3$Z9wF*{&JCinuhBF3pZxSbTnY%IfyHH!pX(^=QT zd1cck99;yN^N!yPne(1jf@{gV4YRH@P4GFQY8wrp&9fZ(6`4yin6Y8k^YT&J#c}$Y zanGK4M^n3DJkbqdThQ+moR*k;rnCOcRz#4IsYG2aLU$69Ul`NqvV7tN5g-aSmy1p# z)x>0Ii_>HJAg7vUAMLmT%_Qz9AC(N*2LvM`LlU#c#LR$k;?4Hia&oG_4++>ZuwrUYBD3aKS0RZlI%G z$K#d&ywjP(kNJCI+&Gz2aoxJ-fJfcFy268ZC9>3)xN6R>-NvLb$&-wpYkiMgsLM1F zl%B!T?wM;uRwS-hoS9<~#P&`5?O_#Lry7Bd_% z-T_U;eH-d&mVj_bvI!jDIfCdTtPYT|67E)9^IHrS^Z#Nb$uO+aR8vEiS3f5bbJi1c zpBHUcZPjSyW66ifW^Hq}CUXbtpTM>tQHcuS#7euE%JGKq$nOuqR6J1g{u+u!-!@2si?$}F8R!K=R}CAgO^&tOlrv% zo(H1L@mj}FRX4n9cgm2K2FDhNOJ=wtZLlQ2h>Lly&)W>&w>dq&1m##1aC54VpcFXZ zNJS4%oZy9DJ$XPY&1*dhed?_2!eHGX@>n9AFuBApw$LzaK$3CT)*`SsDWY<|Bv&m5 zH^#bLEH|r)@}I=x@16m0JNn>8?i(!Xj~z7dFtxLqKeFo>xRFu42(D*v?pb3RIH0yV zVmRU7`c83Du8m6>b6B+&S>55-4{S>ZQlK-Fvc|WaI|0A@Ysk1(u^<(va7ny#*#ZuzI+@6?#@e|_LT`dOZ} zg@t!@)37m4-eCy4EB^(Ebn1-HjH;seI7Jc9(M9-%A~IMYMLz5IjGK!``sam>YWryT zzRG$bovaWvTX*Jc5fNujM}QJS?cpu5ve9f zCiOE~H3Omk>S~CY?jq|-XFz~;kM0++K;aWJwTh-9F&}c1TlVXjbC{ zqv~2F`T9Qk*rJ*Jd|msM$&G^J%~RR97tfjA^jFwDsevz-v1b=^CiB0_PINB4HYY5# zZ18q(E`h6-Ju9wq*krJz;_!r&x<0wVNcm)~lSiC6@6E>&Pyq=uq;&N7ne;OmYML?9AwgyiV?So6qUK&qbkcNP*flIEFt%_g>3i5(YJLv zf5<_8WSnb=@mD{@HE)Gac-UHdldxX8tt3FI_?3naSe+IC1Vo(7yWVdxZMurD#mbUDP82 z#q#8b9{F4rK0splxjBRRCn0-Gp*}79=g^qr<_8Vw{JjdF3)Od=8G()T%>5O=*Iffm zlWD=A8#l-UhDPE$5EKrAWJAr*5DF*|At!evKcFjum`R3){hYBsmP+X9%Ko`kA4PQa z-Iq5Xw7bFybPN`s@AY;PRy|Dp-J769QpSeA<#ISi29dbMulDpGgkjTnFGZDj*RF6z za^fH2P0p<8z}egJoSwfnB#z{DPP$~%CQT#3rCXJhweLToDvpIW5PWa*=w=Ub*H1(b?+57}C_j zNxtq|168Dgq1-u^Dsfe=eB1cqn+88S$hF75>3va(Z+a8boB6l3Z*!V_r?07 zfl){NkK~)BwqVnO%sijIweFA!vX6h;ea}8e%>2kdM^e1YZ-}M5vTl*%^s*$kW211# z6#J~ppaciaooNdARn{w0v!$9VOV#!!sY#W#PliaO+)gF1y<1Pg(yWrE*a#&}2ZT#h zRwhL}>69z&rgDreij*vCQ4W3bgLyNYCa%`o9EB@-u7Hy^SpuQV_M!mbM2=n}9g2ZT zo7g%N@W5wAHl!NJqZ5w#Dx=8K!^`AJq zduYE(r9XnQnbz+_q{7yBjZz}tdoF3uPG#YrC`U#7g->qT&KLyYpVIMTzr3p0kG+_R zC|sLb0{NU>o^jjK$|fo+=uuva6t3X<3?l8s>k>;zhve0zAPIM`rjEbtf-UV7J8`%s z@!GC7O(5?_;qzAh9i0lC$5KTh1o4!;HJ>mAYWg^MYjGB{5p+qFf=T*0EHzp#7>C zSragY4H@VeWpqe7RN()9)_?lR%dp9iU_Fr$_U^7!KJ?2NR(D9lEGO8xODc7T@aIFW zFA6W&dPZAxTP%f7@Y_B=(c-D$h4xpMigSggesEoA!)NcHK}oqFNYlbLDRAV8QND!V zK@Z~;9$zd?h&Dq{G!D_B5og2Oz2zaihJb}dvzLQ`KGl+QLTKzSDl!`XDlZxs9e)lK zU1$qk$V*Do5B8bX243*`(+5oVI-ysGE*f(kDwJity8jm;)*xTq`I&R}Rsr3~n$eWt z6t*GS;IYpARm(~Ao=J6y1Q~{@EDvQaL`q8qhw<)jx{1p5^UZqG{>>^>%kvyVAHsqN z)`RJEZhJmkr+`#rdpv(u(Ypn>#o99m2cnW0@Lky9R;3zPB@_|eA0b3E^SV4>V+-8A z=k4bQ$9k-i^qwBJ((CfSd^OA{yk;xdugbXXqN3@@zo>o@;KldnEGfS1_2oE?#fec@ zqzjZOL#k9&2Ru#LoB)8UgJ%JhQdfD{{i*4HuB)B14z%iX^%AYSLX5{fA_2GyLXfEg ziC!Em5wbn1%!Oe}ejt#WfGx$wK^GxYI739gCk_n6Wt6Q8=P;kbMKci~!FB61R2brg z^1Pu~r5fFfcpfOm0Lyof^PML_;xKAC400@v;O3xKGE1r%#GpYr(iyw_?3njydg<12 z{3GIZfhf1lzgVoxq3^WHUC(u8aWY!@n_2aM*iHN5*sy)OTKp3W|ReV!iEp0c(ZmSLqluJGgd%BVX*MmRJCD27<(O)bLL!ZyK(vW}*}eEK8x z(UTjgOLGI`A~h(cOUX3HeC38>N>o%u`t_Q(lMqM`ZV<8AC(8&kNBQ(JvIvj$E4zNl z)LKz1p`nGoyP1mQFz>?AbHi)>TaWJ@CSPNAeTbVJMxdgJF2b8JS2Y?KZuPjgtPj~z1Y&ff5t?AHU-825`#zgxqo!6%s+?Nzgk`25!1fP>IvnE)nb&kB@ zUxNM}U(lgYMQ0yqntf3v?9DMQ&QS=u7Y_1I-69+x>0Y_!1!|b6X}=4iw5i2t{y{y{ zYOElvLPV7G*Ls}Bx*gJ!GZl$+BEBDp4T=Z12*J?FQZJ+5H1?E(xRG-4%rDu5j|M5= z&qkI=M51`n_NDT|a*#K@#UdwVxB($BfCm^sTOl18aBw8{Ths%wtHdY4_UFs!{aiia zhvPz$016t;~7@26^m`1@FdyZ5n ztkxQvcaV660rwB^D%#1S0E<2)(~ENo0ml725IbsyV0t@9ceOMOO5}5LRtTZW#E@l7 zqO0BU9SGH$sY^}SrlJS4rf(Yu)YC*|QW}0zs7ZyB`&bZ`5Y?;2U*v7o?vV;j^pT`5 z{#tX`JKt!HrIHbzl-V3l7vG!E+x<{P8`y^=U+i=Km=%3#w(ra8J3(bJ9pFS{tCR9| zw)EYFqE8T}U@T)6R4tf(MIMJD2t0cV^X_uAb(HQgnQvGVsSBZ9GKaSDQOf-JT|yip>bH8XZw~rLCb(5??qC|qs~EAd|3bEMcSA49p3liqgK3^Su^dslXNX(*Rh`A zl|h8LXSd_htCt0P?fXKq&7AB6CwR^lWXf!C*or zCP`%(|M=5k7fj`bB9oXB?;-TE;F-kaT|O~afSBJ1`$!-6kT`?@SZPocii`v^kKn%t zrtd0$zPKd#uez#DEKU47-Ju053-{23A+(gs3;rH!D6et55B+YXx4l+0z9N469s1k( z?_ka6*)bN#tJ^*m;n-bDFc6FK!3F%!Ve)|^_n$ZQ0(%HqqCC4b5hrrl#{1{vh^W3PIMp{zl*ushf1fj<= zwpp+Ua*!Gih^@Yet8V%^5&gWGr)cX#7?jK$-v@!lUsDt-st{Sn=wHM}bn)y1sTV;1 z2hTt-zx-)L3=jfIjK)Ef;TbN@74DJ(CgDST;J>s4qCw!IF7l#d1Xz7}M034GTm;2X6va^- zget1zIU0pyv|=Mh-VPR^P!RuxJ4PNH0HQqzVhYFj-k$|R>jF1t4U-t!oWyWUv z+-7c~U0faES^$Yp6$y4?h=yWIJesDh%lQD5w1KpJsAqZ0y+ym_a_|pD*5GC#*vfWB_bFCX+Y` zQ90Q2M*t|dtjEI(H zOE0WNw|q;nWKsa^T?has4ld~r$_brZiwPhqw?q@PbgHyyYKumb2MK`*T+l47OCg0= zxO~ZAsA6vgi)&zuwkT>eX^^;F5D)2q2mqf>-AiNWi>u%Ptk{q9?1>G1Af+avYY1zs zU=>g_piYV46B5g;hU&NAD!S|{+6_}Nc?7wb3%ZD;u^@}G)Q}HqULuiey;#dyl-W97 z(y{#IvLyc!X$a<81c4+}!#Ugn7n}eNOoMyj!xw;(^I^dqFx3$zt7D960GKM7#hH~V zr3U=Li3GqY;lc?ZDy@DC!#XSqP;44lY%_&f#A4~W)~MtyZ04Q722{egI7Z3xAH2v5 zz0{De<_k3J3#y6&=DkD4IBdGgYZAPR<2fwz>B2wg!zK*mW`u@QN}1DQsX@dnFb-=7 zX5Nw&U#}p9##*a1!N3kQ0Sp8Y(b8-RA%wVm>$zy&yIg@c$U^lQgt*Xam+)%WE~+$T z1laJ+Kb!+6ERNpv4d0wd4}e4e?41(^Ca*%odQKiCxkBFTP2Ui1+(-)n<$&Z4ZW_?k z2!j98;Rpl=ElS`FPT_p62{bNe01L|muHgR7=n{_MdR; z*Sw?P!0zGftVE#hCTR`V#GV|Yu0gQwIs;rf)+1*_Dko6 z5_oC^uXu=K5W(!8Ko;Dn^J*^Vri0w)IY{w@dqpb9r{=KB8) zik!e4$iqL}Ln)|53IEUJyl&|t4)$W6J@^Bds0$UxF8h{(`?Bf-BeCH{;1u{lJp@8Q zB+vpekPs9uY9g+rQt!#OhUy|vlC*IHd1@bAZXUbwlbOH-gKnx$m{AvZ~&C^IXnNV`l4(H z`!WcRGYqS2?u76NmCy;Dt=7JB6*}Go8*>P^vnN%rOpxM6^l$+5un7y(DvL5IcW^)> zggQ5nLEL~FxPclx0TlON2$QlWC~{8Wa*#C%I)s8gq$g02v`HV1P%d*b|FQzPv7r9K z34})SH7Xkp8Lkq{-t8tZ*{ca`P9w?A9S?+0H=GGT%Rxj^Q~#I%6Ep#v(j#9@CkezL zpDrR4m?Iz3LijY>07WMO#RSvq3*ca9z>+Mfi}dsn54G?+U$w|)MnWKU4NZhxv!NX7 z^{`C!kf}8ePE)sT#3<#hK_t>PmTP={wI?%ni8{9G7B6HUgk&envONEYGY>Tzyn;Vm zgC~H?zXt3VSZNA{Mhh7pU{|38yR)lM(?iI_(_XM0;L9b+vy=5-INLUE8w71jZAD%- zLbP^OPd0o7Kya^c?ywTLyizQkszH=*&sw!L8cSm@3ql9kavOFv@$i-+L=ShjMD(={ zL-wdqH)dnfPY+ADN|`=8o^#Xn1Shrzd5{X+rc2W|0duxN?44l$BV6);HGpwTy_6or zRIH!?cgBGtc%!}C!(RHS5ixK)ph8h0#Y$nPOTm;(eTcI}wX__#hsR1F4|12}!cY|z z`=ka<*%VHhE`IcbDP*`Xi}hNNc!JB64!rnHS@MPGB^df;fK>kiR9zL-9K=Lb)I|kS zP5rnol$lTwl~D~w7F>8yt;LdCls6>mDGSq;V>nRhxPt3Ikn_TjduJ+$6?pDcPs!<+ z+b4Ps%Z%4}oR8F;-#FHU0gY#PlFoSw`~hyp0U>y!2-rd~REWClfD_p7{pN3k-+~tK z2=}^5m}fXR!*)h|Cqn$d0zZTe1cN_}Ls(cyhwr$F^EPaU`lx&I-Fg9(}Qcuph53cW0F2^TkP!Zw=WJ!r!;Z1D)pR!L|BCpeyK$bcr0 zLp)^HT{pzS57$$NxIv)Q$Txgd3$i;?mvv#6!pw$oA(wJ-1`ULQ$4eK^;`k*7*Kk?W zag{{PzrYUUqdY|XtO-PTjh71<+6j!7b@_v7p;mDPKs5wFaz%wORo8W8*I;nG$7dJR zm)3uM`C5Se$7f8+JG@#Ry*A8Re9afIr(bhDS9HaTY|)l&*^o8id^|{(4&;4p-IiKZ zwnW@~&g1=T4ZaPZ}&5yzUrTT=^wt|>-^bwzSz%}a2drx$UYaS%XW1aT3Eh#G2vQpJC*-Rlb}U;6%-cDrdT!8PR5dmo8;m#0V5) zMvX)*;!LS=qRW*xYgpUF)aTBnSA!a*Sn>ZR$C^hkcC-r9E6a^yjcR;KaqP~HU(v!H z8`mV&wpX{JrFlY!jc2&*-o2`~t;(-8xeDgF69g=3fwj(5s+e%&$9K^hHfi(ZM9WlD zRC)&_@QbKZ@%Fqcx9wM}iA#3fDm%dExpp2j4q+BTCv6Yxe!il(=H& zElq^XW$bx%>({a8)F~7w_pEJ+Ufh9&%&2ckvZuF-RDFB*@86$bqeqRLQ2YYH*Pp%z zI8pnZ>+e4RBNA|;-+-8Cyzne9$@sC46nZ`tHlG!q-|D39=1A6s$YwMkKMQ z=YTrlhiU9thKmptD-p&SX*?kXKfM2u#~x-5fsIB0OTdGk7ikP~$OnxiG9n6jTCqhX ze?(G9D5I2!Nf>R!QAZwqgi*#S!4y$D>}2dR%mS4RtcC(Al55&=<=#2$2CeXnCJ{CQqgkTh7 zJAn&wk-KF14}b#f4$o+IFvkJyX(9BSxI}ou5rJ@P-f5u(DOa@X#L#fxVp+UssH~$E z&Syo-;RkKV!V%t(h^>Ph40-supy05FOhlIpe;7p00kMcw)Y%EbzzHVDQqmQapE%rXfs;iWIZXhbVUQHbFJT@_cDPA8&}oI-5b;+_~zbIK4}Nff6$ zOK2*~jkBF6QYTXI8Bl?msD;}a*FVb{P7pp0p+8w?L+_bT2I2u7ZO}mwktl*(RI-c^ zvs!m*01a1Mlq*ql25CId33eo*2j=KQOJ8b^KqQhVAsuOSE;55%^ddUOoCn!l(o2J| zAQkL*f)4+NDUpx5BsA*)f(|?|ij~f*gCUItF#`}9_z4Xt8U+SNT+$eQVc`z#Ae;$O zFv~CA3sw64_`+Hpt-& z(;%Z4RBOp?(JGo@VllWc&?$A6AX5o;01=~MA#HylLE7^41SwrdGoR@aA3&oP&Wzc9 zU6KERD4am924k;#Q5(9+0yKu^+M!@cJK6kZR-ybPY&;>X(Dbm=!hp3It_=E52@{!g27Q{1NF-2a0f-Zo<8{ud}g}r)FqJTF5Hr)S1QJl99-hEL7j&Ur8zAEscDV?65fmNZ)*%2L5YiL= zuu=)8021Q>0tP?s*U{dUiZ@H5W)wHDKq}Jhm=q%^Z8S`a;^Zhx)&w@awb^b3 zF^xJSS&h+d##Ty|ivSoXX%^#8eewfqm;x5PI7nAvL6UKbvDs#%;7UzEK|5FE1V#A7 zk0m1TJEzFtZE%Uen~6bZP}2k=$ai4W>1jrUTH4}2_Z_I>fT(!J#L|eO_CxaMD@ouz%pigJJj-+ zH>2M8#*ac&QL9sFT? zxb;gMe#lGP`@*u`(6Dzt<4`cX=a%CZPvBfebehXVT3y=RNx>be(GX6Ur0XX5y7AxG z{huUcAq`P{0B8UT)Y+pOt2hX(UeOMJq+=ORjSdoy!5`c~8H6A-(jfrPp?sWz2*@GQ z%!z{T%Y%r*ymmqR!Xo;bVSG42mmop|=%8Nai3fU1w|v3_>qRF*hIs!h;aecEnXV7@ zau4VzYqR{y_b_V0$_cgd%hOg)qJGYWbnUTtZugYWomfrMR2{ z5C<1*`A`nlBu7~~Lb5C(0O&;)giLc>EicT>jLbxz(4;wp;w*-PF!my=M8kYIE+bBZ zDl}jY7KU7Q!ePKr3jqf?2*)Fi=-y7`4pYXo9PETj&Y!A-2_b6-c@J71G3RCov}R`Z zo(Tsb(Q71841)&v=5P{~h+S^&)C#IxUN5goaSku%vq}-hXmCvQP}tUx2f0u}R_Oz4 z0aH!|BCZ4?7Q{RTC#(Fx9Y_HNoFf4>*Au0dhZHGA2{U zPf=>@SAGB*$c-mqgjcAfb0i{aHfA&~QFJasqevn|oU$pBGIts#R%WGEP@*d(axAbi zAwz^LnX)y&l2&XbEtRq?;UlDQAxh{n0HpFCs8S--(ii{5OH%GKJGOF3{t{3Evn~2E zA_h}Uez8qjAbkG89hjk%Oc4!{;suUC9=<^i)ud|@kta033ACUSMq@39Ca655_oxuV zOkk;QA~QD=Eg=G5^o3tu1T!_0GZmvlvJy;iGd9(OHut4pt^@zb!5-`fGhkCU0fd0q ztVUo*c0zMBOY=2zQzIJFBcd}sJku}tGChnFIXy)%T5D>kqCAsIJ7Y&VuT!ZA#!a#_ zO=wFQw15omBNBLW=L%;dN`ML0%1ye{6p@3@-XT3>(G-P>5KV0g$%V*Xpg*k-BCF;- zX@Wof^EwhVO&;_@+ru;0k|B}c7W9A(Y@vV(1492dB&8g1MpQIGXo5fubVL6W4!?6I zT9i8})H_`iJVSIuOH_8+A_fQnb+&DkXX(*e) zcevpa62yHt27VL@e8h)*NaG@4^p{K^O{)iZt~5*EGyn*7e9Gq_Jb(~bgdhw8e|~9y zp0XuOXZ}_PBPP{QUjR@yW*o@WOr0rIQ)hJo09A3vyvkHB4YWO=-&>p;02lBE&nSxQ*^iluuG*&f+V`eo4((Xf7q9S_09Z4(bw?qh ztz4l4=cj%=g;Qz5Q&IIF;3qWf0d?fn7l(&rj3)r#wPLq50J`;C_b>~v!4OiRNGU>O zN!Da1!eAq!SYh>FA>v^L)m?{iCiGNcxk6aY2mjQEeJCU;l>t3;_8^e8OTScFbCp<6 z=T)DxEvEE)f`K|p;E^I}lD_JW+<_O|fDMrC1zOYuc=H81zzL>+G5$mAYTuTX^ez*rjhgXWE-8~Xsgo3qU(eJh(sqvOsBYcX zN(WbyI7wsipdSFQAaYEtYzdb*a1XO+i@Ip7BDZi&$!}9hY{}MalSRq2h>N_abWy2| z+RO;3vvVESYICe|FE=YYPc}z)aXg~!@iOr1D+Rm$yaX!h+*BA zC(^fRH5PI2FML(kFaItMB%xlI;so;7FI(xAVhIH__m*<01f=(LLjxTqAr3TQVeANx z^oWm$g_R6}Z_Jk`y7znALI?j=0GGVWgi#nd4w!X8*NaZTf0ws!V^@`K_ie`lZxy&+ zf43B9;*;o(lxS23kN_Hpfoyu1c=5=1xi;Bu_^X2Vg=f?%rZffyiD7iA7ihw#k^tNS z;4DL6jP5K$`>tOVSENWPr*^8R0F9-H>x}~;i`n?6enSXMK=U*LyguwA5;Qsz6>ZTN zr%WJ=x44a|B0pf841%fxD5O?{qAUtjgvumZ6WY+`Hxie?DvD}sB0xccKzEdo zW4MhI8L1d~HyX}55|ooIDk}NxV#4-C3zK=#xI*!ArwTdH0w6$XA_yd*8qVPsxZnhE zU>a=aA3WwD@Z}%$VH5x4l9tW5jspOX&vz$ISY{&Ec|wx`wm?r{11f=+1Av|V(y5?IsvKW@&zbJWjA7?lN6sDwvz|6Fh6;q#o3Sp00wrT31C17^7*NvO3oxAL5a$! zkP2Ye;R?J#7ML>t64az**+V}1r(8Knoi-Sr>mSbHXqyYVp6ihMBgxM4YiHSk_}5^T z%O#51sF50up)3cS3%U+z?VQ!z%1Dp9g}ST@yR_?YofVJ|Rwt%fs}V`8uPeL$$UhF^ zEC+cP3?jV(0aX8LqP1Qtwlu=6$@;4?Stc}qf1~3B_!_o2)0g+xjMw_8nM|&~7@XeF zIRG235!ggxKnz@TZ0+-z zd9tvl`7ga1NBu}6h`>KiAPdyDw6~hKVL`hBAh<);{+dxA*o)d(;ns#8K z8=lE44Jh!g)k~nCltRWW0C=ljyPHBlAcG>pwnG881p}kkd4=8cxA$7J{aU)8dP1Mn zxDn|D=0G0);T}qXX#cXi!KS+2TDxk1yOLC5{lOrfRlx(hvp?IH{5!Mzx~6AhKBvGB z>LIVq8QgHWWT%ZT&ax2u}lbH3Z|}F#!b8cC~e9OA*?X(NYjwfu3>CNJ-n;^#|bd1M*tXYKI6Nnt{e4Hqb ze3Nr*$9fFmQ2R+=bib>t#B*HAZ8|lrBglum$U)-Fx%|hvbwETQYAM2}Au z%fx#pB&HhHr!_<*%)d{WC$!#_)-KS?lz99nHXI;zD zyvzSzow@_m&}SSXZom!PKn>SMT9MSAzg5R51Dx%xNT)X-lBL18@q&>%L zV$0K9FyQM1C@40)_ze@8BhHe61FfmqtI@E=+koQUAqJ)*LfZ&FczC_Xkw^W&W!x@; z;TNsZbVA9&%?u}d*u5I3U!adoK;GzW^K_3p8a&w`p2tmn-5eew{@u}3rsR!-v0J{R zq{cXGg5yEE;0{hO%DdNyNfTPw?c(k!0_-IM+3%UY-9GDq9(h@w(Hu=8{yriS9wJ^k zGV;C?eyZoa8r_lrG(mTn;(0ss8nwfmM*%SL6k_*BE}Sd`mu^bh+NH#3mZNRPy?E)k0(l=Ou15r zMvfmv*ecfM1kW8#u(-vdMGO!PdlI>klgXW{6FvJ@75O4XrFS}BfPiJRZ%>>$c?S3a zP88pdHiG#Zmqejglqy*Yeg`DL;lu$dU(W0ivgDp8!t^tB6{OXXg6S316pxfm)sq7V z*7sCWyI3B7X5-h#;|YNT4W9TMdURJa@t$VN{d{nA#4qUD#jAHRa$aKi=gQ&2+{1;i6sr79BEHCtGJkFC{Zp0#0y!ul4W@|rnzGPdh+S#3oq;d zM>FyONeT>DzCZ*y;{btWMt4$$0vvMsQ_d|4_ z9kKRgl(eGRC4rS*s!gUDX+VcN#HHv_t9|~dt3Rl!no$grcys?xG9XeFEUqtDxm8cZ zs_84VA4%F*k8;{0%_X6!YpbwAd{&s7>N4x>MC zFu(%$MT4O65L#=vQ=(ubj@WSG(Z2!nBv?9fo}I9PMIRmU$fTNTEw9RHEJUp@54&iwDVBV4%B=3`>YorP z@k%@Xq(dllCxk#rK6N+>_OuCU%dNLlC47PgP+!|Cw7uDB0zw{1>KI=TiJ;3=%BdyR z;F6x8mPezmD`}{HLGJj#fCoMx=74W*`A4DRnGw67b1wfX;XRq&QAL%^mibf(?-#}BPJg{I-TYGY zyXh%UA%80#wCdLv>#?hM35?(E1lYXiMK5y$BA@n>houvUAQ#5?pb1Zi!W8ypa*`um z3lnucJYi`ESRlhFzJVGQqR@pp?4b`$cmg))K@D;U1wR7u!z3zki393EWSFQuCqfL0 zQtZjXK0-qq;t+~lJYf~Ph{7v^QGh}uq7jieMil=pCvpBuqZ`%5!qcHnR51MDolfwB zY1m^KTzF&a>gdNn`XvQ^@P<6>!Hh$IPmqlCVF~a+k3KT;k&y%=B_-(*F)AdFd&FcT zEeT0ae)1!leB*NpdB{XAa+IKBr7N{JM>?i3j;kDF=wA6r$kh^;yzHefJNL_A3iF4$ z9HudkiOggwbD7L+rZb-j&1gzO#R84$($#_OJw7u*5F=;!SX_ z@Rn&Lr#XiSNO8&&nsM}FJm-1LdMXnIbcjbRdUFCL*rFZqI)nw+dCyVaa|CM;<$z@P z5fwCr6#Ih~KmF-FeRffz{h;XIcnB>Sj!yrhDI%#LLApbc+S8;-OsE?Z1yRvW4y7`U zr3nE^Q)IfdrnlTFBU$=Ml`3Zm&eEL=bP*0gj3E&YR%zd(!#nju^E{`FsN}*sZwRKnpNmi zMH@qqzCZ@4SVs<_3i7oa~hgzQC3f4f$8-`#$uEHb^Ml8f+R@>SfHkOW@ zYc6L0SlZ0u^`@4aZb++o-RQRTxrYCR;a(}b)8$h2vD%HSWXG#mnp*ar+)bl+rAxXK zQ~|dq2-OEtQM7;f!zd<@0V9Ut4J0Y#MW&J~ClcZVVYEXWv^YjNRuMRr_V>R5wudoR zX#yA6f*b)5hbj=#B(eoK9kT$!ee?T|ap<4}cd*o0%X(DtH3<&^gVhO$ppHnmNRFJ7 zFMVC7%P+ZOM|#Z?9>t-^k3uvA=}E!~T#yTAw6#e$&M~0A3(>?%R;bu*rFlumWi6*S z%VZX_U?Uvf#hw?i&TTWA!7SnG;*`8{c43!a$Xqy&RLotDbD7^f-7u>+bbg*GcGnDD zI~V%UFkN(`6`j*WZ_Z9C=!E}@uY3X{Tmc-ipn?>dfTbEd10`x$Lt0!?oj6=aH$70t zB>d11Pxt{JX{c{iMNMi`V^{@iP{SA$;f_)SjtJg>&|MnQBu#VL(@h9WC2T_m+>jyw zC_uzKo^S#djA9!=u=RdwqUDTOx-fe_!3{J)Yg>!71Aj#R=73{-<3;v3!xOF75dx!fR5pQB@pLF1Gzm+dbw;5)&Ob_c3CJb(FiFM29fot|TjD?~S-1x&LW$r=rl)w1M`vXQbEe06*(Gt2wtE}sc@lV8V^)Ef zCxVG*dkolehF5W@_hqh!dRqowbL4UHhk-2UfG!7dC^u6wr*n7$d|7fRphJAZ)+FUe zbY#*8&WCijb|of70+dh>p0FSpVFSp}A~qF%&{2eL6963mNl)+xLdRS%(lT1cgEE9U z_l0&MwH(3V3=Z=LGLm;jR)xN20vdn_@Ze;CLx%H0cw=^fH`sF4rGu4MXP0++FW6@2 zb%F<{fgadityg+9=!h^_M_9Ib0Y`!>hj=`dffeUmi|1JE6oZiHX5@8OB=>nUn2II{ zX_Ux;ifI1=mG&us0ysaYbck{%J%=Sgr-wwxIb^bgp=bg@$bdaDLe79Z_a$_iLoTEw zGnFPI@?j0801Lff6ImhxurL*dMroE-C{=Y|+t4^CPz0aQ1|h^`V#s8l@>S=Uj#$!; zRRd)KFnA`gjf(SnuE$=R7kdv^aHN-tn75Dd|jW(j#-82E1o`H`bIaq@*6GK7o8*n}a3 zYSw`rz&Iw)msrLaLMfsG0iYm6K@|>SY^BpA%+Vju5dgpu2f`5xoMRj_`DQpqWLhSZ zeQ^IL-f^3#Sg}{IatYw3)qi1sF9>qauaBQ zvWSqFNO5{ugN68#3TTKMSePn#awT|)jLC5zXnPc>asqjKlgNq;S(kXJnI)%}r*nS0 zz-{#gzzpt!#P01IW$r!{vZxZ0yxsiId2tLWK~_q34pM;k*(O0 z82M!^S)PMNfqaE~0m+##DTyX&nI2h+Hf3-Wi8^;Ca^W_EHCUgS`FeJFpQ{Iuo9X|G zx0iZwL{(HlRI(61jR#2Y^HlqjW`rm~HdZ_YBz}bwL9w$5wio~!Fb8%8U98cd(Un@npG88k%TCElSgv2LtWQ3kcgOFh*e(g8Kk1ah~tG`jOT(4_kt%W zpZnN=E%c%qNLmkNq!3tmAPJ?gcao?mnCs-FBPUasnV*_9q`;E`%~A^h^&rQ`$&=%6K*Ukngojg4#uf`a?MwNa=J?v1m=2 zghzVBM?FDKICDad3X^4eLYI0@_@t13+Cz%!L!{b)Njg~7WU4^Qsf)^}enkH`;Uua8 zbU7&$t1&c26*a3rx~sh!NH*%L^Mt6uO030dtjCJ1$*Qc&%B;=mtj`Lq(fUfO8mxw@ zPb$C%AR}GFgss{tt;b4Es_>~ylmS~X3*Y)i8gK`MGEC_ttD5JS*7XC>pp4_{MhxXp znB`eSlYl$(dQ06@IHjStv6-1Xf~2R-YOKumD<)F}an?8j7p8NsG1t(6p>%v#%svUZo;P zrBqC%E-Ru8T3~-!H5nG!T(wgIOwcz>OBN-HJuu?3PuiIv*KhvwsyFB>p+ge1`$4F6 zT(eudX&bO`tGiaGLeBvSyQ@P%unXBxNx-ALZUl5K!${pbFVaiB#ap|f%Ol{pxVL+_ zBn!8+Wn1(GEivFDIuSqednDx8F6gVh#@iF0J4)uuO0gAMw|oCiy$2ChICIqI1;fw| zL0l*EH zU?(S*Un-_$8b%J*nKFR#yte4U6&AuZGQtm>5jKp&;n*X3WH$j2W3@tKT<}*hpa^_& z4R4cS8&of>|yNd$jj1VhZO(0N~{a&Q-A>z#wn%)f?*Fw zvdG5Lod<@LBy$CDt1^o`9n;CSX)E1q5k3FWZ>tbtu+&Zc)C!@}%+`fQ;I?#2 zFgU%@Glv2|9UR{hb&YX78bHdWg9Ok33`|i;9t~@d+`gC*qLf##9jml28rja0^^;0!f2n`0xcMa0Ent0*YP8 z%CY3)XX4%GLCFK%%&Y-_KxXchCdf{Ngxo!Crtweo&ikb(j-=jDF<_0RubG1Ta7)Yf|c7Cx##KyErj*Rc8~oeg)nj z3!R%0zuR+d_0Y~PBggLK$+LE3$rt`RAS+@m|8NeRAQn7u!`5OgwB|%a7~u{5&{!hZ zN01l-9Ilnvl>DKu`pSmof81k+dsyhXKP!DKCe4R!G&mRRs@b6d> z@aWs(nSN!Zc_+>P{LnA`A)bWnHy~PA@wXV|2&n>;u+&ztA+@r@oTL4r!u{PJ{ZAN$ z3t|*Qm=F)$g&}HoAOr%L^nnXF1hOTz> zxTNf1u1^lDJj?|;L6YfFgh3cpOE>FDR~Ro~=;#{Y$UT2?J zrGf8;$(QD+UurT^QPY7_XHTC(_PzrWz%O9LiW$E=LD(?onoXDH4KTTK=fWvbe=g1U zG~qe{I^d!AL|~GaF=v{#{P#uBk+6ARJ~6WRhg9Pl3r4tN)VAd>okO3w`X)n%5G9Tl zjZpW?oI88|M0o$hjT<#jQu0P!r{cwo8(-K!_RH>GbfNT-YWp6eMvi)LN2UNo2*w!4 z#N+9wuCi;1xdL+mCc4}}f^a4mg8Aiy5Jve1h!0>u%bFOBmu7&Bu`bEG0Zap}SUuDo%k7&>7mh-0F`jY#meDo#_}5JG{CSNyrg zlT>k-<{$rBX~pM@9eSGL4}8RckvT;NoeZTaAv~edOJ72cI?l4l#}0KONvTmGiyO{2 zA)SR*T4hsV_9ax23RW-I0`Q=dO#@gqI^I<1eC=8%rfW0;Nr9$mQXxw_8e2Ln&0??tU7jA0z-6W($s7mAp0&6lA@aTuX6FlyX zqZ*z;FhZj!9(1OL7oN%8aX>^0(F-%QF905_DdmI`(72}uA-4INN0nUy#cA;+i_ zmbix(H`u5X&J(hT2NgLU>1azI7b^Pbq?dkyNF%WdB*_3MGeOUUPCzppJyep@AQb|r zXA=J`0K6`YeoP5Fr7cAw+GwPeW?F?x{tjHe63lRi7d^B#3L(g|;ir$X_ZIRDR@BfU z4{v%20CLF%7xHdB!e%JjuMabu4@;2*cXar zZu#cH`T-nK%nTtm2!=n#ft$sl&2c*L*G&j$z*NNxfCBuU^u#uj-yLo*^oX4%;z0^E zP4Fdq(FfEd(T-TWq;sF+4D4j*FQSa(g^x(!@R*jQN|Z1TCrlyh04KN;9Dy9y z;GqUxg91OO!xB0$FYJ`~D3xt<8rroY|R@|LnPEZ9F?yPs1^4WSI@uG%! zK^Ww?2RAmcFG5t~9@>}&ibimUYWTw*+Ta8bN+bhKkRu+-5W)-Wl@NoVK$4WSWB}NP z5Vuiqgsz#}UtoCxBpf3E?qFp~_Er!YfT9`!utzsmaT62JAQY-x28a>|C0S0gl9voX zGDF$SXTD$uaA<}+Hc1K?Amjx^AVIfDZ0RA*Q-H&3IJvBek#=uf9Mk zT$(DMAgv~IGRB+}$WJ}SawseDr$51Hl%q2#!5r{06{Peu9t@4=EVtPj%B<`rLM3WX zfBHOWQd4IqsHqT807^E6E9lG|&Tt zpr<{gaS5{Ow4>gcRjsxm1eo-dR(}mZSFdBxdft;WRaH$|iy6_Qf)T9#5G(xj3C@h2 zKnbsChdggao2G-aFSpGy^A3gU;N#X871Q=ZA&IGy_!teR^wBPaWNxHi9s5((Hv*12Y zy5uczg9FoF^ez}v1V+sB{`=eg^4BgEh7f==OWfil7bzUxC1swcUiN|ox)P3VhLw`v zq!2j0*Fo@2NQ`0fM))QxzORdwVww{Nxmp2!iHM7A;jZYw2G797QHac7`Ypj7vap$h zrz<+nsAzLm=5m+4{Lbpa)4Sol&@lzG11yliLlOLPbD`8`ILH54&U2=7oqGVlI%De2 zM71-X@0HYXpq=LgfU$1=EZ=v0SV*0ZKHol8KQSJ(R1^E33Wb6sMx3Tf4?{&lSt z?Q3I$5zwz34PYp{h$2!J}hHtmceQtE8TixSc zce~v!=dMsY&Ix{Zz3qK(eCJ!=`{sAQ{rzu%2VCF-CwRdPesF{*T;U4`bqTg$33&&` z+K2cwoVT46heMpAJsxUtle^p>$9KrhZE|&sxz7J1SVOTPv~3mS5oIqg2ny&B zk61*!%_fie$ynUri>sVD$D9+&!#r|S3*Et3rnS=Rd~uM!SLtGQ`j|WJ?Q&QB-cUb! z){)L_qZ?c0L?1fS=WO;l_qOccCcz*05eb?vM9L|oU=4Ipn{1;tllQ=e#=9PXB7lM% z_TUC5j?(tG?|kHF4~&@^zxJzN`SE2ov*Nk_&aZ1b^CBlYe6bwzus7c8o5%X(yWZWY zUmo+QCw>&ajH*5^6Au^%<)w_kpai$CeI z_x;$Nj`CE$AJpgH|F)qd|AdeMMi>Tf5G7-+08KR*NvL$tx z1V88pPw)eLNP`@aLld|}NYg~30RweF1vNk&VCV=HSOYZ}14VF$QdkHg@CG*Op+;B; zBe;Uckb)*a3Nr8@QZRuQnTNArf#=w*PMefTq%NF63Fp!PJRmeBB)Suz5BqRNNPq_* z&;dU*fg>;nX)zMJnUqCL#B^-KLzKt$o5wv&ze(i6YwSZjOg}GlGeqP+Gz2qz1Virw zGc+u^NR+*MtjB|l$bghYp-a7gJi~rO$nqOVhP20r^hb%rKQar*>Dm%>EJ8>D5q_~H zt00o9;0i$$0M$S$zX_9E2u9h3URa0@_yb7)dM`9FisfO4C%BmMGJ%)~uXQ*_4;Y1k zsG1+#5$(Ab=1e|pCi1Hp(MH>fI`56$|W2`GYmh>8#7Xi zKSS(Dj10Rl%tt?5$dDwxhJ?#5`^bLGNVts2x1>b-`^dcH!nhp0kW5H7JUzGkyM1)a zqH9a;gGj?XvT#gi~t~lmfQ-hfJr$FfGwdypdmrI z@P|{RDoMzOcE|>DU>RiS1$5k-qBMvUs1DD-Eu&ZhBuNxR05r1m87OH1SZK1C!n-ak z0$Y#=f4GML7zZKnM_4KXSonss5SQiuG^KbfLxKFuH^WT2ghV*ZzV+ltzzod0Y(4qh z%#EZ+kW9zETuA@?Pw!mIh~&P41WbO6PpykT$F$3d`2$-5hw`{$oL5opoDtx1Us6K`UuN{2+omf580p%+ZYI> zC<%9JL=~A3>UxXW^ar}2(GnpVBZvYYa2ZDwL>~wh9Jvoj_)$hgPsrrQ&{4|>ZOu1y zP>-}w!wgfjyhwelKLp)RM(oeWyhgh;NRWh6ReVbY9m6&4Oxr6pFm2C&DGo7Fs*w2=Ik6X{a~F82LN;*| zyLiIQ$;Z{{gq%5wT;Yjb=@l4R2xO5%70`qsOvPid6p^5T@Ql?{Gu7v@(>P2|G^Iyk zRZK$F&(ga?1C>(>y-fa`J^f5jg)B%0Mb;;s(`Q{!&J56zoX^!GP-o>+6IIZJG*@e_ zR>!PJ#LTX?ti%4S#4)`{Ek!z$v&al38z||-IH)21fK(H3n~Xfs4K*9a0VFz#oHN=8 zt6ZZt5}SqCqCpWOGMbYWDiZkeL2hcqL;9RPoK7w`%trAjNFdod~hqq5d3(dvq~yV%m6&6MwTzUB(w_XW2%h2QZ*Py$`gFZ)(|y}C1v zv#v$8Vy!cQr(H4Kpr%PjM|;3Z~%Hqu&X=U=GG$ z*ds_#V>I+hvo+fdKX3<(!jwyMhfIq)PD_ClUSX1};d9f!v+Lh4<<`?pI2>*?5%w~Y z)Vv@T;vzmY5c;pye*p<)1TVghS>{ z_Oczpg=Bl?M}g&F6Xsne7t+{XXn^J}!{rj|j(wA62w~=#@Md%)VSHtV1`D> zvllo5dH9fuGh#YcJCu_xC(wdUNU8$9SH|-+Ha+6risyOOnV!*wa1ewsNQ752XnJli zfwnS;Mrfp%Xiy8h!7IFaCIPxC!4q^Td5-8q6T;Z2HIvq7ex?ir8)<|_=jy0}A@nky zfoTW-OTodbK};D-#sCYR24^9W>65-Kmj(;mOE#OXw+vAREg*wQi-czOC_iEX+fu*& z#b=|#3+8Q1X}!3fMAB!KWkuq%sjg}W`Hn#;Yb*O|E>r8kU~5o=KIx;rN(+n<2!rI) z0zznqAabb^plVP%m$RZZ!G<5a=nl8W=4zga!lvm(LaN2SX(YIX%&~=AWsVrgq~?i^ z%nqe*d5OmMYrr0BbxZ1ZYou^QhFUDDVd%gQObB^u22moWPp%g4Xsxg3thjC_rw&D|JbQ%ZZ4j*b1M z6@TCe|LzX)9&SuhZ`aC{joK^0IPjxLZ+lqp7ZBxUfNtpy!4W)fwpyB~J_;-B5y2>! z6OhIzjPPK9WzoQJ4Ug;w!vTpQ0u>$!L;{@zXYNe7gADhF=&lSJCj}c%B=3+Iz4(es zWpRzEgmjQ??cN!Q!EhSXxb{{J_#SW=WS~m0hfw-)y+}Zc4;^8ojZ50j#%UZBc6hH)e_yU5Zwi7^jd2bC4`t^_JvJ)@^(n5$TZ&cp@%K(6<_J?Qr z7nu2T;XTfP`Jv7fBtLmnPk3^nSdKc1mydas*MnvJ#BM+FX)FPDrvi7!k4cCFO?bN) znxTHRp$#&GIEeU`KY<(2gxZ7<9YC0uSo`NNn592yarXprH+Q0scYnY4VfUk(Uu}@D z1P*$7A)k0;=S3m;#bA_!cODibfCgen6?Cuo%87?t$QnbDc3wxXFrasH$8-&OffyJ? z=d($i)X7KbX<7~eRjmxRz9G`k(332P8L&wq$;lUS$&e;w=`Gh64ejUt<>eXl zstK}c{@#BP>Aw@>5}0IxDf}5ALveqH=L^6R{tRpVAdm!rsCMoamlHUq{Y#h--x zhz<{<*d;l!L|vp8HK4_+h{Kwwi%VZLeK8}LGaxTKbb_lE$jX)#UFiC!Z_|krGA+Ic zQ4{UM%$sL^E{X?T8_prYI;6ylL{As0E;sS zn!s9uA0=qQLo!H`O*`3;^C5r&YV}Zo&>UFef*Gc90*WeT6TpY_iQ*7`{|PnR5*3n| zU}X`-!^|Sk1+e3aE$YP=NHBV}BrHun2@`8q4p`BR@TCEykokpVnRpjrS&^9m4EX|T z9l9tbi!Q$O!VYCjlTR8uU=e7bT8P2eLq)*y%@b<>UWP<7yfAVMI)yMQQ=4CP^#YwR ztqIazq`dSH5#We&LKgYd5x|%YrlCTnn+7mysi&rD(Wjx}6egRx>h=y}Cxn5jt=53* z5Ng2sDcCMXBKgpl7NNu#e$E7t1309F6l@yN1^_}X)IQXK6J&%kS*0?a1)5bC-TH!- zTtXSZrNg%N(5n9IRM{Nz{But!#%79a8ZX#MWeqKvrkg$f3cK1*6Oqe$*4lMY2#)G8Iv%M4x?eBF0|;6c?2OuQR$*q6yZLY%)?@y{GYd!rXP=owu`AUR1SmTI(!C*$(-~>Bceh z`uN$y&FSF_UV|<6)G!6XNHyuqvgipgyZ}R3jQVg)G;vA((Fds*W0DM`jFX#8il6kV z=97OUFiag%BSR|G+{F0MLU0Q!@x~*6m9|^SY&GRpb1QsN(_`xE_QI;ow%fn0hIsdk zZCZA=s5bARnySq{PQo#zdr|%C4;<3qcH<4W_2vIfd0czQ4cy=cH9P@e-a}R$n)bA) zeU2r+klaYnQ7%%b#({kMTo2sQ8W9Bl!x+^H+SX838M^V$aKN(&d46#M&3pq&(KA71 zOu&+c#R&z7C`;r3)B!}mBQ7#Bkc><;m>JfPXK~9xg^swMqMWN+zv~H&l4g;mP3s{> z(*O|3M>Cr_(LSn~)(K3oDe9O6W+wQ7HB12um%zfr0$i#q zVfPDwHDvD`|IN{I(PX%|yxG}8~^_Ya`Ttve#^urW9 zb;wV}sMMi0)m2;VY5+=L26ym64>cW*eO#3de)IvWMU>+S&cF&aXdw^YAc|VK+P9)e zm8y@hs;P83*1rBVu!E)l7f2er)1ID{siko%T;*!hiWJ8m+puNua(dYpa5k=Um6Pv2 zRM@V*Km@iZMjNd9kR3QdP*@-o5P-FhMoa+xxNBYP1`n)a?I|xE+tZQIgQ}W{2dKD-UQFqO zrIKhzEG%MIyj~@-RTZyy%@7ZkW`e$4@!qC1u!lsrK@OR4hbz=XU6`Q231b+=I;KF} z0AzT>9sY0tu3M_B_!Y2%B}#iwi`ZODwW{B3>Sacf-#F~Izpw4v364OHYd|=<B|N3S+BG#nu9k8<4N36{n*oLL=`M@M?nMX@uU^^AgEn+Mp!KHT3z!t6X##MFuY z%By3ojalP_4({l0r*w2@Z0o7pvW9_lRb6aOhX+@Fsl+J%K}~syo7@3-K@dRUo^%A8 zwAMbio~0ZA9u4ICA|;puKDZ#cg~#J>+8*;T+1b`PL|owvcQ{Bs9ct*9yV*iOft$mO z4Nh2O;SGn$NYVl6bEv{l`@Xfyu7YeyGjE-nl9QXo%n8AX_b`Njv^}J837I2&-0VV# zLbjn2Q8ryAD(84np6zRCU*+1$KEcf=J#kdmxDqb+2h0uSZcTIAT}pUGJN%IjQAg{A zln~b0jU59?c;g<)fU=(vJ?0Z=0NTF}HkLyY6PScbj!<-$2+)=|!I>scm4S&BWwdWj z#Z8RbXvQgkmuK41?ec_+Ge1TsC`IYfwc>GAbSTg_e(2u_4uK)bqPhK@I zNSOD>>8seqe)ingy?7=M{mJhxCa|x_{Iw_%(&8TZ!)L$t*Ay-E1K{H2U*yG7W$1tn zJOeHuUiOt<^En_yF_HsHpy`pG%_!dJ)n4jZ-~_G-ppd~Rd;|Glpk8rc37X&uqF@TD zUG0HFk)Ko2G1 z?G9V8_RWIYNb5SF7OiqR4TKr}K6WQ2qNAdEpG zq@z7XBnrO35y(Rf(H;37FbWK0N>I1-`>kiZ}CLnN4_{4K$_)euUm zBufV4L)3sSunk(&f+YqnqE`l@eS{HKJxfqh;8waIYB9mIJ*87_4ALp(WV-V~@HlU##5lku^rcBx;U>2TLeq~vLgl7KbOPXMxf#nmH zBWNOIKiVZ%LghY=qi1HNYbv5><{)Z*Aq`c5V(84j4!z%1mrMZADxQ95z+zV*H9W;Y*a>LPOnJ!QRf_O*6kw-#k1w9() zF(4-=AV>U=*)X(2Im|*5*hh6sCk^32!)YE7{L*xe2yW_Ttxcbxxx;;8r3u`QGt5Czriq6p zlWbn4RkkLLil*WvBWKEJ(&%VHVik{u=mv5okalH}5-Dl6W@-*;klHAXf+#^&+R~h6 zio&K9L1m3DsZ>JYkxFTkN@12pDV1tzSZ0&_wB89A!4;5@6f}YVeN0qIslgi5mRlWI z93&XF^nf}{f*6$hHYsD8C%onyeDx^y4JT4)c3~8B~1VNIjspg1{Y5*RDV2dJ$ zj6CaqXr)0msgRQEm~vsb#%3HLX_AKKk22}Fdg+WBDU_~jX>#ei!fU?ftEjFf6xJnS z-m8s1>A3D_z@jQWVriJ_tCMafegH$~(NGzQNQoH8dd%bhIq3&n@XW>*nw0JpfLIbq z2!RbbhFtv8UE~D~p@v!#hGC>rT%14}TpL;`XaHwosQ&Y=RgFh|rL=kiueW z3lTtwg!~NJHtxf)mR4+v2^7f-97!M~NSmbYwTO}bwwOe#@rj=Rif-{%xQt7=P)1Sg zOTUJe|B?Az(u2!@b}j-<$xe8|D{f@vg3>Gq0A2v6?r?p6KJQ|ajF?$Pp^3yrn_ zEZ~Cky6EVoFAavP=7R3s(ksD&XylHmxU%aM)+_(ktyo&A-5zOd3NGG$Y~eELz@{y~ zhVJB6uK)V&{I6wm=B5C(?M=#F~@h(GW{A+Q`I)Lg;#EePvc z2*c*TYN?p^aZ=)F=7K5Y{x9Dma^TWy9`kQ%GO_?aE|g;CxgN5PmZjt-?7Qyo=3Xi1 z8Zg5eVwoC-&3sAbRWWsxStkV1pdl67e#Eqx$fnfr8nw|Ib*&_^tTUuCT%Z6ac@ii& z%gvHf*P1{*Q7x|YawmO~fnE&?_Zc;eUJa2ERDB3Sq0&e2S%M7IHkDW{f79rR04#v( zzcS5!Xbc8glNhN|-0XnKOo2{_0V=JGIBy>$JMR8&FemS^=B}naf20Nbu_TY)mo{?W zlCvK_vi>GCB5N=~`?Ek7Zn_$DL{IepxgzjE)9pYzbO9?c!)7v)dhXt;;|Wv&URj_C ze-Tg_n5JS7D|c*HCKf;yZG3zjuj0F*kYwX0NhxP(IZUJO-9S{IyYgRG;JpNe#1X=)~_Di5-+r1rI&`{UqoXZ73*-<9V@pk5E z#yU_0!(w)Cq0`X8*>3l4zR@jQKQVw*cc=oe9yhiIhBP1-cE2WeXCic9Lw4FK^dSc{ zeG9C8_b38`Dc)jmM=NrGZ!lv=wq)mZ2X}6NbFLv4#ZJn?5<7BwTSlkVrRBw&M!#ie z)PU?IpK;q(#CD!I22bx{xNIhgOeEd~qHgZFUqvLIYW!bJZ20Uco(9apO;+Fq%7r^T zc$%PY(wO$;T9J(-C2Qt*1&6K!x_6Qz?i5Nk1!g!`QnGqWaEYgDZ`*AlPV!f#9+YeO z9#`~<2lSUWu1IIFmOthHnHQ;n?=zJAY!&+Aa}|gT7;eBAhh^_ zJAm8or9hm|xdf&_pbxq-LLzN4rJ4Ka1xq@lccP=aB&Cn$04L!tW}|_Jp%r4G7Bae? z@A(gg#2bpb!fnCNtq`Z9;4iW|qc^&xOQ3CTIYn=&D%yHgV)|6}Xcy**EJC7Rq9T(w zUli%%#soVGCcCUVJ183av$G8X33;RYz1oFsY-uFC;+r0~tG+%mQ;P<5lTz%Fv3fMzQ(Fad%)(Vzct*IBDPp)K3 zt_k0B3g$Py;K$qFQMx~zz|5I*;Oct{;xI~Eb_VXV{t3pwnTb}XUqti288e}{^6OyBH2n3O zv;Xuw@z3C8V1M&PzV|#J667Tt>}6kuhx9+cMX*2hOVjqpoAn2#`P51u0>#RK1x;wU)668dOF#mANW~H9L=#Ix25=g{g)MUa#Hj)T(nJbK z9G*9n)P`zn*vC{gk2pW=Y#$|(_dq^MWn*9Y{@BmsN^MSoj9`T{t?KK$By&o!;e!;P~AQ>Y}J zh0=P>J^1+hNgjK!u_UFr>KYI)E`Vc9hX?HmEIp@=OK!Q{`m>Fw7fzTlfF9i0WH;`- z!%jQutm82}*D8b!FxeO+F2@o7nJB-q#Op7^4WlzGt=&#g;uVw9nFWXk-y!M;Xq4Ii zk4nS>IBuba9%9dhV15CqO=AKm0St9eQ6mp;fO(+=Gu+|D4K|R$D57zIFrfuq&?bpGagh;tFtN zp+br#uwv?Xm=!ca!B#8?lMCN(o z1QtfwhN!+;3xgj|v>0QMCzg^n*kK2x)mB_}Wz|!|b=iGl-dbiF7-2}A z>NB2fGl_?k6J!YGo@*QtB1iUgSYg~C*BiR%jZKyrWRYQ&mA#G4$Y(aF=-Mr!b&|-y zS%+U>)zzn8@DS@xw0`P@sjEiqSSy!SSj9^8k$APB)22G^R>{x==Wf3ZcU&M!P}$zE zD?w+HI5goWk`rjQ8RrvNH6#wP(Y`?wQ==0P=^mfz+~@(-rmkO)!F?NL!gJ-bYgL1m z4(z@Aj`r7hi9LY`Z>~wi4LKSC*&`&NA!a|?rD=e`GX`l$bYa{__?AN%=}@KOjS zumLi9@dA+Gz#5`3*8=-uK8#>bLC6XK`&>bh_gSxh+e^Z(5Oux*8bb#<5P_m(v;=j? zihZ%z3$H+gjy4#rffqT!0TalV0i{o4cX45mW@G{cD)1>XQqZB60zdg_@Pi>7A;nHm zgio}gH9Eq>4ii*~cL*T>11z8rtymly{ZKk!45H+=q8KM|Et6+Gnu2g{6im`pwW5&={ct~Gjy$N;R|0Qxh`5MCA!3o^^Um80Ki}eO@ILw zLH5d6uJJVve1XN-@q`fO=t1kaf;VKLmUNy^BhENwq9`VwHu=LJ|2W5*crX=!0@PGJ z07tbda=t!0no+1IKoUNXR1YId&>st_bAlkiA|A|Ggj!7S z)0oZ_D=UrSL_O0+s7lqIK1`r=xQdmXj&!5o5XDW5+Eg3=>d2{IooaJTiqagFRY+r{ zkX~Jb*AwIb90KB3KGN_J1MZ*<(;(~)u+Z4XYC#NUlxEZ>yB@rn@&zZQX(FeCKvEzw z07Ou0b?|mpjh2)vn%#|PMM2uN1;DeQDMTSr$b93UUYyh^qTdziSuEnygQ?zMXe`xLmbCAbB z?okTfe#Eyguxe0WPy+&n&P4veNYLhsRURyKM(Si!FqAO`Khz@-P66z#sLf2S3;U1gceO4Vm&3bE$w5>9Rr>bU{-x z+LVGeEb)g)K#6*I0t+bw92T>ynQ!qJ9k#n=-wuJLZf zs=(IV*fRryvXrT8<(p{ph8Z@&j;Ziqz!Ev4O?j)1#ysUJV^+0kUJ!YU%jdOrXOs$F zDCzL#lprvo8e0O_31IL77@RV@+tqF$#RFz|)^)R-?M`!l`;?PXLk6k%o^tDDVj(;? z0D1m&s7FocPE#k)u(mC#<2xWuU;0b7#v78A%;e(1wz#NP?VAZ*2VtWWWhpTVli0J} z%GO%UQMR+0EuGgt3!1?ncFGOh;0853fw#E-ZEhW4EMp zG(xe8O%O>}u(UPEU5bkNvv7lahJt$;PcXih@suDpC3y}&a$lg%rToAerhr8O%_SX4 zF;C>FwyRS--eT&SJW|rJF9^?E!`E~G=|yBYMX;BWe162{19Bo1n-aC9mk852f;z*q zi)LV7`zd2*9}s;9Tbge2T}bm}}MM zb2Ob;@k4 zNK%fr5||(To})(Wr{eVI|ISYj((i>f4y7c{e8Rz19zpq-s-)^+5(41dGA<1I!4&jw zH~Nn{4ln>;r~)S;$hIs1{Dc<*fc~(eq_W{YKn{D(fECn03-aW8R3HE>@C0q_0V8mR z5)c18@C0bk2650Sy392)?*8)sZ~sIv0oAWh{zMO2&?zhiV_s)Eim(Q6&>P}M@PtbtC}{j z1NYBI)X;E5hHpOW^e8cI=0;oEL4};+1@8lGeo+6c=@Xs7ao7-Ya)$O^Km+!GcjO=w z+`$#1BN7d<5C1R_cjFMHQ4vW|76-%#y)b@Okqiff7aw60N6`0XQDST{S}K7D&+P<` zfE;$GclPhOP{0q=!4h=;pagsHYyP192m$``4J5Q;m2yHD$l)HiVG~A!5USxGv|$=f zV+5RnA+&)Lh)8ENfgIvN8GwfPT!SDD5+O~1;+DeV0^tM*G5`)TAsH~Gum=f_!5`dV zBel>qXaE!@qZ?LXS7;y9B#o~OyDR3z#eikM^4}+V{#^a3L`aA2oeG&{b4Hsrs=#a39&~8(sCw0 zvMA4lDH{?hQA0IYqwA`PEwz#>@k|UaG9%B@?92`^)$(iZfFT_s2*pFTgv5@JVjIXQ zHCDqi7b7zlu`Uz;^VGtQJvQ?eXJ!uaLJLb^4&njr@Dd>-PYhYpHC=->RZ}{6(kIoT zF*gzgAk!e1<0)>F3xeiB(g7yV!774?Dh;JO%2Fmr^A`;$1t9YvIbnv1ku6}-9;D$m z{E{=<<1#toGwlvDv4}XwaxmSpFsUOn9dkgS^Dc)HBok6Rjx#xzGXRc~A;eNfu%sRQ zAsv45x%Q0`@PtXjDcvXJLqxJ#3UaoK!{=NlVdYhPE_nT2!{O%S(d@OwDvk zr<6>?bUHpwLa~%axl{|?^hzz%fHIUuULbVn)Htljo@!J_dDKjgFHFC55hoK3w{UYn zOH6ZwPp5-KyL3efHAc_$wrJ8u5h+6>^+P3eMs&aio&gsu)kmXfW4uq(QZ;H+R8?Em zRbQ1m7>f)>;TuL#Rp)Fjan;GzV}W3GI(F4pgH>3El~{|Fvxdr8k5#Rb)isc{^~}gA zWR+HJm07EGJ)G5AQx(;+bqm?RQeK505=_%@bz>rmTX_{&&sANw@CV#t8T0~Hb#+(C zbyZ>ilvwF?UGr66_f+)@OefXoFU0hn8rI)@Y9wX_HoImzG#j;0z9dRGF4r?bT>YU<)`zRh<@U zRn}^;R%*X?T}?JysTOQ=R%~w;1?V6iVu4DPz!ut}I}f28^ObDHBTRrXI#|_O&(>JU zb_CXd3sBbQ7Dxs7Q9w8=ZPzyG!gkIIw;$fE#Tdv(hc;b{wQfcBGxT7L07lTn(eP82(TeXK%*XUTabWQh%b9H!e7=L&F_=cmX zeD{}Z9hh+uI9_uiGz@}4i-a<&hNTE(g_*)AoFX_LaovzZIhaF4d(H&TA^@sF`CjBd z4uxz$BrnBip`5XGcd^3SOxHB`ZQEY+}W4 zz(rih1$7SMao#y+cBW^3hJ$MP1TaT)SW2LM252OOQY-~i9H(`lMLco`9(@OUR7Y20 z=N0oAaL8q0PC&pw?wr*J`H03t&lr0UIt})?FD&sFmY^IC;owAhq`!HXXZdr_H*{|~ zr+ZqZbK0ky*_)X-f1`Pqh1i&bnW&rEeH$2goors0TBnCPfOR>kk-Dg}nvbbjI{3$u zxJQRxsF^KdPgpckv?^+X;g{5CW4gNM90+&g$#` z=s}o7s~hU9jSia5_?WP3k14c5vfv&_;a{`)w1ecRVbrN*5Pj`ehm-oZ$CawDIe@#F zx08F$j`^zJs<*2dn|0c`c^Q4D`j?FtnpbFeOLcRv8mgJQiJy8s3Q8h1Dz3omDyEw$ zo~Mw{j(Gnnurx3qLMxsbsJ{s5Bk~NXUO=hDwI?zRzDYs83+qA=OR>^`vG7n2m!i6^ zOPR1oz1WM$R#Yhyi?B%B6*$zqG>1Fvs}}%nujI0xd+XNryT5M~>T(U_*xR}NE_oXq zyI9KOG9U`>VqoVo#m(r6w>rATySPpKH}<->x|y&48LE?9$$5NbT9@z%82tUC~Y;C`m2b+Dr&Y zUW@p!ki&69Wh5xZ|8Ow@b-Es z+z#&Kj_$~7JUIgITCDd>;GLj!Dc&yb=FW<5>fu&y)Xp7k3@;U?PdbV&B$&_iNHG9X zk3%a07EWWjZ{nQOdwid*@TTMN_5$~qqL0P_Xj)mH=28Soe&k11y?Z>mf4zZ;-Q9&f zs;|83o_Q_{`PqX$+K-soi~HBj+^CN}wX@yZo1R{~oazNv>bD)}uUqAm9=!S2x1}AN z0Wb<*hzpPK3uTZ%V$kTcig?}m4Ov*uchCfBtj98q+avalsZrkx4d8bH4Iu{`S5Z2`898a_FNtcs1!F}Jp4{WF!6LAnP zrV#l98GDEE7bX%jE_z)!6ks@SO;l{u(=9ILlJMV}q%tJ~Yv}Zzr6UXp3AvhL}62zG$1S46E0Z_xInr!@AYQyML6Beo4}^~SvnTBmkb)2k1_%&wVt^XZTv?=O z0)-c07bldwY8N2gG9Wr8B2nmWVv0)Xjs-ZU3W&uQM97WvY0*T8QR{>Z zj41Ksj9uG+oG^%E+P|y+wh5W2fsVWXSQAYkoXF52$F5%`b|`b~9yUu7jUrWA)L==b zc$=CGYhrI+zIFju+*>yHmYYfbQJ5Nq)nVA0g_e0%UUw6U$JlHlO8A+F zt+^*6VJf=z;)Rx3m|=;|MTnaJV%>cxBZ(Y&xFTglvKS+W91iwE3SQNM9g+@m*R@?n$VIh925kqIZ&q!WoCeq$r^${4k9@%WyHNoq}#!UYHwIdTFL5m9PaT z3Uc^pq@F@(Dypr=?z`~DEAPDY*1N5*_VTI% zADq;SFO2*4TkXI37Tm7?sj5&rX$D)&V(`O-O85gb&K$>Rt0SzTX_A%A5X@Eac`8CK z#uU40#v5-mam5OUs)Q}=Fchf?D@QzX$zGlShB~CQOzXwvtqdN7C>t!a&v9aei?K=v z;4#QzPIRa$s0AjWziPFceT1&7dN#jrCqG=n{?~V|cql^}0gtDLDKhjqM z6Hm}6?fD~qctQ%0SpE3sooWHe{zoYMon(IX<52Vp^a2E?t}p_`S*MIN0x>8>TN~OH zlp07sBpI+(wX23dwqZKLbN~~5RPdI z;sUh!LmWN-Nly;1))y1VfDyy+#zKsN96jRE1dSxeBYQFtgj^~asDYak-~k_+ z`b!KHAwqjxLwtKF3&M6P<<<2JJbW1s%woDxc;GCLNXE66{KuAcEKt@M+AP9X``(mM`HTBNHKl znv7Ege)Qp=0mb7+^(j(Pkkq6b`KU&^*1y?d)SfpQIPkhpP`Qq4_{M5pUWucsHFLQV|)G zXBLN@?krF!M_E3?WN{$rGlmIDurpMsArId0VhPOP4qmu{4ag`N5IJ|a7j(c0Q#jsK z%G1}s2DUhSRfk^z>&2VULl1HYMGH}4ge!nU7F3Wz6JP@o6|6xGV^D-UN^zE&F2w{j zct$bwpoTRhG_#uxEN)y2TiKSv1RB6YKUfh5Nu=@xC_uzKo^S#dj3O#i!OIE4zzO0j_1ehyvv@*SUyeK`Q+J zk(}bx*9!N22z$K?-uewT9?Z=|JbLC?L&QO}kyuhCK1&8oOgI1!j_`y-m)^ip?X#gB z?Fr&*-Ue2dv$)CaZhMqtTdUMyVCI(f8KmSL6?G-X_Bz_!|Xtdfz(WPSYiD+DU@iS^Lo z4@Y;p)x9ndO2A^(Y8V}ph=V5H7veZbn8F*j@RJjC15Mx~4L z=s*OG^^H0*jFDKRCoaap13KEE$UR|a+Iahp*Qn#lc|!&{_!b33P?E!k^hMdmwl%rU zjc&x9pa`F612!ijkQ1n2dg_rky*J`9XUIo}>}Yl+*6|Q%zkme6ZsRCI-tmujBIGy> z_VTLsY-IGJIru|(t&>onjKoAiCkTQhs&S56aNz`1*(E-{fSVT_#nOa13O#B(3V84w zF~G(t%>!U^C>O`aUe&-K9vuK5Km!*i&^fc6&2^hYckF2>``Hx)cGKB&~Uf;@OCDezrf@ze*wrvPV%L$dSxy z+O{CuzOC@NZ+%FgKmF@ZX7WWb5I8z*7bUX<<`4*7avZP|56rL#bmJ!k@j=m-dO-&Z z2&jO!MShI&fCCW#)K`7T2M+wld;?@0*)?%|0V)wVf%0(zIRFm-q+k$XkPm4PCMEy^ zx!?>;79S|_f@!dVrGOmU1%okxfqQo~;g?K{w>T5Af!zmb@y8qju>)l=4KPS0SWtyn zpao(eCZty!MPLi^;1Bm86tssJLU@Bj2n$9CJ=Q=9>_G$`xKNm;g!T7*XJ~|OC<<_h zOLxPD*-?gEfp;eOg5tA>&9QGkmbN%5psBZkahxdAP@g=52b(`hNy!taDVE@eqNV8$}k0f zP!9xX0xO7GR8WjjAOy*`e9gyeoD+g~Cw;|1jK(;I*|!n@!&r>dD1+fQe)(2@5}^W= z&|Oy01-j6F&qzsqv277?jo5gMBtQxE;0ahjj>*J>1Mz$Ta6LC5UkV8Ic7kjLMg3 zRHlt+m~}q!gLS}zCK)^qIfO4zgw%mQ8A%1lD0eeu5<$QS)sPOeKwT#Q11|sr6O}5U zXcEW4XHnn-&)_*omTfXglUs6#*%5x!AOoo&I~ZAB?I;A6h=#aM_~;0A7>22VhXkjRqYD30{FivjQpCvX&wgKIIN9#cVX0s%*N#&h_XkBDG( zg2R=jsV$@_f;UJ#1YvfrP#K3YGR{B{G+9lrnFJ7_j&?zs>opa=i5xW9aaVI~G-C~> z01KA~86lF6iDfz|VgkBp5*;v{1tJ+Wke%ie00I{n-wB%s7Yt?z9?tm!yQduxH<7^7 zc-#aS<5_MUM-Y>TlF0-rD$ohG)^R+Y^+VI)0QL zkuK>5NeKZ*hqSb)l!yqDBBHp2 zrNfIO8svFK@XwdH)KFb3SW|~8q{4;%vX_dt465UZ+Lq3-w_wDQQu%QhX&T}uK56yt zF)Et#F9Is%2=i7_?o`-U14k^#APr@i+$v@d;BuQP3h=VmoDxe~B^!$+nqigpT4j$Y z{p|`8WNFQqrz<$@@|pS!4D%%RzZJIj!VP8M;xgg~tAS!vqS}SHgVJ!KWa?m!h}DHy zjwtZ2Rx7_KLzxBJcNF-^XV!y$qzUonBzbt}Q3U>H5W||7lJB} zSQw8w-4En~A#g9li<0o@+NtjaG>o5nBqtLfxSB@Yo3Ni?UK;BRgk5zlyp6d>wLQ~> zeY{AJr1GT*JB%fE$+Br848%eSNPJQx#-00M$9Z;z1ZCu!NSQx>)IeBRF-_2rHr8QLb@%j5uA)#o+fJkn+mB<$#=0`R3Ig2}*5M-Ag`uQ)z9`fuBL za!c^*NL=wkuifDTw~d?U_2ms&#+D*CGWHJ|f#I1DZ)W_0ZITv4W*!eXw{ROpcpI5c z+jmsk@?wY5gQg?PW~g#!vuYRcU9*Iad(J{9*1)Quf?Lh51Z(jca$wsMPob`Ap`yu^QRtuP2s$l;N zNxCzY%EZWl+kLXHdrQ9S?y91eJqGrvc(=ayYWwxE0S#s|`!`$rC%;gv?X&(v!p_PW z7Of(7!pV3Hd!yrcX~*@%3my^-kt?*Z=kSsJ^PvCor=!#dIwMd3K!YKMeci7EQVw%s z;p9C=n?+O5rVd%OQE1Ni{E{75DPktnr83ebf0Z(rsza@_H3}5&Cp3D)O4x+AN}t-s zfLJk)Xk#dQ-%KUnR=IUG;=WEdFa|STbX${D*gq)g3PDQpMC|}SW zvpnO(Oukfscl$9TCMLU)u!0Ci2DHA-2@I^^ zljSjv1Y|Scbs0YOQ!N(HE)UJ&C{OkEGr#xbLQupsRSS}j3OM?6$iCr^R%WCXm%?wZ z&_08PDM3CN%2>0qsN#zJXn!_tznC1eXw(M>dU0M?EUt2H!C?rVRU-MOM6^t?_>pA^(JaVen8(k+r>IAYEa6gNSSD{?seazaLF>_mIQ*zZ|uU+sBMaXFsIb4LxV_~~&oN^?JrTrav1A!hiR-SCF_vLl{l77%L>r51=89YmMp!E zmya1+1wPDp@-;NSAKy*;(94$8zFs{*{g3E9cy_bxf;OFabJUCZtZ?(rS;l zERA8~>M}~XT(|*hoaf8;Hpfp6Q@YQ|Uir!^QvlQe1Zy4Y=I=kO)o*bEKWW;7*w66) zh^!2rQ>eWEbSiIP?jrB!1pj@it>EKc!yS8KzuWx4Nh3O^DPVmMT@{NZ!{bp(7aFmfGF9`p7-}kx5>3 zi`8bwJ9dfa>48kv7m=qzhs|0_P+UEa*_3mr*|5&C;3`dd6iC(ISI2~zA~o468AYC4}y@FRhy849LPaW!L9 z;l~KTh6*eo=&Q$=j$$pDw*sNB4OEvxGxmxRA`wH^Zb(dF7L!h8<^(biV(R{h^GWz2 z)8O}y<%i&SV{EXAdeGCan=Ie|ogKU?)nq_D>ceQ$;Ok6j;mXL+#RvQ@W<*S5R-7bI=&9lA6+%>-%5Wxl;0s&K7$Fe@Y zx_gQ-A}61R<8KE0h0j?>?-%!=d|^Mh10bFD|Lx+b#r`wchp3VZKM{{+am4;FsRY@^ zDdbC$!kJlLc{iFw^6Y@aE~!mT&_T$&%Kn#XVtN&)uQ|EXl4>&GdsN_lh<#F@CeU3e z<)`xAs`ehg`{NWNRY8={vcEVDpnAH2Mr`+v+TU>t;T7b$d`oq_JDNll>y`T8)!sx7 z)4gys*xzBaSWM>=MCR4uLa~N}WXbKR>r#V!Wx8j9xZ@0iU59tGpMclfbe>x0)HnXb zjsqlL1w4h6-dBmTJ9*@#6?XvSFgdI=%v2hT`+p;`=|aSM=I8kWpbE3D6x3TO+u4Sn zO^sb#{!wi4=c@S;Zs`2)mG}Xm9Xm{x-21xL;3#G&$K^u!J#>n_+7klcMGFJtPS!1j zmH#b;3`rkW1sn+8c;3@%J%ZwPEh(VZ@9_-N`897DS}|}9fl&;sl7d@i=Jf*R2m=*W zhJRBcPmcOh#6==!M-uw4w-$j$b$X1`B8yK2fAr(bGo6EAB7NU%G|!tyw?}4KTWtUh z^&H^iCLC4DWg@}rt-UlYt{lW%9BYQIF`q1zNt5J;?hCKia`7Hl?H{)NPrBCWS5mu| zBz?-hdJ4|!__J%C>r zrlj`etdZQNmD0*fRy3IV-w_IqYYvjyQI7g`C$VmqA_3=Xb?pio(}w+|YG_{r3E|X)BX=0OOZp`1(2GxV{5L^QZq1_YO3csxy+g8Pm zw+&0O+co(D-k6y0;B|SwG1v=S*8BN1d!-ZZhtwn384zrByn0qsWTiR$SYAZ4q&FPgDR$QYQ&-^;g(>Idb zde7Bd$`pPo7W~I<%UbQhc<8h#Qf`;nhZ{-dJnhrOYI+ob?qL@^z|~-$kRaOuOUv!z zQ7Qj-{7hL5&N{2l_ZWCUqd@8AsnGX>UY0y4)l!?YgHz;oQ!=CS5ZiA z63rC3pxdKsFMD_YEj;&i;M|Rt-u6=sIUS};xjEEOBFJLPNZn`0p#-4s-YMTlwCVm6 z(Y~2ZbrmnP)Njr9JuFX=8mge|JydJS&SGP2RTJ6V7EL|IK&6L=(}m+eT&Z@5@A=Az z9~X#co_m6{>m!t3GNHjeG3<8h8f-ODczl{-5*UQ@I#{YDy%?1q)bGF;R2K87rcL$H zOoilt6vA@z0Ql8{tj2#1*`W;h z`9xmfeG@|yl?Ic6chF27*SYLUV&&n?=a#!Zp9bYuPe2PB2a&>&eXR$@3mMirR9av?^?xy9J{~&J_(Utzk zRbSRgDtnU(bE9^mb;)VVa&1z@l17aB?va;nWUdK3v{WtDBx6|Lrs+9bBA2zdgC{yc ze`JpRLDWiSUA+?B@GEf&$@@5#Da9`ZJ%wbGhm7~F+ zacrQyKIWb`Mv|0oYcLC^eG?jgP3grL)WC+fX>b?~`&6g4X7ae;^o!eS_jpiC5b=oe zX`ko=))8)XNG{@Lh@ooRJX_}^{b6Fr+L*$V_DFfJ$6SS*!u^sj*%-d*xP15^cX!?&?gMl>`grsyp?9*wJBZ>entbi`lf^gxg4|tDgdM)WSASZcwAy|=(Y5X2)l~55sJuJ5 z9s5aIwv$@a*f&J~brQ0g>1(~`&4S+j-STx93DM|Xh~bhmznc5iYh?s|?TYWsqVRk0 zf%`evL|05*);I4xnv3d*I52bl;|KK94-DuzIP4qrafcMQK9j->pQI zS}Ru3IC%$uguR`6@$fV&-v3Nvp&;MW`O%h^_ zG4N4|Ygvx95s8H`JmOKXPTfHII^j-7LG~GP14Zhz5W_|zrnJ#T#SP>%=X)(%@t#^K zeyS@fIxe^@dGvi(^_imY!?$uTkkl5#oT)sm+X@**%AD3YV++cpCS8=Ca*qkACJ0+` zbcMklwTL=pCOH)mL@)Dtyk}YBdT&J>-h3X+t zi6(_XLsx<(aKz|+lhs133jNS#3Q)PVDftj>ET=};lq#PDv#cj1KWnga#U}da<268| zhTOqlc9j_N+eU8WR*Od8{?GTC$?JegvS=E!nt|H6;`^1wJ8&m9WT^?2saaLg68FS9 zE8P!(1;~X9J}J}S`T-9v@%=LSe`b#I?W67G#vQHIJw3-SMWO>z*@EJpe`JYsgG4pE zCufv_(NgyUs>DAI+0=%(@7fbR>Tpsq6M%&Yur{9mGSCj8KyIj+5I6~-)<`%@BpaHf zv{(1ief6{xKgtn$nI(WL*IuTqp28v!t|{mn0gup~2oeW|sH%q>s;60N)*nrJ%#2$h zCX!8Rj%95!%C<)Z(iaMQ#y!KL0t=daJN%Ima*ZY6*iqt$=Jg!O{zv*R-VR$0Ho`_r7{-bi;s$f$^)E!VU8P(ci(idG4@c*( zszErB(P#AY zttcY{Un6R~d^&*zoD%?>=1`Al^dCn9)Z`c}4M&UGC^eruPY!r*%zc~{tBxWJ%#V{= zh-1rMBnetbDOaxs+0j7)wJArLLKvS|H#NjMER zc+#~Yv?EQ4Vg_zRd4ftSS{Fh-p@-wq%sXC|oB1mRQ=zz@-|$w!b-K$`V(?&L~CSV0*DQ4B;)yDr^aCCY7rPXq`8)s=0VSFTT_ zEer$7at=&#FHAt}{qDQV4?RS+HTC zGpqA9<(EI;)orL*%&BgTPz;c-M`@$CuD`84ydbcpkFSRlwr6g2Dlzfk4hEY;KaVlE zY=6-AE!93tUK?Y!#YCdF_^;^+D&JybgGQIm+E;HZsl zsj%pO6nN;H{`!x2(&aCfdqwc2Nsm8FeHA9B?nM&`qGH%H@RKk-D>mvsQSeW;2s67H z!wj2qo)shbDNnQaBARTlRM1Y8CILpyY%fAX%AB6@7M;aYPu7Kc4Cc+tTF>~m5PfWs ziiKI@KcV9Ju|1MzCJoLfPr-XRxf@=+n_d^~R+p$#M)}eeXbupWmtT1~O-9=Wchu%& z{033n=vLp@IjJT9CnG(W@d(*KzOeYu$18IBI0P+@-;GlAe(dufn$CA5|=ux zmom}J$yTx>dkp=gG&lNMzqw0{PPuRt#h)JK8eW?5-0m=Sm69Cb_9n+FJ&LBFynu%O z3)vLVQ97@NOfX$DFX&tB6AQPvUKb|B*ZVWOd);cm9CUUc-PpHPw2fx#NfbWE;y>k% zTOXiF3C$v3pr&4Di@Gh$P5M|}vrpB!Z}G-S=$y(uGulL$!S28%kTNECu(YT2dmpDW ztXE}E?`Spt3+Z!?kxw?E8+ib!!@7dPw0~b-G4Fk6c76)P%$+A43)5749$ZH{n?#z`l+Lq#V)oahKn zaYVenax%E&d$X@*w3v$FFQr*lmJ*5l$ki`8(Lc@k^W*J06TgKA_|_)%5Np-3@HM@T zy?`+j_4@QXi|#&US-O6mR(Q zJp~Kdvm)r4+iS@m>N}RdqlZo8fBG$u zG3uu?O8yPb{C$vF$jE^k>-Bu5p$p}takSqrGUih80i)-z|grJq_>8*R^x_W_GmxUEzoBcOl< znbU4JpVV5hPV#RDD1NRor>c63R!LjGRQScJmKea$s&Aw8v!-mxq>fVvN)fSSY{Sa& zUX(+-JIE1yrIh$n{|FD6SnG(NZ;h?pr*rsj_egW|p^@mBK55$4zeC`8R9O4h=&3-( z2k&ftRfs*r;{)8_s67b{`_)i)h~hdCkx3lfqKk|A!kigho^73!}rLRRbB=M z|NhfjcG&czP-wr8|KJEh3yJT4z&jNmJj(rduc2u&@i{|OtaD~!m&WVY zeJ#hEZ^@~fIxZ4B!$7G~f4{ibj`>8y^#AGq)wCkiv?k=k(iYyo_9tikZz@`Qk?YRU zL941(yo{{jD{HfH1qnGia)#i?3=_deQotJi%U8zXBcz0zpTpm2{vrzvmzv}w@f1_i z@Ev+|rW6v*eE9dv1=)0W#POfVEh-Qq3iszZehUx{;O>X8VS_V1)Bx`9G6Sm5SVpOY zK*dp-ek222=x^aT}5Z?k!JIB49?4+z@Y@pVaBFNIN0CQH4p;9Zn%9M6jy7$UfVW_0{p zxlxsZL5=LeX>mgXfh~;Xf&Qyua?RtuMy(ZIgCw3+#{IzEv7JUCIas@g!6pX=nW#J} zqW9&kmog{>EO7gEgq%|s*L*-`S9LX-uM}4PZhd@(i}8CVv0wMn>fo>3xWmm+@8|R> zl~1{iy8ZVC3o-)r9!pKFw+b*mIJbW=O1mn_6Pd>F2K(!BEonaC?yl{2l{AsBQxM)0 za6tB8(BNalegKBTrPPo9-tz7FDoCRwczy`{IAEJ0?`0I8zXo&2DEu(%>93)$jA(2U z+L9=H`%=x9t>NbVQw&G9JxcIIRWJFSUk{`E+N}BBlhJ{-&xA7OoouQm)i*IA`#8dZ>}W1l*I4`M?m`@1u&AM>k^gwJfUEQwfs8RMtF9wCGnQAzo^)HPi`zX-p`osdlA(sP$a@&dQ(N+H zb=XTNj>owqEbM5h?pa~jFzNgC4kD`BX&JuSnp-T-<1%c4_ScrqIg3%A#G6ewXvhLN zU-KB_o4WQ%g+Mn{^Wf2O>k7$qZN&gdICHbH+U&HW=2&{^k%c|~l;QQEJ{_c-{8a<WlX(9czq!-(~jFq^b&CWO-kA8+%0tPzBgtT={>Vz4K!Xym+)r zpMx1CXB&Fza-00xr%!m~Ac3d0zh7pPc) z8#qMCv@z&k%6me`%;X@?2I<>9TU4emp}bP74}}HAeh56#Fe=W0APLCQu#@4%oW@4G z&-`~cvDzfaAiDZu=jROh11p}ha40=C9j>v~OMmN}v>*l~p}n|YG$?UhHzR17nwna@{WHC#+KSKacsb{<}ffQ$e0O;C2kpzV$q84)7U ztSMh9jeaXppTO`1(v8whQKH$E{M8~RqYuUv=0&;4A9LaEdOL`_Y!66jGx-z7CA< zfu{rW>C-Y}@vAzWAE+8?uw(mNIg`(tN%kW$+{3m>E{Mc&E4R1*8(u$*DkhgfQnuQm zO%@j9{^8cuxreHHttR?u7k^Nqd-Q-np}87*@#)5JT7a=o+?0HnQ9u4hI&Zicy&1p? zFhC_ln7m-M+fL-_6iz_O?cg%_4kKicZ#_#fkVk+-{yOG(PTVqFlp#LvawfhHAU5c+ z3{?I~39L67i)b{uSF#C%)p{xslPob0TAo85OqRpjhwd91$W)tQ)0V(459kam!|Fe< zsc-^GKY>|W)=yaty(8lfRB~%|+Y!=!dZw=mSDIhffakdr7?)eYo}7}5O=uO)&D$8p zItQ|+hlyN&qp%s`o@OjH_kgIypOw~h3hKuav9ckH>l6A15lX_qI$t)6U9)T4JX79> zJoctd?m3OCYP?TgqCo#+K1W?KE-`HRY-Q_guR<5;jF)xu+MOhjc3&+Yt!|sXiumc_ z+WW?|x~Z#qP<2$Ua5~25J~w6JHIzGZR6Cd6mU1b5pM-Dw@Cl3o58eE?1CRUb!cE2; z7#qGToVF}U%PcoawgiuJ{^>bVX zY=oKQe}xT5O1$Rr+1pC)6qTGWHilL2Y!#wkl~ zu*f9&Yr;EN3_tYm9%FU>WVS#NDV;Ve-N__aI?Ncy@rrf2Q40w;-XYyGHwUXJhAr7* zaD21vA9&aNp7kmX$Zjn5MHFWcD$sKlyh>OC$syH(YI7k>ajN|ryZ0Snd{eqYZP3IgD(5*{P@MS zt?&1GM@amD+OPnVI6ID!(p8&(|Gp~2#LeU@zu|yB-=FrPQ4H8;-=gJy__8`@+%bPX zA71fULd*95rKjQ<`6lMy_aTxk1Bs-Hig^&HLU*W6&~IJR4E`|o*vu#zM}5dOA4A*| zO;;DK9{2vQTwV~I0*57sA~0I;Dvp?WM5wjMvkd1dGR-}(jO$)rI+rUemkhy;6~P7I z$ZX>fAl2@MybM0n_0WBtH5yL~1bDH#H)ysnaSjV5I z3AdYIfDRLW`FiEc8(&_g8qH7zWxwQSaBNOE@7nb<{QRI7+~}00995KtV!jAk&2xit zv#1+HEY`GM1at}?GT)bGZri9II9Za5J1p6#CJ0y z5dtcvFVbeGBhF{cpG|x){Xwf{U;CQOeT&T>TL+dli*qd7)1}3i+?@(YauB+UlE??~ zUny($CO|MC?=7aXt4gJlDKXAzC}M~UQKl@lEG1r~YLxBm>jWG`@aJlo^ikiGeEySU z`*LetAXhIVa!UukciYAE_{Wz&B_*Zn96$du+qhec9DPm$X4+K{J&-1lF2T^W#?ZPB znR^v@J0j)EV(8_R6_Mum8%*a?C1}}@e3==&^?QX+vs#BE9$Lz>qoAjz%JRCytb^N7 zdZ6^pBWGSbf}3a=^B9skfaf$Po3i>^KP5|MY~X$KzOcqYxyCVUB-g0g_n27m3Yphh z0)~ba9!CY46T4nwrdG5UX=eDp07d3fj zNBtUa-x?Q%VeiAQ0p10Wb!8FM`-qb$@(9MmBpX27kb?Ly0u@c6I)tJ;j8*{ui>mRj zsdWRnc+NQ+EFXCNSg~PFeD<_Pxz3rswZ>!6l*3UaNM=is1K0FOk7B0I!xh7LoI~#z zBij?BiP<2s$XBj|5yp?Y!@fzHQxixHQOhkORHH3c>TFn_KNc+f`^7f>x$RiVORk!i z){K=_6)%Sc*z|yGQd6G`8r*V0&!MAg#)soV8}W+SBLc3J{-dM1%c_dR35p2y4rDx% zIc{Yk{_(b&X+D5BDuyCxgcrM|!PtevB%tda2jGC20>qCh)sIBAVjgU%h%$dwg*BKI zM}aZXCA;+@Kc@*DEn@4{BT=NH5EXe@L^$Z<5JJnyItdjUVD+ocp;Y$TH5&V`A8w zLVFxts~!Q)8sEmaof#6@&wVN2g^xdV6968Fk~kD3e6uY*hV?QDE!pSMX5M|V4}xiF zu|PDkr~J*KRlO-7ECV=_Xwn0B(!p9IlYJrkf41iR@dqIE^9t~fo>>0n%uz?ur+?!% zQ%M{66EzkSycYJdvmMcz6Ot!mfpa^7;?D(tnE%B!)FMihk(uEBfgi5PE>-w3f`2r` zJnr6BcnR}DDd{iIsfSJ`yt;+F&V27}BVo#K3>Z<_fw6Bs?y$9vC4&=PAE;z`4{?HUBJTg8-Hwo4j zM_q=+(qTnLSb8#xLv)EB`}{a+*>&^j-zIA8A!ssJ9hU;<)(K5|0{^w24t2gN2DI*Z z3mOi+0y<2k@J~M#_UWZ>#la?Z*Uh5&eS0*|aBN%0Q1|8(Vf^4${Aa?D#8ya#Ff^|f z`cjynvK6qF{Hp=L)Q<;wBTV#FXS@z{E2#aW<&4zAhv1ba@#nKLM-1gm3_(%qWRljq zej?bZaJ{2mvFoBDGt(dU`n{gQCaE?x_BOn?qYH>Mae6&~tDj0m3PmshLdOqK>~|N} z_RdjrVaFSw^XkfMo_+*P9gsXtq{Gz`*?(@ZFH(SNQwE3*Li_`^+R&XC`nyE6MB^C9WA^+H}*1G#IoiFE#+@p()CX}znchDrNJ$%%cFxi6yx+Yo(T zJ{ZrbAuynw%YZ`7Rf+4FXinLMIzP$j4au42R~3G`OYy}2C|PxJ<*HTNo%m;L&>~{K zqOw_jx|e8FKtO*xhe|T%Z@q3rw!oz*$H;|{OWAwM3@QL}RvD^9)t94o%a>$&1JdNN&#kg$vEOvBzIBHt8kd(A=G!ahhkj0?&=vzGMjv zb+LD;@(mdC&l7`ybdlTd(CtzNNP%l`*jS9QC@H#?7n4m-Ua-V_Wh`o~#}cW9 zurxNDH>JnQO2&~(DdR;c^CYcY2cynH(6Xb1rbc2;a9<4(&BlwwB<;-=R3yck5*t;d zXdb2dBY>Q#iQbCGUo-b+N>`G+$XPS5k7nK{W-lJ)Dpye)U#lqLt-DG@L!W3oWmrwu z@A7mF^$q&%C@uYTEazzsX!x~DO4gAY=Q@<%ys2h7$SGHO^!HP2TWGMUw8?q9H@nP3 zmWd}_z0klY_*OgSNlqSBo)VysJ@Y85W|&B%I{*)mBQ)yfB`w)S528(cH$-1$+lzTk zm!ho6Q=eNrDdpdFaC00Fo*HM6BA&GGJ{twg##9GWIhufEbd~8{hZO_3JiBsk%s}$g!vqE4 z?l)m4cN5);s#CS*TtBHGG5aV%^2<0HiiJG8F#;17|B%HQLIJO*AzM#?8>D;7BEmnh9~eabZ--~&V^ zycsYN3RL_V@n&Zf>Z!!x8_DubdEg`67m;CcA}cz_C|@Fa@tDu;k3FSW%#kX@ty(dy za5}dLaCtPA|2Q~2eR&Ywc_;FD)K%&(2d!!8j$!ho{<$*@C9*89r#%g( zem?*pAX4<9cV&`<7LO5iozP7s%5D~VJM~$yR$JH50odyiur;SgQ$D zyp*k0Ua-3Q*!p$N@Xkur@)K?LKpYF3w9?JxuTy$12F{R7@*bFNA>+1ofiMeeUKx;~n) z($joWOzZKvF;TCo?#g>FN%;4X=J7T*PM15fNPjthJmW>?XdOojb=S$ZOS5eX=JvYk z$0Mc%_n-397vEQ@J_lzPC%8r4<|UoA<_QTix=+n+Ov9>e0qN9BXdLdDR%+1&lPLCjb2ZY$PcY?ShSiV)mN z?2L~p#>kmK@d)b3$LLh9tjk*g+bu_ncW`&6S*{bcAuJv0`D&iva?OamE3W4(^j(^o z6PZHT^KI((7*-4&DC_h62NK2phY}rY0Bp;7HnwczkS~a8l~pksw4yt?8FfS&Dl=p- zv87!9P+ZWjongTLOOAq2_wrE=3L?=O{W@{JQOD|)RY_gtY<)YOhD&{sckEqN54&P> z<|L#yQya4n#h+XB<*Ny-OF0xZzy4rz%pXN+O!GCK8c}diY}HdYS9NEu9mvO&p2V3% z-YMIb{-cjW3ZmYi3tTP_^=pGmXC84d1uB%u6%%;3^xwjJWVc2sjL zWw$9ad!V{)aVxFc8!qde^ix$Jv6x74a+DosebSJ;B|y!##(()p83uf2I+wleVO_E3 ze|0?EUBXg7o1uF%MEel+&8h1Z!9jyJgJ^fJV-oLY?>8hezh>%bGofX@LlHuvAxWyw z)D~!mkhbYy7?V33(Lh-@0-)LO>;^$bRiR)e|X| z{O~I4q+OmSUl@nttG@B?j!SdL!Dz#r{o#%nDX68Nnksl3(VG)?Ry z&@M#H#OIDcB`d8ERGM*d8Sek9A$210x&JrkR?8fr=CmA^57_}rE~kZApz(S4&(KRI z=NkV3J%8PR}AqFdP+e@<$|&>Z1<5hOCQLR;tS%I<4Os&!!!egCN;Juhaz3B1!2s`6!|mR}B!~s2}zJ+16X*`S9y8$v>{6h*lI{ zws*YuLpdgrQg|8{wc3BvN+vwd8Ki*n=7#g?s@ldc<~kJxBHeV~W-VuxidSEcx|nFl znAT8EA~QNUQZ&`?C!bdhw!e|F+YYg)8wYxw%1Q=guLgs!2*BOxJG>md`fB!~jNkN- z&~eXDT1gxkx{^u3SDvAyUBS?HLm-+{FZQtF0zQNdqcu8oO$fmiC5Lx;{t*;kOtulP zTw!T^Qq^ZtS(V$6ptl5_8b~-s z)?|d~dI8L}u0pAo$;LPh8zv0X5JsG>c4Km&dQt^1GW~yb7j2uDyC+by(~mIyX#L@S zsET7Ky!M|kl7lOZ;7Bl5C_iTYNx0Rg=E=OlYdli=LY9o(ED)G16bPSWDU3-Y!ShAE zQk?cn;UWqdoR#oJu6_9Qi&Y)`zPSa)JM#(PcmuTzC9;b$wLco9!hDqQ@q{&@yo9M$ z1AP-6=BZv%apw)r3m82StNR>s7R>ScqY&0s)6@QY1*&&&49vy8dymPjF)sDshQGdK z7^NqOA=^qQ1C;@pCU&LUpQCVB*0LB2W^je-Via$T(0zviVYS~33)n&qK)t=PN8#H= zcqIoCOppbvruZYL2>Vv=IVJahe!<$p_z}KCj=;%4xk5_Qi?FV^j8|gWy>h|+XjsrZ0WsC7!2eQh*LfNE^fRZt5 zk$rIqt=nB9@cgfG(9RnRTX@J*8>}z==`x0PDD44;^r>&GkH!xF@AP4}2^t(_xx};d z742L4*0tAgCqTfjDfS;FrX5!H<9`YV`o>Bkw2t>=8Xun!ImS>M#>8O3QtlTjmkL(( z_CF~Z2ZJcD*sJMxsPOX#v`VjTnmkujs6^lYVe@OS(%Sy4b@TrFlJ|`#LtCfCPeSTJ z=!;0m9uKbhiY-a-Ndqlb-@_LP|bziD@kdB|N~8-<`%jkc+s9xIg5MogJ{? zd^l?e854VWk<A5e>*y5ya&$||6Wlmet-AuAIDST-_wt+X0DH@hpUKbTD8)7+5G1Y~=GN z>g-G%Mf}MJa(Sxmn*lKZ|JM(=^GRO`>RHavcMQ`fxr{rzD9-$Kbpp9pCva!%_ZeO^ zyt;j@Oyr~wG2JG;jm9E21@S9=3B=Bb_k2l;UJ==z5)umA-oS`nvcVpZ5Z9j)-OZE2 zGr*)@9+?{uU&caH48S{kxVhUCu5-B7N>u+71!lpph;z8S0r7Jecmb5`a-?!{h_NS{ zoS+3t&T($jM66?E3yQ-17sbTN0w5eG6M-N6V0FQ5<1t^PuxpbgsiB!Ehq0<(xGPba z#VhJ6QH7qvH2n6sjVNM;=>9BN%FI)qk`fW0_xFo{dl#vagkhIL3{*xI+ln%#7T9qc9aj^PtuCF>^GlmDr&Zvq zWf)TiD;Avay_9}Jnuv^rVo67LpP&ht@d*X7JaddeQMC{K9iCye)HI%aAg|INBbLN>$Lx&!7I#`8q7jFzwYYeH@8 z_z+--W>rublE!9?x6PQ32XCnGI|Zrzn}l+PX*)_>e4E4XwEk@I4Jf9g?Za&2n7_!T zTQ)rY)YYs-{J3(4(P+x_a(c6&F$EPcwH$}{7 zVuJFCf<^BHpS$vOsDDM23kI;^T^}9}KbNK7!|xyRk0DdCCqHL?C|dMR6@+>N0*JXR zVfpV^_-xboxGwoNocP$eJ!Am9Uy!^$3u%GTwEhrM$=L+7Qo^>!AAM$_Usvs)RA z$T4F0$?eXp_Y6z#(nL=SQm?zivwwL z4pM3%D!Tub_3*F1a=u|RcB#Yp$IMVz&d`aZ5DhvL2arlmD!jzRzy8L?O~K%lIiKKv zOM-y0i7P`zt?}BZ$DVV&TTy;1@pvUC;_pK`@+-=DQ{|tpD4kl_Q7HUE060#G)zN_= zb>jYg#uQ`t=z9`_WGcI=GeZl8&Jj3WsQd_rncA4^{}6T84^78?yTCUXFnXiAyFp4o z7~S379nu{ej1K8W=@tP|KybN+_?^4!>)~E2Q>$>fW+JW;?h)>@gw79kkgTbISd$T%IT}vAPhe z1n)Yy2RlmNKD*^(dKc^XjN%0LhUTcorbRn8e3mT%`UwMN^WE5kT#+O<-zKBd-!4Ba zoI=y*Z z*9x`yxEvVJ4z-`TJc2R&+hx!=RIvnE^N8>Os<@KsC}~WcE|>uqV{HHS_-x>493~dw zKruc(Md53f7%yI`Xv86 z>Vm1;0=%;WbtxZzbE|DJx`0Bu)CJ4McSUR@){O7uRSN<+zsYsJv;Hy)V>le_dL9i|KyKL1+OlGU}&99QG6(!gSaU zA~z2IYEY$ADIprvP!;?7;RyD<%=mIc*~0=GrqsU|TqlyU{+RA&qsGM;Ie?;V(n&$+sK$&)c3 zE{AZJcM(gk!-QtpD8g;Kl;w5zf+1=Ms*|MR;8-5DI8YKhs+&#rpoX#@q32koU0HQr z8F+P%^{puGLsePK73a}lvkhl(@hF=TfWmA%pT2lDaNY6FHNJ1C*4)F}sR-^%F8nZl z7i_0hiz2?3C-4bpsi$euG^Jk+E2+6pk`4`gwDP*mOSFH<_1*`*)-6LwwpOkC|F*CS zGq*|?EW`0F`Ji5!w7~78*tQ36TWc<8RX5N&08b!P_&q9Su(1BX==tPvglO{&0YsR| z5U{(>u15h2$NQN{0JI7(x4{xM&(JMOB&A6t;hsCHiGBs&P})#$`2)@bHN4}8W+ioG zrj)#}!!LF^FNHX^8*qP^c7Gp!Ru?-3N|=gX9d5AGs12l2jl`>O#HxqxcWeJHj&^@5 z{jsvor9Ik-mus_K8>2lR_u0qqV@ZNC&m$n_P0~7=9#$Sb`X1fSF%gDbLX52p$sW*p5*8_(0DBgxC_6L=oN)5lE9(K(c?bLs?PuEKQ z^Lgc0G2pA>vCC4;eOJ>;UG4u7v+q)smJ&#Ohe6<(;VaGK|A^Vl)E1T|s)UlF14AFQ zhix)lJMEj9rOF5(>q+P2z@yGZ$u3goF0r@hskdHTDd$~%irsxf6R~fnbWu}+X`?3& zYqbQ^!dk;NbYBW5Kke|%mYes!s~iKRjpJX8H(iq>W$524cqV8&1%6@-l$n8hkM}!x zt4Y41dNJ)sF2+zVP4a00{wVW`_$p}dj{-m35-fj$2v;u)%k(10iwd671s;mEhpY_? znm410S_RRMc83jP(gW6gVpvPK)tvI8X7{9K&ttvPWUH@96mh&z z)J9!bCztz7lvK`$ik$Jw+Hts-8vXR<);`fT{KcMDM)1Q0q%KkXsUk7EPQd+b0Alvq zBXRSiBC6xB--^XrmVz;F9h=dilv_n7yNmJ8#@H*y?(dvO>v{0buxm@NKGgc9wZ07@mbtn?sHIejAv%jfOyDh`XD2fTuU0jpz5MDx3oE>xVwNUELdH zSxnB($9E9RZCQWS)^9kkzOdMS>+!j$d2=CQ3;HD}t538mIQK&}<|p*UPiyo~d%0WZ zm|OS$TW|EOpWLsYm|tQ2zo2^P@0`M%hPqgHa8ln_cj+^IHS+kF<$sOk z-S=4B@rtk<{NCO{bF%{{vodR7q#1 z6py45aUQ3c^KFPM%C4EF9{l$=0_UJoJrQDP^q-TeYLSp&hS~6FrEa-Gs#Mg6c4zej z8D0i$JTWJH%Y%R2jw@VE5e;@98X0MDp{W zZ+22n7At%Dy;i3DFogTqB;{(@eT$HG`GIfNYS!t?ucKm>)#f;nJ#C=VeCG(&J9wO| zTIx_Na{Se5E@CeX=JSxpj8t=eBH1UXeYlrkrbyY#iOcFT(<@z{{AJLa=*`8tsM{w> zU88l=#E%+ZQKuWD_#kKaXYGQc3pH68qgjoZzRx_@#-5vJa%V^MYcaj%WQ=MT=1Q+Z zQ?dRlP7dj^{k{3AfU6~wwGe>EqE@IuqY(JWP6`EWB>+1zHC;G-a_yAKZMyV5AnZvC zsZ33W$qJA5{^42E$O{Ef32rY zsIlg^a~e&9^q)fai6sgSl&pjHBd4jT;SmoDGV*PaSIy`JLD#X)L0 z>WUly#;C*uXX0^W)v0mx2~vzO=f#55mcI6%VAQn`4eqd67n20IzZhNg^U9H%1z3_! zxF7z$e1*oOjPy06ncLOjBabrEkA~Dd(X6H($(#YvY; z;8lL{mMFO0SbYobE+zfq;Rk4z`1$)ou|(6?m%0s8jkt|80biBb3*~EQy_hhmsyXa# z6>!ixS_p>T>W2Rt5=;<(yiopcBypq>=d2LP_^^vB_vkDF-B6gURJ^bLN3WD)0YX`= zfOPHUv1G-t-jx5AnmFelJ;OO>CPGn$5txGIP6l88q!M#=1$-qg{nyJwWV-m%5z=$5vqa_ ziHwsI&>3cC+GY>jLrEm*FZ3f>sg-37*|p%it*sr|-PH{$a+ z7Nh8Ghg(j`Pda95QRs^)v72Q5QWl5?V2U#TEy+ot5}hct0;`yrL48IErf7s4PBo-) za)u-lr&?a@;T|PtKbnVW|Z%ScTsIE6<@OscShJ zOON3;IRPiqBuVIq@nKELc+B4>xQOy2#h`C4M~T_3Mra7}O;*ga5>UneEPh2QR`SH$ z?K2)vEUShCGK6o>Ods}NDFsS4*C+@G$y}QPsxMv;`j9svQ1Ye$C_&M>r-VFIB4E2V zk>rcuUWzOPTl;*5SEi%$wNk0`6yLF^>f~oQ#gD00KD-LBF_#|w0m%mqiD3*%iW|Ez zmLub-|FqZ5Hoq$k@X;rp)Gof_@baeD%pSbdV@b+?TRaFo<#*-qr7U-|(nR}SIf|i?=&hdlw65J1138e>O-@Sf~C48X7q6{CED6hAjaAv|o_cRG8MOtP3 zU5QO3n}qWD&hy+{dGV9lU)wBwntCC$*(=}uq~h3-&|t>Yze|T!&NKapaHWX*9|ay1 z?cBF?tKOSj?`}@ZUPcwxFslKo!QoJV_*ZzPJob|q>5)q5*Pla6bh$wNn)`3>E!0g-#?;mwPPT#S}RgE6u{dM|P# zK+_g4J1UqKRv@1&ShRYRW-Qg>J5=Stp0kZ5n0~h%P1fKmVfo>o%@;4&xKm9|Vg{Gx$k5JBM>KM=8bWKNHrrT1XGm2=sds2)UHs0{2Rhg`@E-dMbMEOwZ6PC;0 zfP_MU1ak@5?)0oQj-Ja`{b}IPH2xFzZAppyqp1kCV#auwQ!~56^YpYo4w2D9?aZ6L z^-D7RA+-qv27@JqC4RSotD%3+V`A=cSm+9{9}eu2zD3hW#O#>DukuLGDZ{4a!_J;? z|4VcgoD0RA(3VJa75)k7x0huC1_A+r_*Co-ond8(aLQxWc8L&(AEplq+v6H!)XBr? zYh5=F4SmOuBd(`@< z3a^ZU05j4n{T{7MlVwf!t^{}cWFjhnBNc%V`j~`XpjkUPI+j|Dw*jLacz0_ z8YoA^T}%p|tI-`wt{~Tw$<0o!l$S|Z=opp|gx$=<0cA5b?amdFpj3GL`jMbChiB`D zVK|57?=Ts^UiI;NTJS=T*>Ez)_^F)HLS7X)AU;qn!;FQ~7j!VE_Tr#`#SfWqn9tNM z#F>mIS6R@>Uch}?z+#6#S`KSQcVO!HdYff>pF**J&w&l$SSApxXk~U2g=k^~#-m?p z{{*Xz8~RO*B&nP5{zyFxlXF1btT?XZ19b^xw}cx}q>mdz2__kHhsx>sQZj4)6C!eJ zhr!o#q{`E0s0lW@Q&za~NcVE}sEeW&3-HAh`~i9xtD39ky1%NGF3X=l^j+=ErS$jm zd6F%k?mj0`*7vj1uZEQW3PsSg;$*P>m$QVEEa5^si%QO0OBC!Yrq;=h8W@7y^DW@{ z&lZdHPhWKlTN6o= z85V*AU*auvD=vp!4hbTMv%FgCtXjx}F*pSA5(yed5ntI=3oBuk!$clM%TamE3Rv}Y zD?OEdjrKz|hfu9^cPbWB-4vGlu9QtU(YHO8XuNu+g&JFr>J2j;>5b}A zc=!gFkc|f$3};ZG>hJWr*v zm*j8mE`*6>YU05=hOBrR2zAtKg4V8-ErOK9El*4i!&LK}I;jKtd7nkhj&Ly!I$j9X ziJ-IjNSQU-A)w%7blxFMA~%cOp;tmNd&??u6yT`;ekkR=uz_h01yr%A$aJDk&XPRR zlbX>1!{>LnVTra+sNi{C*SMb}IZhXS#ryhWh~H)!oqkNaLZ>2cvR(wVz>~@Qywe2z z8>8o-`yz>S?FPfF>75gP)@*XP}jUy0gor2pHT+b$Ur zEj$e2flcVL3U-w*hY)ke1b*-QIhpB%M8*m8GHP0zX~G;^$X=OYWs^Fvl;&&@`Tu*Q?7*g{{?3Lome5HxMMo;()Pi` z_79mq9f>J$RFAF&LCd*`PZCH#5**okQ}rJNv(k|*)fvOBdCX*HIL>OK&vMA6xi^fU zCqli~dtxx#YQXAz{G4{|bJJwj)}#|bbHxm$LG}{~4w@HGYK+L9Z=O@k_Ff_w19WXP zuWx8B#>V~VMs@AIJfLzMFpL-ob86~Dea{$|-*guV?OT1?d2zXR7Y7p)lKs<^ z;-`K?$ziLRZy~LtCN8N6GwEj12dBy7b9zyvk0`+G_(9v*Lbgf z=vzI%&s%M?^up7-QUmJkY}()09xkT#OUe72$Ej{GevRu46nDB0eSTWzOf?#293GhVwoj>mz;5f_oXY8zu zkSCO1^l>64p!%c$B}dVz_T>po+C>BY)olJINd3qV7&y1`K3Vq55WiJA?*|etTw(X) zeg5Bj(8FO~R!mt+Loqz$>}TOF%SEx#-)j+SBwkUx`VMQStd3vN-Oe)iZSYS?cv?C-3dZyTv8C`Vi}}E&lc6$k|PLtuKVzDAVbzzgtV;+vi%^ zXfi!{Q6&da%G4i)naCO#;y*0rf&+$~l9HsT+#`$0R+M?YtHK}O;IWr+zUPq-1>^?8 zaFxo*$@Rp{Qk6%S%dJlS}ds8&Ai8j_V>PVEo=WV}Cz-JK2|E19; zs`)NZGgF$x*e7~C)F6B0P|v1p=UP*G|3JEOuEcBS>qXC*^#Y21N1Fa~Jo)xe+3W$~ zaahao$@X-4qpt&@n zTM?d*%C%kuisrOXt6^iSarX9=$7#l0E95&T?OUe+zl#xPt3`$6hzWB;EIWjK0>aZH z{+}3q0tZw6hsQIx*$08ZmtB2(rz+nrfPkm-Xff;OL$1`&uY8v*PhBoP0vm>2Tk9fe zWTCO{M4#a;Un{M)BV=~Yt*0gkZ}c**No*J8dXGcq|Oj6n16 z<8D>&Mfz9p4J4sM`^1usP|mMzg4fM8D9D#tXvvsI`HJ-g>(Yd<%QM!1YLq%i4OG8l z{JirlS=ftuFfafc>KF*MUNO~uWy{j$s@(a#C)H$*!+R;XxW2&~I`933!Y}|3WI$;? zd-U_Zl|fq$YVrzOp2y?K8;V^4djd0m&P4ZFVI+Tu7Uk}DCF@%~U%eaG#uw^yW>@O76;iFU~TUgulw*Z=%{ zN$`8Z`xq^Kj36Qj>WqFx8P5AQ;xwb?1cLVfFAuNyZBG5H`q+QauJEaDP2*}9>hdb( zC)C*$nr`}I@x8I`8jgnU^jn^q-x49uGCTVe_C7=#38-Ota-Lawdej$$afgbTk%A`b zBJ~#{KZiKV3PmkbLWRK)pCy?v=)lMzQ(9w9$v}l_RAxnJjMyi0jLu)h7j%LRI`aApXe4LtG0N&8XEQ<;F~!z|tBY z%$3bh*7V;S#igByreA`CZH#Nw&@~3{@PaDM^LSOf7okBO%dvuo9Pg5!@Xb`Je&!&I zFcnznj>6*oi(k06)17F`qGDMnJT(vj)y?mb6n5&!W98KO5L$UK2*+g{(<**z-=}8i ze?cS$u93tCfj?fvmE@y-1n7LDCK2kt4xoP6SyjZni7 z{u8arCpQNg*G&8QQ=`<_d1y_)4jlGaJn>?erps!^W?`&+n`&t_kl8;&{+dhpiB1iP zZjT5YqC%~jL}|5l!#$`qL883ym`=X+(DET@gE6Wtt8iGxr!0E_;$BE4pyp8bl2sz3Ic+=F`_#Q(uaBaw90 zPMMJZ+jTAFif?o5t6Q?)IxP=+QWc;ld+uU6p(&PvTPdgSKKNuByZgm zamW%pjiLTJQufIap=iH2!gCug<}Xpd|Aw^wwU}V1dlo4+YvJ|o&;(?e;$GDp7wn6H zpW8O4K)~d+QJ2Nt+RB2LLxO4Ulzkq5y_5L!aMji^>)kuH6x~)T$8=NgpUY-OQsT6} zoa_o^+FC(xYoG6K3#Jt~zPxW_-`Zu*(7WSC4i0uDn7b4cIxv&RygwD-CM%_K%nhn7 zUq5`;FME2~)k(nA#9>(FcGjrdTfFC^T1I4~$LSeq&~zFU_;!+e+sJU7L!x8zv+!%t zMdhcqOLKF&n(y8eE<7E36<`G1W1@jIj4?)hFdrQy%?+)gk6AFs2D9jb5hNcy`k(#U z)=rWqd48qNvBCHWy$r}${mXrv&uRALB%V5Wz1I2dSX6^mu8rDm2Hf#7DN1FMtLGV?pjJIzr$K4_$8lxOh$-EnMrI!HFgFlOLdl8YM zk(T<8gcK5W!iz8{`&&LV@Ym0glvAJKD$uldxfB-rue=;$@$19d=^>U9Q=&fEVoeY{ zInEUG)!hV6jt26Yk@W&bo#999WwxiwM6e6iE(c0=v6|?e>VZ4$huKX06wQ`c+(Q9G z2Gu7_Ffx`ZgS>8?_$lp-I4e7Wnl1xp4!wpa+b32}9v2VhGZ2CM#{}FT);b#;Ba5)oNu^Hp|n#4)9=V5-QR6^T=(Bqo^jM< zAGJ^U5g9!(Y2;!Tz;Jf+cY3AnbLA24WW}iYq~_}!ycY0BTh-YL<=>rv=t<8KWUV7KK8b>u5 zTqKzGbmm=Il1^HmGrX@}HhvwBb!M}4A>3GB^+uLE$ZRezr04gcpzF_)mC=;Oe%+{s zGqf4L?eFl_^g0CV;T)~4A_Zvxn87!mU#1=xJsJ>l&>Hvk-1>~iU^ou+RZcZc4LhRC zpU0)G&Oj4&h!>CqCGm?>kE|#6q}0oNTKns^A=^=-%Rqii$XO%Oo{e*TL{!!L)FW6=>AY0aHQ2D(mMKi|L1wJ$_%jZpRWP**hn)F>;O zavb60uSm6)8~wIzJIc!MSMkPE6T0-pPo8e1T^rc~{>5_Ny^S9t8jy4WFV6WJ^v;V$ zM=r$O26&w%0yZZTZbL>-yIh0M@3v{>g40$1`h9*Kv7zf7?D&!Jo7orhr3HbIKK(vl zOwZ+BXiRtaJ8LP4xRQNo^pEDZk!mkC!#@QO1P|dp^G*0I(~tE=EJ;H9{8F}T%1j^( zzT6bZzgU9eB<^0W+3|Uqxcy!v=HU%ybTBn(m}uBaS1ryvo9SmX3(sxc7VIOQ#-Sai zWSyQ%Zho9G&=CAFs{j1s8lhY;o?L!!it?}Ra-~T55{cU@Yz@iZA>YVFL5l|xuq-26)nsHMPfGk3> z!l){^6_62``D;=@LSGF26J(KH|GYoY8jK@P6{j@NlpKy=%}jhwH87(fk0w8mC0K$H zc~L)<$i+<#5MB&alf=T@uoy2ev3BJdy|9@62?`Z(P(#ZIIIbI<^PvBW!hjtrv6QQUlxv_>5(qZqp27pLWF|6^ z@X%gkshlJ~A&{#K#rYlZ`m0{u91oBq6l2l9P)TM0MmI8;$r~4@G*etJ`o9zs-=~( zTp(CiwTvHXyv(s>B8APkfL9k2FM8C~ zKon|^pS+S9`a#mnYUdI$zfwR|htw5;1`A7HwbZK%d5 zg_CJWTS+ra0~ljI{$_*{x%N@H6F!%=z) zaoj*`NdOoQ_ZJ-?aT}ZRQo+ns^iTqT=K!Gcd?*|~X@uudtp(0R%o+g#kZ@4iSZ>!a zf{uWgD&BSiE?18IH?BdWJTVP7J~|1M2*eD6VTsLA*hwPzPH@RkVA{gkIlGJ~{YS90 zh(t)|v07W|H6=eYyRW759FP#J_y17R_+l_-^fl-|+Nhq=_15go1$&@=fuvo*o+Oa9 zD>k(pgWD~3^-lX46Q;IX0qF^VvoMxgsiY8~1M)7A6W0A>^ zM7};UNS zi6Cc!yAc4z;(-J*pK{=eiZcMoGF7Y@IDs0jPwpqu`o-c2@?b#h=m;=?YLR^aht!L$ z2ogIepTL3p%BDV<=q_$UGB#2zj+IJB!CP?<^5Q*=RxQs+LmAqlp?|j-uVy``uMJI2 z&LADY3@oI|1sfX&_4RQPyS9@20N`PBj~FE<2SJuL{}8q`%H;q|?CIk-{^XcM0SWIO z0~~RHP>5B1Guov$1-8_0kf&ecX0leEPgoDaSYMH^URDp16|*M|3rDEAVaaRb(z~sO z5E2Ie9VAyLrx#jSMj+_jFsST|;&3q@M_vQEumoDC0dN4G2L5c|j{Ds_gqOBx`QrT>h8njYuql$h3M`RgLXwjIy zB9V*qa!d4LO3ao-Fu~(P?GltxvaiJ~TEw_hnXuxzlSSR|ZC}e%G^C1hfywgbpZDT* zU$UdAuPs{1H)EL|rA(}&j#L16>kXl0;nrpi((2T1Ymgr|xiM8dcPqziv(Y(o!J#_T zJDv^JG)He8sg0riXI}0pFv@!SX=Y+fSE7LsuFVEQq%c8~oQIWb#(NKr4;V`##OF)=|ak|C)_{FSv00;oY2wf-j)do&1V*JSx(Z&_eGmmozioMpFUxM38 z0b$#aK^Spk)TGUsC1V>nZ4R6fs;YR6rEZ5K1U3p&dXbI*gae(y#V7n zcc)Oo5CQg-}M>~686(1dfC4BTyG)-_xXKeroO)27)Y5i5tv z5)g2#S~AisDdBp+{Lg}<43LaV(X`!ww{)Ic+h)|tCd0bVIdUhH-u!%DtUT(RmKJzHh$*PAC<+jm*G^9Y@PTdf1jIcv*=r>Jr)Io9<|qBSRQUNTEXI-5z# zrX((7Lo(k4jt{LP zbH*^c-WL5?q5^>ZZksUjhB`S6VfG%!3Id54h#&p&B`<@jJb5!(2+P)wD(Su`<|%~* zb#W7_&}%)v1p6#VrXbLq66+bRiHVVkAGw+R_#=qB_zA=u|Zn(SBu*nHLAru6lQM`f||S9IKS>FR1|25SmRbmQ!fEPX}}0pC?ur zg25|Z{vcKgg(0vu^41G~H!a@-d8L4?5ZO*P4*Q&3u-8a@lYmG=?eZ7EmF(H^%MT;dr2P zRqW=2WwyRPqR4rf+Cnvv7@A<~=1j5fOypaK7gHt%X#2#ZJ6gl{6!W?eR2nc7)Pxn|U(xA&O+g}0wYAL6UrN<;re~7Ng;X*u3 z!nw{Q-Sn&27N(+xU5C~)<&-0Cqo4DJU2mVlF*g{|UMSEKf#2QE*PqelEnAJ6bP9}g z+thV_i%|&Uw2K_GQMOW3ddBJD+8Y|(CW2Ntjv9bH##`d=5Kj3J{u&2CD13gF=VJl8YaezDuPIV|snZ)KZnJRTQ54OK?_*1x&_mn?V6Iu0Tv(nEFL*2o4&U-SVmok ziyi}p@PW*gpTN#Rnu||mAK9j)zPP9=PfHn~9dX>mykyC^zn=lJWJ+%pO>TXTgMEoZ zavHyjsFo;Dh6L({1lfiJKMx6s2?>1>687rXk|9u5&o4Z&ciHka$p9SnDFl8jc>-yK zn#KA0$Sm<k?k3`QOe>OecKhqUwTg)e;BV*VCn)X{2E9!GqNDW5D2n1c?Su z>D!(HHK1*7@Zm;S*JBJ#n_%dV9qMKa)lA5%Hp-F^nV-a|F z&{HYk`{VnUiEFP4*b))SR49B(ZXmix^UEe%Aj){Pn-!4Im$I;zmJj>KXuJaj|T={<4%pNV_|=1;0$Q@5sS(5Xl(wJpDi znJ8cyk{DoA1OJ%48^Y9r6uACmMWkm*mfoA_HYz=Wxx)la8UVBoY|!i z+JJNo$X2I-nx2>YQp93qw;uu!Z*EQnBtK&z{DcQ}b!&V^6ynn^v5 zNiLr(OvGt2Pr!C4jXQwj6S`RZ`QA)J0O#kIvJn(ALvQAAk?b=gj^vz%G6u?q#n*H0 zbP!VhK9vqYNd~EDI@a2Tu}*z-7P!PNYwC;moZc@BU*Fx>cr2(+ zDN1ip0h0<_v)X3+;oWH*uJd9-7PQl2rG?kgto0!lwAuDtCQ}a)=S?b<@2CdwW4hV^ z;|9OUxcd>BbEp|yI0yGl!6D>17XN2q{{vrwM3fkSP>4GuXR+zD2!@o+6@w(4Rsj&8 z9SRPj3%Z<@g_SA1?Q4~(g889C&?89v z9KoO@<_Nw)Th54K0(qbU*wA=cLBQPm9T7xIZKaBEf~qQxh{cY4US5S}zXDg%jCD%> z$;Cpd75yJ!U6S&$x&<}tWk5PFrajrZk(60bwMkwNs}ex>_8W#hC)tfUZC}wiy?1L+ zCXIt2GGw!YlkE&v+$T)4mGG{tRWT5^hNCq9%QmX`6~|!k5O^NfRUyQqas_73UIvH@ z-5<5EmTxMadx_j;Zah(CV3Q}kR%JmgR4HAw{Nz5{Q80FfJBrcNP!5IiQ!6hUoG#dVW979oM+iv zHQe^Qb{80E`I5xnDM0C@3clf*;PG#Gs0aJX5YN$DXpMX{%r%86@Vi^JVg*5B@Vw|qzeYnAsxGDtI)99fayOG{keg!2CHik+FH&0lIe;;wv@L-)-Hm zuWbVZeUc)NJTvaDLL^QnYB8X5@~D>u?=|U(JSU6jG`P$JGUsN(1l3KCq)rBjV}%0b zi}jCv4$=_UT)6S_K$e*w7^@YtAfCs+@>*^}e7ytqv#_LZuzgI4bWVUoWkSDQz{@PU zc?k|_Vti}xa6UV+OF1yNd}hC=gt-MqB&k*f;mn4(&%5L+AR?%^3S#w|q#$>%@^ z#8D3b>&%|1?)?M_|H}k}n)Z#hG%gaEBM(C}0wU10W65v+B#TL!B-&lB@t+rPIfA7; z+}rOAa@5VSJk-?*2ie8iS9}pKQn%Dg$9X2QQgc1=n0YNvr%Yc?FmV=|rtA3TR`bk`GE5Kka=VlIKG4OpaS-OhgZt9kWX{D%+hS&8W6G{E?dIQWD%oF%N1DRM>Yyi5} zf#DP(bY-kTcbGV;a_EsX( z0jRHL07L5A+FZuUtJb{N5j;~HcT&N*s>@D1S;ZVD211P(3_^%)N5N%tkK`p7@zFb<(a7qfM5DTa7ZvUQ2Y3dctRwPGp$lXPENw}`A~F(Jw*%|2c?c0 zTrABlPjaPAo8NCAF3JL}IAD|TyfI9voLC(?N}GvjnQ{4}Pr+(71Vwz)HgY%OeG8f} z6%Z=;cyTCDmEkX+iIg%y>_JlewX3xP4Enb!9}owiY2n%Izp0g@ZTIY9!B1ZOzBrca zYv^--Q%G>;|!S(4x(@|=%7`-E!f;7qD3XoEM89iCV zFL71`ms)DH5o@x%JTo1ZT4{eN_S)0?+;U%P^}~@U-mGs@WnVqWvQ??^?~I+1EeZh5 z{L#W)fZ(5DSxNR0f7itSlx&Wfp^Ym=hiXQi{7(g=4!3wr9lb(;=Qmb=8KTrHL+HDE zq?DNaubzm~k3xV=kb1;+6ezdw((uMf@jeMC;{r zSHS2!AZXLOPrYX9{6q3Me+4!mze7AH z3$_2Rk6he>kQN=L{f8Q3r+9pJ_`ZD|FAan)-B6Q=_Vt(st?eq5%69xv zIFoawd{lqeZH)pk7M)?Xf0XSA5?tI2T_Y2~Bngj;5U){lj+3KH#v_<)@MvUFHo?+0;P8e z-c-97Yy~&mBTy;$c5^x=K}Fk|U9H!bAKlCzyNN5t6>iiEgaq~rk}z*47{IB5NL&@? zfLR%)>6WJA-eHBuq#)-T`_MqG-ik66jbC{;M_LNN*TRP60<71tY)(c^%7@uT5FA>i z{LJwNLh*)f3Ctr1{FYL!@&)}i^k@DJhqSh>Xzj|fAe5j(2Cn3cT_h6wkNia80P$`t zBpz2GGr0&taDA;#NAF0(#wv;$n()E`aLhAmZ&S1xhTtj%!(KJlZ>^ldCC^~~ZmUEX zxg;4YVUk9mnSGz!m)={6dqQe5{!kBYMpj|uO|s*2Z^j@P4vG!=)IupC3i>*<>@_G^ z0Z(b7cWkL*YY7(#-F95`vISJDt|F~}OemKVXoPb=9)<>hAQ(Os00RUgq>Ga&#pIEZ z7o<_j=2OqPn;;3@!S7J#GgBX^2`QIdHK)rVy%``Iu?e;D`Z_)6ges-z=>BshHC{ zu>XK}_wwvovk(Cj zDAf~GC2mv7SJp^K0^w2OJu3IAVKRN_I z^#DJPV~Wx9+6oE-JrpVl6CbPx+y-|%ZdGFkV6G0;6vi-_t1&5qT0V7`RV@zG$PtD- zm-OWNQX8dGv*nK0iNx$W7TDCsn#+M9oybIq)0iZJUWu(62g!8gBg;0MRHmCwq2Q+B zGjw#zuV;b{Anb6$D|OQR=VGfpEcs(BY~=BTf>7N9K6}J>e@uir9S0m)5l87{CS`;U zX}P;QhZ`J5R=Sp~+wrSVE{(dDM(0O8oPLaCGEOTrKZ>LOQH={Jn>o-Z9%8ScIdd&d z1P>@!xG|O5u_^7%Km`sqBtZm7Yx_OIBpW4vE*C&r#b+d!Q*ip=oGPfE!!z2^vDPEg z2n|mp_@dy!6;r?J;ZV@*d*%?$Q`+*SvXsk>uDZ_0ZDLegmW3PD^T~r{ge<(;Uc@sq zvl=^Zik=WxrhYCwVV$68687i6Vy>vO{GUu-0`@AKG+c5e0|lzsA?HdzJrj)N2$#8V z2j__PF9JKBWj6(7|4s+qgTG+G7!Ps@L2+_vad<(C5~uQ#={bnmrXi7LyfW2eW{Pa@ z`Yg$pNao+9O!1jVFlA#oj|YX)L{TYGpK5rLMy!=K zh17tedx|2&*4JH6KC+schlo1;!p=sCT52FeoN>;kf~nYE5dBNE|TuJb!?0ByNlev`4;TPiSaUJn%*3>L6PaoTl)=Z>AB+Q@ddh z%;ZFCfb=L-SsjcicRLqI3WT*D}|Lg8#$apRBn zZK?XkOJj@IG-N1l|gp`^zPa3}d%p9dPnlVYu`z z9B1tojyy}{R&b}dmqR$9+OLGi)T=&Xj&}<4LhhKH#lV{N?rvXLd(sNQc%GWxb|a4S zbUfthYsOTBR7w~|3g-ag)B)CD*(LnkE zuHcf!?Kpf~ey>;++cx981URlaAo&mPyx^)?17D2!Ht`6P3VE)`IvtHg1ZMHzvLD6U z)mgp4IvP(&4WN>Tc3i<4HH>X5nBt)pmH968bGrY&c6; zut=?c8`0)1_4*RRI>3>N@&NQ!0|Pms+S)|lweyb^8GV>vTn@T!%3052QHDtdM*4t*CY%BT!I-~x2BsTfax@}cvBHGZ3PsDVPE^7YS3+s zTtFEwLS3Z||B*;-5|*DU6g;T+*XbQPz~4_qtshrF4Med9dSY_S$@P>IH_AvWJmebL z^^zqr_*&$}*p=eJ&yb%#U-D4$V2EnJ9gd*sjzsro6xqi2gy(T-J2p`IE(mx>WbkI^+6WK zX!=K4M=IWiy0%Si%Yd#73?Kzv?q#tIM{&5Rk5~D?$N!ptPHB*LpPoEo;q_3^sCJA6 zitP{|QS^!uzi8uiM-Xq;nb-h@NoClz6av2lyhh$hrKuF(2dGkwLTNomAV4uq(|w?J=BK+K&D}@6tufqf z60!+T-bC3tw?uF+6~dd$g9_ZuG;2k3N%m=$WvSc^?n~}Jy#r8@S@+@%a}8yeeCn#l z5`SlY+4(6z+V4$Am%j{emuh0*2_7V*SCoQ9SG(i~;nk6KQO9B}(7oaYwJ z`J*m`E6vE{xA8D4mM7XQdP?Bs+i^nh;{6n3<8Y$fAcXathZ7-2DT8I{sW2}!xdjK@ zJOFcs6S0T4kAHv6pOEA#CBg%5Z@%E&dH3=9H$0fDx#h(igY;nxN5Qg1{3~FBJ@~gN zwo`em9L4nHSJm^S zxM*gxNYV~<^nncK9C2(=LS8C~sF7WdA|{>`H`{9kB8SrBxnfz>qve|R)BZ*okd}`v zQQc}7OMI}C^^b#%nrBUR+vK5*+nK;Y5YZWNbj>H0^wpPSA()FzRJRlCzh4(KmSk#`(N&BmxkWVeR>jvoGLOXYEj)wj}kXeV1Kn| zqk!L|bOcazYUI6Aj;Kt8kB^Y8qy0nYp6|TA)!PWz-pVRhTaV2G<{HEvz&y&qfbp|J zsgE)c=`I@+(UkUmi^Za0c6;NZPz#Tip#n*@DR8z4CP1FZ(3`IZ;NtC36{gD{hCs*h zIvFgVm*E(sx41isiw<^Ri(u8zI%ni)kwjE6(_l{yv1J82((<)oTd3;yV)r-oj1O}F zL?#m3rg#it@d5%k`%9|4nr8s~#7>C;?7$)noY_Y{&^eg5{R*+TuQBMocu`7u{`4yt zr&dtr_k;6OXW=kHex#D4IvgbS>q|elE8>z0d8jq`?8AYXD1pdH`I3Aoz^r+khH}C< zd)=<^MYhEC(ExosodRZYM19e%wa70E>v#dA3WWEyIHusP62C;7z$47mk#meuJ$7*r zQ)v2PNswv-kCY8vg*7HZ4Oz4{o<$p3zIZ@c;V1jUPQTDD$kL{E~$cGlHB^5IJ9L>kBx9E0i+ zm9Zy)#LgZ;xbkYrY1K+0B>jXq3ws4-Qr#dVaB&wdY;7P_;-dEki-@T+hQaG>Hd=vM zuF5fbLujoNMzZRO^|1D;PRz?dA9-M9l00gTm{tFLs!@u_@oiru+?eEgPkDVskQJ{2 z3uzuDGzfHL*C@0pn|T%Hx%tJ!LX~66bP-VZnLFTAD#0uUR3|kN{9OovYWbd@j*bZh z*XI55!n>B~H!<;~2XQ&|w~^HS_=iyk=%7xe4|99kHEX}$6^>;(bo0kGfq0!LYfRwt zE!Xdr-&=(bqnppn9%_D~`>`GKlDmCs;!pDady=pM+}|f=R?k`L*28*SfugeclqYT%dbd@bm=J`P9iik0BP8lyW{|3}8-}IxC^9uZ4rX;9BvoihW|KqE zhBKDa;fq0~e|h-Fa~{yeur&4PA`+O`s1eD>VJgek1O{59>Ezi_{>Ai(R9N)al;IKf za%EDis&Qx-VVFXxK;l~^lu>E2N$uKJ1=^w4OD7JBl4pFW5e1C&1L$=x1g$U)nnCoM z&0xO^`oFZx=>V{3dj+pNo?L6HZ@G#-ullo^v0ue#?5I1aWaOiiP2&h9xT}B4E}*K=}pBc%f!X zV%b~=c_~btv}}d+#CA0mwg7@KH@kvS!7A0u9oSu9H2A+&BCsavvf~fHOZp)lxhy_-A45rOOb5s71Xs zokZ#ovoK4#&$+c!X<7Q)9jQ%#j4Y-nbf4=06#dV<247upWHe2bjUbcD)@8g!3vmjpaRZnzfG z5Pnu4dX@MqTZX_|Th2k|vH@ZRk7M^kpDtDQPNqKNUus#I8M|yq`n2UB$yG5Q6LMpy zDu6HD#VlDAjcr3YL#Babty==tlF=q9mU>5K^jh3W|4nug65Cm)>{a6j z@@b8C41|^vs%u5Uo%KD|lqzh$)(oH{ zbe3f;yxR8Br|Bitqkz@@+Ju>J+iR*Xj{5f->mPl)-cx<`$dOp!kt)J=u_Sgd?;ZG7 z^s~Dz6t5unezZi|e~5v)JTY|H0( z8m}0?yNrj~BZX{$oHIFK&N)P)5h;1}LjbXuFU?ffN7c})<8jy*MIn?$HGIMs2 zkcQ!1kW_$;+hv>S#=5RXtRI%zPQkM=eS+n4w|1w0_*OL~v*SXqXTFxzJ%NcRUx6fzZK~ItkvzO&)v)IV;t;KM5+nTMzNit ztbn0^EO@rg?Es$@5UF@~^artw;jF~XK-&XG#%76!6aY@@q3Fe?7Vn>PJmk&u_RKw0 zxp`m{7SQm*OmZ0YNs>SyJtf!@n*jKX-~wcRyeY0F>68k5?*7qVh9uk_qe}+DF$N}T zhP-(GkUJu)(2rRU!2T5mYldUiVn8GmZ^X;MfBMP7(Mb;QVorEwIrl7eFBn&waU^ze zyr4W6^EgagI6z(~`aY^kC`8!hnzwYb@d9`K0#C$U@j(lZ%Uux}QpH#q!HRtep?BX+ z*ChC<6F_N=Ig>5u%F6}CcW@)8sQaB+wfeFC$@@&-A<|sH zb}s=x6cdiyFr0^VbcgkWYAD|GQD%kX`pBa+dUUs^QO@8~t!UnnM$s(cGiB}&y|o;) zqhko2dea;R7n#1#W_cY$Ky@*QZWiItmd96=xX%@VDY zNd(Kx3XX3uMcr6}JIGAi!KqGo^@jY4Q81ms_%qA*3T9NDw^lZ^nTw*{=qz|wBHetf zLko_L3$)wXy~R50HR+67I@BXAmXmzUD@|+^9EM;S4l+-Hw~H)GoHD_1(hUTq!!GHh zC6Q7oFNG&96vjCr#}nmAxuBy!?a2+{Linuy5xY5TLfgGvg5n%Q(>YGq(!LTH?;=jnCE^K&jfu!sc z8+rX;v}9h6fzmxVOs^raJVU$zR*7q`fXr?aTgPDXg-i5!pcL;oUd%V}oP5AImBTVSIB@lh(it2w06 zVR}(1%^P_{ukhVVv8qjR#)~2QDXvTj4gIcinfD{XRD63`@o78cBtz{`K~8yD4ymh( z%usW=QDs;L1i}9ch>CoG=kV_&J_uCz-Y9r^OWrL@4kaPxz0uTZH3niezIZD`zRQ(d z>vXl#5fUkW8`P$Iro$$LqZSJOo6rid*2*W8mlaaxxv7E*Y2eW1pXp+CL1EO^+~po6 zn0T??aCAuvLf?I#_xq*XyJPV?;Eu}qePt{L1>mz zN2ILnYI~#lxD7?8@0L5ZZ0)xS zh&!fLu%T<#6EQ-wU$Hd?GB^!A^9#+^ogxx-lc8ny6?Td+AJcO-9<)XtI{(b3+W-vXzz3s?}vUn^6~u&A4Lpu@~D1PoIgv<}t9-H_03ww#NBE3p(mtOZ;j{;_en zTx(fg0(&v*O(((lc1jnF!%-&FBQdDu>}@03-BCO*-slfWp^gw zRk02cZ)V>6~Ic6_cGI_z*v^*(|8gUeVI>x$>$Ct$ePo!UI5aCwY;Vy z>^?M&WDreq1MDxJ8^J@`xnA*sM^?+|ZE}56WJ^ihM)Z$$I$=mAiA>Org8=uhkpjgk z=hz~@`X&vfJpHb`Y0DC;CW0GFrOko9<}+$?LEIkzJNwo2A~E-ayv8p^VTqVEc}@&l z;<${!MzQ4C#TqF=5vSx(N*lo}T%qcVh-RG4K8?NjR*dSyE~Z{Cehs!PogKVLL0k+V zTS879#b`pmQsOc(7a_%TYWE*Xiq`zeT8ikV@5POltBH-uc(p3bM^-iIPX6y%6C?Mq z4G=i(#o~(SP3khOn%OFa=CiKf~{+@)DD6{^kEm0PyFUk(P|NKmXm!azyl|p*=-E6kq?2Ni)*^ent)mvRb#n@*?2J75m3;z`U|uaNsr+<&W4SlWFVs%3WnFJ0qquse-Png>Ek|o8@S&bt1>BiZ0<|^pNKfE2^ zl^hki0|QLwAunL&lC%23l;$h}arCIkl4G0^C1gPZD7&K|+LhxVbzaliAzeS_!kmnC zMh&X5bdv#fXWC?zj`XPx3l7Ws;5plb&RWWar&2@4oGqfk9~s&6%KR68sKspDVdu}z z5#o4?sH>aNFZEdzt{qIXr%!JrPGjtCLRzMTff5p~^ocVSHAiGWE|@-6N{mW%zMkI- znpx}CV3I`Hkd(E9$gHcl&epok)f!|Es_j(W?Mg^8HAbPG{+B5q^-pgtyW{INMy$V#xrKgj{5JIGVzloZ{q;qa`euym zXS=?OT@4@k%Z1UoHe&NDme*GtpRRalt_94mMP6S^e7b&u-2K&eE&uu(1MV3eL6KVzu>9* z`nKqL^WHGm9sGv)$d*PkapATi#LPVRi!uIT^rt%s$~z#1qK;>;l}um3dAJQD9nm6~_pl&f-NCgaV!GB7BrI3uZc=|C(A zhfIN}_P#q9-%6ZfiDl0j>ng`~Nl z*_NO+(I_0+hy~u~f6mDyF6o|q7~1#NEYEJ!d+n$VC1|oUEWdM3jy&gjDg7HO_I#m^B=?sbI?Ge>cPRWK za6F%k+l3UCke!TZOyGPOIvH?SHXd#_JfuSTN>o%IXO6HehTIwM^Qe*&%78_rBiQmm zf#0&4E=DU05G`I^ofad~A6YR-?vz*(D^a0#V;RekeJg`0Xt`qQyY0wg@=U7a#OArO zLR>*6A0Hcm$CKX*=Gu!0!-C8YruG?GMn*9eYE$w71+KO-inh6SAs-IYoLj{{7WxyD zMKZ1wcwv!f7dG3!eu*Yt{W{d@U;)CWd$nRFh0I-2EYHY;VIag$b_Q6_Er=5ZiUZ~$ z62Jm26v4(5i$N0>xu{fDbAE59+6r93A?|mX8v``gjF&}nTnAQa4tTFFoRmKU26VEA z!g?xH-dqxLxsTZD9~Wq(lbIkE%qFOmy)L{(;HoZgAbT2!Jj*j|KjwH7fX+bm~5U&hqvG3r$nt!iOAwbpp>+AjiPVw)!;) zv1BZG%s~Z{djiC7L!I=->>TIohp+lyJrTUO-huEdrK?MN>i%on4pN)TN5eFT<7oLqudz^19o=bb+p11i?#3lI z*Tq%<+JKVK>GTy?&v(B;PQUYkM??I3wAtR>0Pbh|@@{J-JB3>l<3X9!)@iTlD8e1(+1ejJ5ecAB|=( zl`%ST8!sYUGfI@b{beabQboJ2iwFYfLB@nA?BB?gV6XkdSykrawCeTUG*TZ=Gi7j7 z{0DxcrN0k5v6e}9P~@a#q{B3<4&_)HhlCU1N`^O5%SQ@9EG^rE4=3PMH2TTxB^eC+6nEuO zv`92eQA+OstfW;b5&23f+yLoCn~skrR^AgAHV+_N!B7*?TTqX;c$z<$h{O2l*;F&C zYZm-2dsTNrQ>f-5OL@VD(e^~rIPgnRO(Ye2RJZ0@hfWT_)tK{Md5&c7s`Rz@W@ofR zYLvV?9lpzQNH5udFL$&B=&o8;v$Z)nFO!bd^x4bd{fd*M(@KsP2MS)Ka*y<;s%O3A z(vC)xE)&kx`gzMf^EvKCJ4aBl%4De6G?1)q2jiuu6LQ@=1bPb)nv8yX#B! zN?dhDA0}5jd%rihCvoTp<*b&35;YX*)|y-g=(T+P-V};cZ-K75Hb_3x9E)mT3Y%IR z75~wanpAJYqPjk5^rID-J5z5bGPOP%_@k{Dr@=u{bz?E_M|-t;gVPXfWrlmz_pj_k zJ=M*PjUSz^LI|kLLbCtG9;EaI*S1;I<{tUa?r|bK9AE83k<$_+J-sB+cDjme)?cBQMQiXeg`}&QKa$HWEWre{12Wax zmtyHhk+#FizwHC@e#e2EeofE$nOMPTubi-@)@QCOCD$-Z(uu)lG+7(GHN z2%C9iWCVRgY`}REYS|jv%QLS7MxGvgAS)u0S(i>H79S3xPYr_(L;1Q|rW0$yQrP%0 zT5GqcQppR6PB%6Z8)5`BC~2`uHuNMEzvg2r&udf=VE5`$>@&bky>3*N@pr3dZ}JoY zA*g8jV7U=GHKbsVoRG${qej=d%9sYDAe5n!GQ7!Pjt|9ac6-7;El(C{EEO>;r zQ%|o^y@8X0wv2?QxxLXp3!H{Y2N+fGHK@-&aWcN&h&T)r7v_7<=x{&ETNgX+q#zKS z$!;(4d40~w{k$|xt~>?n)u5YgB7n2Ry57={N0%rKmUxb+NEhMb=xDL#x3OU|&U%ad zh6%z%v)DQ?Yb)#b9KhD0*d(3n9_{$`T0wV@0zQr?_n;h$4d(VPWhgw=s_K1Bc2B~? zHEzn)MJzeL4|z#T>ufgb7)vWzaaEbQ_p4tth3McIGlg=9@8lbzEZAH1@qk-c<9n^K zlm$JHeQTd92y8bNChV96VK%6Xe_za77%yf@SyBJz_Z;|1b2OVp*m*O_F3fceW8=j$ zeB(#K*_*Oug~=09tGjLRkX;a9`YRk`mgy|$^4AKB=H29n$n9=}aA|rvPxmqvQOU3S zG$>-@GSpZ0ETSN>x7Z39!e@ydO4gia{NpYcWq8kk9Zxzh>z13$CX&4cUU4xZ{a;fA z(+&DDu2j1I`TE!3!ZQ{ruWoO>}G` z$un=K;Gk&hs+Vj4 z^+h>#D@HbEzhEk&umwT&XErZBD{5d?`rD0}t{>oSmQ#Ps31{#2V9`Q-fI?WBQM3Ul zaW{;XKtVoReDG8DXYCO%`<`wO}S9qn!s_Kt8p z|EO4EP+=%;5-pgL7tDP4%(ucHwFSihj#hSobL6GH6c*)n8*Ok4;!8zB4leL6wfMg< zMoW3ch>A@mjY}7e%QT3~_K(ZWjmvXz zW$#BXpw(w}M~i7DVQNOb?})3;jdv483=QI6+G2gNz*}_3xcAfI3O84|0co>=S7R-ja3iB_}QFDfJ(s942D$eFkYbZuo@0I zB<_hOt3@OZTFK*BnhuOZg?F$L-Mzjlm?nMD=1;}^+#H=ulzh;gl6xW}a1hD33ycKB zPQY=8tU&4b0NNkYtgA$S`o%}nP`~k_%r`@7!_A-LlW3O!-!CA;?`g&O7ueZu=|C~e_o={_ zaGc3Sd+j4y*y@wG1+T3`M|1oR*Gnb8Cje?CJ~U|JIeQu?RV#GMnlMtC ziS~whSvG%7IW8>D@2Mv!e7*3~1b!zPv94H(qyudD8Bc;X%Bnl0L;%&_!q&nT_;2t zC7l-uTt+X*v!+-l6@$`+{?@{1icxUVRp#;!;uTCkw1Hu(sDj8<4IOitGmAyzqnKZX zqP3K@=|iP431mU&ZXS3Wa7?d=y*qhp1?skegklMlS}8(xFjVz?cp8C`aMY>|R;>Ff zpw~sx*LH2na(Txq-QC41myPPZSjoMC7&$&h3B!Vl1HlRzq03Xj7iU1(7@CeD*yOVz7S1dP!6v&7@NM2P~q1|4fG77a2 zy`ib!YbBhTW2{YkH`JB>U zD~F#?NEQTrNo7b`3u+tGS(w`CYn1Xcx1Ee6TVw-z_?`4bhzu*hDmu+7 zZxIs5=X*R(5@KlT7 zT1)rX3x_j^4>D9N+V-Q{;j1~khS=&TVCvf0vV_{2{))&7#m#`C=Us$1&^r(W+X4A$ zQPa`9wE4UfhQz0bq|Zeeo!_AVv(6--67yi71=!+IS#hD)Rbmh>3{{dGu~dt*Cd(c=Xxrt(gpBkf7w(o3L?-cgW=#VP#0?^047(MueWKj}g@aqS(_qUCd}qlad9 zX6LPD(*%Ny3_s7ua(v3D#Svt`{-{qs0Z5Nll)$w?3)&=75$||-BD{uhEQ^4`v z@U>qZ>aph<;uF?$l5oy}E*ltf6{`sZ1gu{vB)UoN3pA{{VS9Ld!W6si#2WH^9{2|8 zCxn=VLtCej&e?;aiY8;~CH};wP|b6<$~!EMOBDK2RD)tvP&V3)i+}>B6*boq$&xoe zr90$lLX->6a|^7AVr^);0rmBx@6Mxs zfTK^OqkN^sthZWsKft6VTJIqPkOV`gFTkangmYw@&)%6ffQt>)SDATJ_=)f-2=Q69 zZ2MY~b2jP|gfR%7tkszZIQV>u=pgt>n_nDbfb)mo?_y975kH?X)9|=H`q(Y2u;oA6 zYBn}(id(K|yM_M8+rc;=Q7qTADOa8RT1{N02l}^;A)_Za<@Z#8PGM+|9*lkmYhnkR z6N)(j?L<-mfQ?&V{E8b{=>8ojloB98izdqpyT5DfcxQE!nM<}H_PeuWkwmF3{Dog; z(j#8nEwrSeT5;vY7Fp!#9#!Ci$cu0}rRZf(nzF!p5`_lq~A-q?+? z%XMfqfYR*|U50%j@n!N4=ivp-AK)oWLME>^DR$?+s_$33LUaF=9%pJ?jcH)tV&dk$ z%zt}2=v{%dHwP(&mXnpQY#)%_XfU&?>1Z4t5n}ddzO>x^tcJV2B#cwuf-^T-K_Y=4 z|L)zvn=@1d$~ZONWXSmhZWzIMSkNWtnN~;?ye&!=uk8?JE|p`hJ&cg_1bg7iZX=2W zy!^_dJinLNyBIf=n!r3hJk@QbKcY$%A76wN#+vo%`F_Ncv{@;SDil~`!zMD~P$Md3 z%oj0UBv<`Ta5v(V^OF93mq6464OX>K*aYEwXl`&M^=1SbK1wFMFPAE@;p&Q`Z1NS=c9yedOz|Xx+}avtm7B!76j-OE zvS#$w_n5HpH>n-<`sRA_?wB#OhBcTsCYs4w)`spW26$dR1T;Sb-~SB$^fLtcGnD33 zds8L?uWoFlwv_x9)9BW7q*km#-%3EQfr)#GLDN*|E|T<_nQeajr|%huw=->&>dsGN z0`D3FDsKt1O(DWuJcL@Dbn8Vz|N?>8{_IKKZH zB)^vsy3ZQx9(`K&LS7rCH{GbFD3kHGZ~8<1%`E!od(Hq|?R}M2u~VL!%8%ce`kI<2 z-#016Po(f0Ujp7q|a@Hu&dPq);PS2f$Kh2Of?JLE~lR1q?=D!?<0# zQ%hvyaEbJWbN@OnrIHE(lj$5Nr8B7)$-UA!RLSNt^^|k~mC8gA+T*t8KCPUMCA0q6 z?tf`FSO{k#kT+Up(aD$1z}y}eJXt8xDwGa3YPXvYXKa&}SL!HPu2sn~dhtr{i&3ZR zc3%{QzT0@KT>e#g+|%>b>cFOu@5X$*JN@>yzo@3$oyT&u7>J{lk)+2~Rbo}i)4x8r z*uIrudwDLVf8(%Nq5fKdtBcogEW^?UFmBgkQ(H%Ub()#A6ZnpE@p-x$5R@7J%e)H!V zTLhB}i&qXvQ##*>GsWOC2+}S{7Du-)Z&9Flp7}-W)aV9xgz{cBxsx!hZmRF6Zae27 z)n^Lrt}C<1Z{yS5rQE%Qrja@-fY(DUsyy$(UJ~Ez0?p1GKQ(s zz&b@<*w8k|w$sr5b(FA?V^u|`k#p0C@G9XChkbBj=a(PGZtrF~O}yshy9CK(dXC8z zBmFH)d^TT*d;}c%kbLz1dDvzCQdyi1L%CM$n3(%5j{i;vCNgUt!Dgfi z9)(~Q`1~wXEf2nWY|33*kC4Qo$AeGjdG`lNP(c6PR^dm>3I1Vu=wJOJdhvQ70v*CG zH?Bp8h{m8Ij&#r}g~thX(P)dnxHI>L|n4~V7yYN&_*DF z_Mfm%$sDn_?OBzx(6Oeitp;g#KDx3@4TwiV&_SCz(t~#GSzZzxTgPV zruN^~{0AdBitCp*(H1ka2uUG*s#{tWlUV6ruBD^fgBy#BSGLBqUVT-VOu{mFpgPpH zC_&2{epL7LrW!(2c=SchP?@GqG<{JRwH6u(Z&T@YE>wUq|xa z&UwQ}YheJ`<9sXzpyZM&rS80sz`)1kG~g{6io!-0SHs}jlZ(Q~F?-NY+f_&=V{*gZ zks;|IVb<6g7(28E5XK%!i49mS3)6CIa%z*D%Hue+m~{v9SwBA9P@P8Q-(8eLm%J%@ zu*``(9S?g_|0;Y1%l0XZo!xf-gW|zR6i;j_1^V#0T)R-ctBm9Kb6idvg%@>atGjVD zOXpnsi|#g|LoB!)X49ho62Ywh?qaR~-7iOzDKs61G13o|;+eGbM70l|=Et!cmS1&! z8IjK*Kj}_DeH`aBEOnI!e*!D`e>=a+-<6pE@0BRP_(^fDkLHS6U^LE>9F>YQdBUuG zdDXy~EL<21_hk9$*~n0#5DCbKDRXbS_({?gCuYmOViJFe&c8-H?|+QOU(qq_KOT*e zB~dD7xdZ+mdkhoWo8AQuB?*UPFec4BhnCjEQ5c+zT^o zcdH30Gq*Kb4)O0Ju@a9_U}+Q*I~C5N@fIE+Y_pk2^RKZf3tkv=(BU%4tWBPjKviNU~?L%9X+M-56U0opchZ zMC?Xt@vZ-5B>t`Q{@a3>e^~sNSEu|B7DGx!y-uPau@KbKVi?bATmmT)BH1!rIF>>u zpO=fDn3o(*r(RIq+BB;e#-?6qVJb zCT4&2`I`4Af$U8uu;WQyeZ|52;{ZF5up zcAJd<#cjHHQA>kH&6Kvf=c9QPZi-HLThhwnV1^%OLOUI8Rl}$ieotP+kVCT>)pvOH zk4FfCO+wAE0U4(0u*V4+=&>Zr^3tG1yiA4vjEP;ha_800^20c$yEE)yM7eoMHaS#` z3gi2FQ$Cp@_e`C)m3%bUzdF*Zza8oSN1?IwZ=+uR55DsMTzMb8`+teoRH^?JuhTVQ zi0c)$KwbH&0Ewr1i(~A`<-RR8GPeaVPDa()qW?nl%#2(&zB~*FLU^<4CUPh4oAJ}bL zY_AfJOl|r4~^1Y`GiHGO*jAFV_GwqgG-7PLS1(SN%41Uzy; zH$he)00#2Ec9Q?MeX2@(s8`!s55)N}2QYc} zk6fEK`QR3MqTJHO$D|uGVr*lNVRCN%&At~UPb=q3R3b15Rl17R!kjwLnx{r~b_c>` z&*Y7tef=<%E8zTb`if^KmM3}q9xd`)|9foH6HFEdwbD`i{62bnynLpNG)OeHDfv+x z`~4Zbvp4nDed>M0>-ily)v7iYz&B>sz^nbRiKwT;kM9`Gx-!`&k(I8-9)LOK@cvRX zlGeZ>VIj_P1_c%T%MM3$$kQFk%C;7KAaK@jUW9>|}(g3@NCbaWqv+hjW)y&`?J6V7#T z`TXjuDMIDdGZaFDKNX1eM0!DtOv#m?xbxlgQ2ei{o|Z%p zH~8?f6b13qx|UgTKvTK*jd63=QQ$^m5Tw1<@C(pDS$w7&Vjo7edp4=nH2HMX6XvSz!6$#W1b7zg&lyB;}Wyk^86}PHei}byd9fcyY>iEC39%dYN_Z69wNAvP66G85 zf(K)+3XGhTjFJ~+|DE`R;WA9LGmeyrB2>?%m1X^DvL6er{&Tzt5pf9DB!6A1_hWtD zXPHKXUpd!+l+PzFRp>ru1dOJILp!O+`dk^ajl zAKxL0?mr-j6GWw>W`YqLniBZA2?M@csw~6X17a##Ao*}N6z zTedIJqSJJCl6&dI2ZN-bArx`sMsY-O;s}}e;{zOT;4tuv$=G7z8Gcz=t?7+bDAnzd zWIB|DW`YGHy?Rv4Z6FG^V?loT3thSoO0x1GOOsFfEJx!)PWSXt=e8NhA9x80Rd)|B zTA;}gFAaMlISZCPnS|I`$j8`vB=}A5Q=5l^WGjJb@szQ&`3JmC1E{>-oR&T+{^2;f zRteP`HrJ~_FqOXr;knWvB)d3y)`KYkbV+Z#6)q}u#RnL0S8&uAP7Z5Mmv)ORv+}eK zCyCs78R5Y!Xj(c6#7v9j@hD|Q_owFZjZD*jC?xKA#6_PMLwNfcARq7nwU0Ntz>5+- zP=B~?^FBoU_CUPpku+?$g>G^cJb<6kEk_#;OW0p7ks+C!PaTI7tUD+QkC)WIs#&O~ z4Tk6Z0m(YFOvRvG(+=<;kv`)rSkfWmiQZYtnEY5i)#?7fh`wt0)IN&7PJwM1WLY!W zNZ;qe%YW4~U|5vYq_0eZx9H?)Vr0PXv3r)~Sa21?-o>#Rc&4hW)3fgxs7JeFwazvZ*a;uCmxTegFntUM+ zIarSt#A&5NB{^gs_kka$+ul_3xnaD$%>4Ii#zEO;;ye zms1T)E@u#+OZls#jDebUoyKX2&c)7uuBt};Fk-3qaJ-0E-%kUhBqo|(%Vc5hxeLOwiAY#6MYs#FBZW`qLN?RiO~&O56FG{XeYu3(*!AqU#Zun( z(vmB+R{|dwG-s?v79DRzvoGm@nqru-T%wQ~Fu@#?F>Rk*sZo9C7y!NYh4BwGQFWdP zlPZiK)3tV!p?%&37lvYX|Fkp0dj&4Z> z1f)w^Qcx)o5P2vS5aq}7eBQtBpRk>so%6cy>%J}usx#S{Tf&Cw?cHQudwdt2GNIr? zNz|Kr;zTcQ_H<52)u=cm*Z;sG1Izr51ciqDCE!$Bwa`+yyiJBcA|k(x^LIAAGL5ICk%l-HcGdO#i8DyXKk-OfdSuTfK`B#@Imn#(_#l*evR zAf>VWpJ!lGh4+P2f#E*Sm;oB$uo1DiKm$%PAXItZF!`!)=A#V)**Q*L{;YQ0w2)9{UgM89H}6LCrk=u*#YQXPS_p|w4D`w zWukt2b1|byXu>zn_VISV{Oe0HAY+@}cn?j%y_p!wUH0YeB1nEq91=oaVLE`K`qf97 zZ^H2pkNcFf{U>eG(Gw|0HXvl1^RD@0Nz76;MC#L zv;aN~D9H0N!U1R+f!tH)93#5_l|~9e_WKd6R6YFn({Mf?l0^>8-z^#rzKF+uia9YoUtlkn z^WijODaoJ(l|s;*VjcvHzx*Na0sC*wQ3&+}Fg(84fw4*sLaCiF*qk8I9^XUi@k}N` z4#+li0lck^62lCGUmFlU<>cK=z?VhQN}y=xtic$GlH5CplomX zpgHxbpN7o_V#f=S*ZLPg+8`Er;2=l?O}i_>n?0OHC7z}TO@%R%(cQ?iFm|RZjWgc< zd_UQXO!|=a!_(X0aXcH2O>;0^U21(Wjp1d65M1H^ASL7A3^{xzH8j)yG*g=~38I!R zf0C}|My=8LfObCp)|Rd;E=HLe|9CuIOFkPoZs+5c9nzT{wwnF?G8@B~6Ct02waJOT zr2Cg&LM18nXXZi~xxz&tsX6hTjHx!+c8GL_n%w-&oS4hpl30xqsKmb%Qwg2n39YF( zo&07lLq8niKqrvmM?RW6XqH#i$=F)sb5NZ|&&m7BH9wg@8*fuEV#C`fuRm6nRNK$g z$eD@U%%e}`@zrIyMVt1V@+X4xc*WViM&uGgA57iYQ$?j{g69Yx_KChygDl6Yh+gIo z1hcGy3i5+lOU&aO5rw0j#n)ikSB@S;sXQAtj2@TJg>eYyDUdo#!O;c+2;pR&=VUh{ z$q5Ep*;9I*5o`42OpKHJV$!2hXvy-(YW&4=8H*g7$j^!?hNuZMVESmDPPPe$G^k<%)0%m61w_W;-#c z@dt{SSnIY`dR5@2B)jn znT)Sfux|<&F;93P27W$EU9>^+E>l2iROT_E>;a)#BaA_?T#aQ%%|KS2ps9w+LhXgD z(4xQcYO8V^iCM@2&5VpFu?#A4+l(?#Yki*TLkSt~J2=y>-T+z@7_+cs>4I^^uiWT(B1;HqijVKMX?t)N|HpPw zsaqjeptn6ST!l4A4Fvf8GQ5uvHY}>TfFJ5TFgrFrhB;3Clyc zOer*r!#M=$0m45W#fcwh5rU1!+Vd0x=Qf=jYc%_%XnFdYihO~N+UNm#dC603+;1H% zt>Pk^`19b#RMW-}H@2PGioKG0Ebp$2jchr%d4xrMH8nPGU-i^X;HF52t{O#-`rn3h zqpD>sk9gIBGtGPsLcNOf9c52E=Ui^dMXyDf2wo0g(}v%2DBIVydSJij&WJn1se!~# z1AZD-q{-t@1+s07+=~xJY6Sf>VLhw)jds6VHGU6xtPUt?4x+!c;`DkIlxj){g`OQV zh|y_vMX-M3f*eJ<0Kd`v%&#}Gy-lt7zgZ4lv;vRBJVTa*O~yPW#D?Bs(2GKbIgLrh zjRvLkR}$s4rkn_=9@6A7s2AvH6FL4xKu>s%BioDPB(akNhM zSrOjq1G~YZ$IfW>YKpQrXnQ7;BMrtRJjWb5J46!NNORmjT$gTw7v(VkAeakb`yZHy7~?G-6k~I<+1% z(c-hK^2MiiV?TAEvg%CO?Nmzvn@PuOL!3X-Iibz!pgl zkdW#|ZT`r9KA{uF&+)z-^?r|x_ZnY(5XZqNJg@z+IHR+ZI&~iTp@&jD4QY~TRa1!( zr?G)Ub-i+JeHK&J+Rp5z4L;^@JX>(~%8|%??<%oG%*xnv33=4R==CT6w>Z?H2exjv z6jZnz(z6`)aryb5Wen>|gz^g3aEZ&5rqF?dznLbsaL`K^CNsB^^bC>_yA-8dvz*pJ zle(%j0Dbz5P8+%`@Nww=+zPQ8LjmOnn`V73>COytfu~tGn`cBoXNF(M?kRpZ$I>qeo%+r)OHeFbLM~dl-o?qZu(?*;BPV+! zu&Vf^Z0^rGqV(f0wJf>%u@b+MCGn+Cx|Me?*-U5wr-m*vOf2LM?Bw)-tjX5>KWoO6 zY#VZs9IC-=f*3QA``;oG$GO7?V3JC1_k_pKGKhvnFrYNIT%+ z)Fy1gq`3ph*R~0D^2+r;l=mz06>RZ;wyl;BBbyc@5q;IcI4H0hA@mm``c%H(9wG8( zUT}JAa8SKMn`_s3TF78`P(n@I3~8^Cx>{T=yuKxz90mDuT6i0hkNPHZUqJNy#ti&Z zVs`>j{tHzeM(`EFSM@1S;O0b(Z16FHSUgWf?Fk#e&+j?B>}#d(-tC{I78n10w!PyX zUS&e$T?NTRS4lZSN`*nm=WMp2W=M_qyTj@dypRKiNTk7`*j8Z~TO@y;`p%a(cfswRL0-(;{+MCpk==9216wZ~V&z)*gI5`5{6+Z&3a(U;1gPB2Z27RjY!O zMcp%`qA1{>!oHH}G)1+|!?#W{{qL0?Bq$|->*rw-gK^3q29_(~_aMhN7 z(4B^<{_K^TA(J=$qKaRFvc0qg_5b7hP};go`o>6p_c@SK710(a`+7}j<_1=8uq`Fj zriPqVvr$qv4p8UU?3a0|CbA$c#x0{DrqMun268?n+4+(^Q6K;QxNqt(>T<)=n&=OT z`~ZS0_iMjj+(pqVXV0ze?5zFJn%;vJpd+SP7x=N$T8BScrx%-0unNt$M8C^KcQm0B zxs6hW^a;d%Wd@)_*yR^@G&w5`EsXRkv3jI)^z^^~EkUq@@vQ0XwTx&2bxRxw@Ly_<6jxx zbwkE~oj>zJOb8#+jAb{Gwre&16S~Pqd)DRs^F!w?GZ7(wmut~L@`eV$1P44w?wU*e zK8iM%p##W23_vdfn`Yy@RFAUqzBBIi4ahh5(y%*4Sq4Sioya5x;QyOkqWb)PA^=dI zQX}VaD5c87!1V<-^;tMmUxiIAo+wbw0X#m zBaXfymdp*vs|W2YAQ~e`vKY=r8e+sgv_!%r@aJ9%(6*}BhX3-w3s|crJLJ`5^|2_xbyJuir99HqD$j_dvfb*N~r5M4sIcv)NtvYz-%} z>lED@;C819RbxI~YsRJe3C_sV<46DK)IyG`kCKYNaaGj<%=<3-a4km!!n;Vc`N80y@W8HPV^8 z^{p8bTs4TBN!UaUB$Am-ElZ&S4*pVEDTBrMx* zGdETxlD06_;aTMQ*Nn!-JQIh9J&BOxWnvCJ7U4byj+0qhYN&@NT3vUJq;q!+IOM%Z zQl>8!eNyccr+N(KiWT8+sE)Kqhe%+CDAdt2pX?OyIODc}nz0rXc8n?zLEsi`uR>vf z=6FLX^f9nO@>fey0BcsA`^zbT;io32B{0h<57Q{4U7H^y9tPnR*@*_n1WZdB>*X) zm{Sy^NUc*MOrT{XR_nPAkhf$5>PiyY5!PRTL>IoW*xyO?K- zu@+Cq9>500l)Pc`zpkOF`*RvphFjyLn|kLCJXE?LVuyvoTQ;kbQ;gp66zs9*K6ZZO zBFj}2D}h(}lOVfbpRBo_!UcP3w_kJq{)guOn-_3^Hvr`Ds3J&mAX4tXv4>Jn8N=Se z`X(@v>1@;M=1KdTdq+>Q;;e@}TDec#Sd5)nN&iQx$-i2&3$ zMPc>&*{@QS)l)R2+ge$V0+7X|Moket@WR+yNbiTReO>=SNBZ^-Es^I33V043r7Qw$ z`~pHy&4RvR2-$HGCZ6z)&%_J&iXS$mrv2%|0@%4ER!o3UgJA29y0$FCZE2WZgukEV z_*ON>Ey30e29R}gQ25=(9`Z3vdNP_&oj71>+)h%B0g6y8PTVVG$4TiL7oliTmbS71 z@-;|Ha=O@NsYQXU?xR3{CBl42l)CW8&CyS8+(Zp++{j7`18PWV*1twGzu;Q+C!*ZL zH|k5Ouhe)w5gz%x=p94_7AUtc&;F%DE0py0s0x)ZPyhL-SPZYO-d#UfWWhsW?_7N* zOexdfCLP&3czT}hQr5rdQFx4B1jGb^C_8@y04mG^hEO!0I&>rw(9n=Or)kJpl>BAH zG!mYyD!n=^gMo;}SvSXvM!6Y0RCtf)kM?59UAbsM=)qjec|KoF(XGl@m|hWARR)K< z`8+A8c!gD4sKpt-h&2CvfzG%M7@CK_XRe_#!lChLYes}~>Mzch+Pm{EFI#LyQYAG(@Hei6JuZ^bP# zBuLj5o+P>j*hfCvaCDR-a@XH+x6BsmXKk6a3U}?gyYyx+a%s& zgKU#>p!XXg>PfNX$KMfPTm1J*5R>pL+{nC1Fm~E@CtSxR2 zE??xA0Xgp^R`sb7ux>%gSw`o^ZG~I(s}0(ldWGNW-D>_8yIo!v+Q|CYG6$fKNuFz# z)~cw4yLGNFFzu;pXHj}lkeVij+B=QizklStYxk#OjB_GejDa?`W=FhANUdV4JS z^=o&Z+Bf)7LjTWvjza|V_aA;!$0gLp37oJ$O|9jL+xr$6rn$U$shp22yZXOAEWd4R zqJWE2cmo#Trvi`g@^}b4X&LKQ$*}lDa~$uBU_=^=w&H|1Idg6w(W{ksIUn6wE80+H z6rY*Qjc+;CI-ZeOrY*&;B>rtBh&rBo9gOpN$yZrU_Pd%;Ul0*_V=6b8WDID#bGIZu z*M^e*MFqY2k7qAi&pSR0oj^@yC_Iy%u5Z7T4qtj)6)i2)2>Skx?OI}jJ@gLqFuUL=wxKpO!`U|l@8qz-gGCe~9?0)U+F(tf_ZX1R#E8?xkaaoumsUh}=btgDBq z!%j2bhJB&Ho?C20{B3+sLp%KJ9eBTPme5NGBh0a51*sajCD|l3Ji`O~bE( zkP2X|b^W5@BPOu)i{pRXB&g-89d_db2lG{e*eH!4AdD10elf@gkgm+RNPQ9cV zePF4~g*C>?4d_8WSQ?nDGONN#(6W&O(SZVKO0XffUu_FaiJ<IQVnhxGh=A1HJNp*t`3)f+qL zAXrp3fRpC~E1e@JgBcDk(KWMLaw=a;Gg0)txstXPbZk>rvhq3LtI{lOc?Oo8B4NjU5l&J7L?rG6U>IS?_2V`I5iSmQXhSs3kK?%mz;G8oWldj8IID zWlWBjPfoN=z8jjHT$r5NnVde9rrU_LJ~h~_7Ps8c-;PY8Sc$MfQI%cGQFcU%4@fPu zOif%9xLUIloiQmzOg5HJnRHIwtY^IY91y!O7`1f%ZcA($;FIlyf15>WbnBw>>U3k_;7f1uGBv0hFnVocw1qQ@pa6V7oZi--{^F`PQa?2v7XEMAIw$e7wOqkiJASeq z0U+{git!E`0dNpJIwkSVpVKVX@K_M10?+#N*9K4uzNDYf5KEgNJi%7tg-u`*io zl0>yEKVGt1Cb2y;=UANFy(XarV_eci69>R6p3#om#BO;wZ}Qi^`UD>xSaVisl`jdr zH;UbDX&RV`+~HK{hIl}qh2fV4bf~&0XLnS|#NXp#^l60WnhbNU<%<=I3=Uvz211~V zH(s1gq=wY`d$)~yG(pd3Nng0#utgAaOK{z5Scj|#`n9J*+T=x*qj0QqBu`3?Wto`( zOe~`vfsg4U)<6@fW?F?1#mjXqSxAT9rmtVL#CW8Tx@N}vXk~GU6L`Ltc}#u_kH^qa z0*ot`%mw&v{PT%8TvOL9h?!HZfkd&im`JzpGY`)#?Z3QI{j(ekS-FQb(fK1i?jOP5 zF`KzXMw^otn*!pBP}swZSQEw;{q8LJ2K`^A`9jC6VLU|+=x?7rr%Rw&5+mxPLIF$5 zDO|;fgNrDMirW{FN(7SjvA7D^C_cE!J3#)ReBs*a-u~Sun|D&=Y?&$6V#vT<9N?Tx z8w#z2Z?Re$5h~4@xU3X~TK$!}cU=wU{0O_Q%1{NBT&&zIRslwn5l@);jy^;T3)s8+ zPCu4;7MokA&Fae7e3!<&X06{cR&Fjr9FDEekL$Tt(u6JRSCJ^P$LxQ$qdGAoDjdt2awdC#9FM7Yhyg#EO=5Al$HrX^JQpL3ae7cq4y z)1Smr7{-)Uztka@#+%24luCLQ$S96L{pW)l8M7Nw*#9cha;VhYO{T`Wn|Jz&lloCp zFhQyYY+wh_J0%J<^;UWpepzjt(Zw-drsUbY1@#-~r+rW{w3fIR4uB9vtxV1C)59Qd zX93`X|KiKJ#|>ktEEC{Z1SZORtm{}S!exUWv#!s=ndbStEpY92Sfc$r zl~b-pRv}lEdrS#|*Vj~&@hp)g3VsCO97OwZ1CHdcG4Ue0E*?EDjk6Dz#S;7Z63nb& zsI*votn+DE0y$0mx$Tl`ApG|KBlE!K+^ z3UcSf&nyc;Q05rAjTJ)*@AxGJ>61; z?dZ}8sMu<|H-mF8K+LvQIkYy8Vu-CcT~mKsKkFZh58 z++;cyOPZ&9$Duu7u&&>;(#>gp#HEH)vQ9iMZY-~%2KSMrU0ez>0TI+8e?PJ`GPTe0 z*@xtQpZVd#2UaR%=I5?ZbiwdG&FI|DxJ`}NN{n_6P^c+_u$J`Ey6L{8I5W62f7J@*`uc8<@ zdDBXiVoI%DeBMezpO{s=SPxIhj0v5Mt}XY(kpTkBW&F--KZt>+8=h+h0qH_cIhQ!<(EqB+!npLOU^Tk9Q#Kr`sH1Q?0EXX|y z3I+0qBl)xL9h+E9l-(7G8WPLs{#mLywaL}csE{%Y6dY{36m?3WAG$0u!O>f&*IfmR z+3F7K)$->iPqfrk)jqC2sL7V9a};T9NT?gu6d)k4eRp5?<;_(Y(z2IKs6wZ<{A)0X z<#(2pZVi$tvzuS-$2xLOX!^81jy&WZ2^#kBvR76`Jvx6UWOfg7y{VZr@Xy-UfQip! zBAdY|ttrIN)4og+m3EN%9!6xpRD|z`Vc)ONY@)#24=+h7u0O;7ypt6@KTWJuB6w9U zs-R`Ir0|E`V|BtR>}Nm{fnd`Op?=f!uj`p&*D>U~_w}`N=emO=e|N0@etGnF_vzoS z-ps7OXYziv_h0`JNqcSrmDq~;d!*AWv?fs?Pr%a@^#oJd<=jWSPcR zW-J7JAwrlb-YW6W@(umqxH2IQ0Q^5v6CnaL6!YX0Lk;4nk!JO-Cy0^Q|7gw~t&tQ6 zqmdGRhfe-q@fw8RbZ0D^oGYzu?BA;vDe-wJ0@!ve&yi!Hs~b&)UY4rIUgj>7f!p3jIFAz>}kN=`Jgh zW*f4y^1(_+#P}wck?vQy?TL%gFfFrUZ*A81L0C8mgNl&ESfl4T?f>xMAgk8L->;+( zS6UvuzyGW>ZWG3>T7LEQcd_atj)!7QyHmx=PF?1G@=vIls~C90NIj5AOU-_DVKUKs zw`<22TNck2e5zJ`A7^7g9B9KUu+l|G2kGwbLaKdWu40Uc@-Um%o;IVQC~bu;^^qr{ zPy3kd#GcaV6*n|u!U}K23u$yoQ^LT(CS%@5iOehn`A+XOuG64gW1Q%deMZj%k+>r3q+x61G~$g zrIHjjrE5~}o|~Qw@7~Iw!_SuL_7v4Y_Jk5W@D5PAC<_aD*bT?m(Ud`meu1W`8rp11 z{~%B56O$kbWkKk zFbjaZrs>S$`C~0!eCam;K9Z!&fW=}$a4nRs4`{C0Hm7<&U;w&DO6qO<+j7hCZLDCg z+;*bkUl^|c8e!fKV?B)PLGA#k_$dk@+A3}k2jAiF2FgzX7%g~?KHUbgnCwzeE891bj+_-(p>8+yWX)BocB^CQt?Hgr;>S@8qwosd;U zChfZbayvNA2euDDsQl;vn>9tycW-$7r4x&qtMH@lv;)Y-RBuI5ImtJz?91Fq$5mr! zu8*;FT_xnJUqn@JC#odJ8yTtw6bK}m;P}1^5E?=i5a^1Az)dM>Ee}9qK?D>5$6Pb* zSXrE8Kjr!cv9!|WV_mx~A%Q^oI8P%33%Oe<3<{{XFbK0w!TX?p#=lYC5taushW>my z#B${0m>I77>3|_ypc3fIAU~kI@>=CN?ZAB$3rVOL-?*+>OIlVKx>LoGfjSTD5a0{f zKoC>M*%+uJE$~df%g&`{&XAM~4Y$-ttX*^@+V-Ko-FSZN{sE_@|1j&Bm9p@&M9%i^ zig6&iQ8;xBF=Jo`Rk^jEA8sUA}w@Xu}PYFF+y%IN@XEO zSlvo<&IXYU%qoeGY#`76+y9yE2o{sbb~0GvNhwjLlsf^p3HNrq&@CE-NFhuM6wmxO zd-1SIfaobHz(txFUu2Tb%f>Mzp2C%=Z-A`{434#6Pvdk*A#9D7R&%X`0zxxnf_2+cF-2!%EXvNicyAUj$Kieo!}yzJr}j?tQ>5+0=Bw4 zaev+$WuR^}C&ecFCba2IX;XXy4ee8wp#B$wam2eQI^bEys7f#Qt3%uP+qF5eJ(!JA zd$W<^uU24^sZT0?oleONG_I%3YZY;r#5o!y_`gU5dwS=j%VUGa!b!tD24Si_!{v2NJCqJ(@mNfG|cphl5c zsr$Si>2%VEy%YXQRciX}ux8k()0mAWJLEcM^+ zLt~}1)@18F8`zsPRZO&`(me#vN-66pPn&MuAJ1wQ#Z{^!Dvxr{RCmGO^E@LrWBopx z3~BJr4hpdB(cu|2*a*AU1@1fSqonkm8D39i>t{M(@&2&);nQBDtk$|RG>k++sILfJ z9N%zE_PCtu$GMRjRa#28CJQH-TZhh3^!GXrD7|O!IXYtFr0pSSoBG$!*C+A$3PHn` z0l|wC#w7$u*M9So1E`L~Q98ERIw9DS@dHc)~0uBvXz&uU!aHV z)-tm*6nTRm%?x1R-E5g3IW>yMK3Dub^CrLd?dQ?&P8kh2+YjZV=a%sGo7Lx+#Ynad z?2Up}Jylku>dvv9>N8Kr64Lz-+e|u|{lVQ(z>g*!;a^aMH)tz=quXBV?PkySNWd2I{ymOd zD+P82_%c*n&+)y;^C57KnhJ|40h|3$xi&E%z6lcFyyrmIi%QJt<8(>?N{V=QX$0vX zrQ6!nthc<4ewttnIj9gC2Ai*DW5H;$3Tv{;CK86b`Y<`Lf`K}~6{wnlW^$+P7LMZ_ z>gRG?p)7w_EVrsTi9b)oiu=vhS#N1z!(r+U6`os+B9nq4`1^cYO|jNRuRZ_5R9Hj^`wV zPfic)MYDdvfoT)46x_!a)RUmW!ES)q++@f}_L=YqxwW%x&;!l#%pc(iV<2wqC1>Xk zEVDu}G01`mgj(FVE&EO_6`+>GDA9QsbSXy9XtY~A0bH=7{nv!IccK>zdS1{+Y%!n& z0z_Uo^WTYB>q}gprI@f7o=6x{Thbg`H=IQ8Otw*m;<$(DrsjkeBclN254l;VvBsZ< zRbHw60kH|uIZHGoqWZX(gA&b%G4o=H?=Q;MK&bT#Q_Y}B@{FlfG%T`hiY_Qp$ki+o zQaOk=CEo+8e<^ziBwFZGR}vdUxXvGT+{{xDS72G3@A;Uy63+}!7(=6`l}|zDh|lWv zJaenV|AcW1RnUr0yBKU}7K)KVF_C=TwEUIHh<0NpEUjL}k~i@qdJ0FkyMwZioxI3W zQ3hY{DQW&t0&((k2S}_O9vU+>S@m2eTQ(X##Pw-Z^MWe~GH>g_O=&2&$!8%pt!@_$ zu`mAGkEUFCAEk}CkwKl;lZ_!&h)s>y9jjkqGcR_M%_6GY+NQq^B~8py8%mX^F6ggx z8)L~xs4*J+v_uZ~D{))PDfIrCLa+kKkx9~k!iJ|9z_FNvL>^cnUT(Ng(tes6>S)v7z&r`=SE z5%9IU?k89$xQeyhV6Z+)wp}x`GOM+6QbX*Ufh(r2(k<+XU}G=}(5LY#dSklAJS#w5 zaV|}G(`=;x)*wF_8hFFgwY7_0Pk(s;n;qZPIIoqADtWueMY4Jjc)$%k2$48gJ^S1`e$Y0h`kc%XbznAJT>M>3#09%mM+do*h`_ZgY`h74R7L>PQ~f zOmyl(Rb?Ohxkdp5C~v_#et#ESg7Ux60Nl2Jl?dWXm%a<#mMND!|14WRXHQ((JD;HU zvRr!jkUrJ7K;qAc3~utr>ay)(>P*VZc|B53U(rk^k8?fkXCId}GWu91b+St~Z)?UF zM;wD#wm;tK#|H1;D{sw>mw$hPD6tWqUJPNL-aiKJOG_~Xpm9L|@J1X%aH@_*2?KXv z`O{0npxl>QLEewmGPee@eHuufF8uR3ZRp}k`;vF(YQAaAx#=Ck+vk$-OyT8Mw9l_6 z%TELU@sqw`kcDT>`!%U%WNTN+pVUMJ7`dud+<2I z?xcuK*EOe;F8hLyc}Lvgq7t5!F&6tUI_g;yv7;ulhUM!yq*mX7hSYwbyJof5@zYHu(bW1U5^50w(ZwN?rgYKqct3a#@y#I}<#ylzx0%iN^ISW{LHPH7%xW zb6rR-|0tGAgGsAxMPCelZ_+GIew<{`;r<)BO5+PxsxcIl#5%CWnl{KjWUpxAF zwS*)a2C(_M(O{J1J&M9iVVhp=Rut;YCZ%YziP_3T`?NVcPmW9#q%Qul)iC9(D%}6J zV1=QgEBN?lPjl?d6*Vm-n+x8MOmZ%i%N{G9%kAxJRed2Tn_px}y8k&WS9Xk4k_Fh< zEU7iBsl_05#~?t<|18}7TrmF*1qRwM{K%?~A*Q-tH4m{7yE=o@$ z)*$p;_iG!5FP*}!pDzXZkg1v1$0(`f!R(1*>kp11yFRM?i!~;cHfq(j z@b+Z?CS@gh=d;6F0+h%Rf>pC->Fr=#)IIyis-;)2$yzII4*R$rb^qs+D2qe*J#dt@ z?8h->CfgY_`nDx}9>ZfWbk9o53i$+MdPb(KDLW_KaQ}zCRrGQ$tKC|w3+c!X>kYBS zsDqtkTSP3|og89!dmC+~pBmPm#DP&efzjGCaM23{*Eo?|RkY*hCp zRr(Q!PL?CaAkUY9#P0#_@hArlLqat$s#}1LYuZrlAWe`O1PEv)W{f3L%%@3#VIy`f zVfN?kyF@@;Vn@)6iV<)}U#Q+(IPrzv_N%CW^uhLATueC0kO7!Bex6LT6Tk<5!kJsZ zoL3Fb4{I>2!541C79Ee*ceHBBA=qmX9c`sj5pl+QhuW)TjZJZqp+;i0H}B2~f*XZE zFRL$Jg7jsW^s}&KEkXwJOp9$9azednuY$^6*E@Esv)Ig$k0r{n@@yNI#HZR~m;MgW zWDIf|kY&X-<~)!qRHZV=8Z($Tuyy^z zdBgM-O1n?4lYrg-Z7nC8yv583;wnBLK76laxZv9LxWF7gn6R|bxoqvJ0jYE-HI$eB zWKLXZu|z4=WU$0YqrH>JBv_$60Q#K3qka8=ew~ni@~rW-OQGzWObFla$+$5umq$Mx z9#TNmiTMoel5UmfcJP@!=Y)NsD{;a$vr3Dbl&>$ysK3_lp5Sg~PNj^Ql&DQ^^YLRy zAZPV2j4?mbxI3HrwGOE5W%6&7>$QQa=y}Fa?Tt6u z#pcACYGFHP8YmN#qT=}WJN%V76J1nnX?2{yeKoI?PVkkpG?w&PDA;Ba{6mfG8NEg| z3b0;E2ar0C0I?UkQG;tCP$>BO9)q~4sgfz}rJ-6Ls*ns+=4UD|{S02%2iF=JH{NGz zH?``)RG1mvgB|B9Jen)p7t)CM^h06N3N+%h`}XvT{heuGgvWlT@^E(dXDhI~{xgm8 z1MNH7yf$wp-#;)ItE)(+dBiBtnV*9s?-^y=SM0143BUQKv0KYMoghv3Ui=bNMVL6N z3F?*b^nRuylc>2Yt10UxA(xB#&i6&GGbE`1hH51ls`YxaJFf|+J-I)$`R>`>UBM? zWw9T|GrKNWiJUHyQLwP6l?CfF+?&2%Ag6wG7a{A%)EdNehOr3A8$i6p-&ekiO`{bB2EbeK8Cll1+bL_%FnIW- z$=wQr;@!nJ+1?M{z91l>SIY5m7>t1;?{w$*I*lfo20^ut*j^hVrTXM|XC8Fg8b>n) zTse+3dZ7;KZx8Gj)M9BlnZi=HjM^s(n0|^ae|5mH>;jkMjY(4XfC*8i!{rKm+c}|4H_bZ(6vLsDqwL`nW@DVOLi1x>sVoBBY|E0I-EY_Z zbm&3pe*e@&q$%JzdGRfRGD3d_=G*b^O)jvQybS(|BgA}~uHbFDN_p-SG0iXjJ>Ya) zP{)64O8M7<1YjGpaT+BtW1tr)e-V3Ik*zv3(>>`vOw-2+7`l&F(R)da8>>-TE|}^H zFD?wPtcgX@dN`ad^gii}NHFcy9Z(eWt~k@b_gl|$LHsvjp~m`AgLDn;wNDH!9FU)A zX{Dxh$HwJB!_o(j=BYcj9-BMlQIb@6Kilf(IU_|6JF3%`H;;CHynL)0B`fMMF3oVS zN^b@Dy1$n)dm-!>tJOw0_(o)9BjU@H$GASxwA7^FcM73RE?>(@2Q{#5jzt3X(aLtR z8kNmXs;l1EpYPeiG?d4lDHOZ=DdV1ZIn8)hfuk=>?u2k>ut~c{hdC{IT=D1h|t=evT$>Ynd>-FZ~0pZPqPB9?R;TB2lrFYvR5P!id; zhdo+QBDr_2CWyBctqQ++S+@VOLFmuQZiQ{=c@-cmiY3(fMn7;Vl;weUeCg+=pX%u? zD&o`rpYNYuK9F$*iAxDl{7ule-vWQu*zV=%{2F+*8ohfuPoT6n2y+_KRAm{?W;p-n zMOPhD!IP&spT=hh3!7#Q%mA_5KEt}SN~Aq`Oa0VG-d00Ujtd4LNT{Cbv<*%R8m zN>qZehgwlnuc*^489LC0O^{Q*7gi`tdT!{MOTHr``y+>gIBqssxzRKxq8ueDw}my3 z@`-V|4STMmjT46WsE=(1MC`huGQFg)`I4GA99WzfXD-4mP|lj$v*BQbWWtjSC;C@p zn16{ni4TKtg_E1H!@YnAA^Sl{jT}eO$O7;lT!RD&=|6b9JkT-K5cq_Y@0(QIEZJg%LXyJ;6YIOpRxn~bzm*;QjwjZuI-KM8&HlOw)-~+c{ zuHX5TQdN_(XRPv1&8 zu$IA%u{~B0qd{+xq7LIFmA0tnr2YgT)^tBkNUV0Cq)1icJMyK}*%7sRFyBw6jh`7U z>?0n#h|(|tfP+}jQW?M9Op-}bvzVkSdqY@Ygcw7wypqUoXm#RpJU7gaA*V39)MHRqCfk{-yLmzB|G>bMI~*CP8iPOqi>DDu zxh5@(m->6}Jek14V;?jpLk>fIlC#L5wu=mxIuk~%D`pHs9i<*v95gko;6t_-6?qkdVqnPl>E3KjvD3VXxz*HV<}BGS)r zgV1$j3sgbi`{l2g<2H-UAYYIlQ>oI!-BQa2g;k-fXwS)qA{QX$>LJ`i#|S#Ps6GdQ!{*>{Pi`EQs;t*fX#=8EWz2C7=vwGTNgu?$UcPD`W^0wP(Z2#9$d?}syA+uO?2@Q%oNKlA<5Ony^FJlupMk)$`R zhV1}Yx&!XQPg9B2a{-`EA7Y9qR$?6F4K3)zwdJPT<{_DpJYP6ECUh_|)-PJ}cEc5x z(^k%!;lMPWL`T1oNF z8au}C9Z794$bWn?Ho#mI^-ji3x_rflbw()zLv3Wetl|Yw_{-gYPmz+P|P%h1W zk{K~=axTV!ZBWuNeewfx|6fX9Of-mV>&M(xmGKy-)q}FUoy;l^3;NM zrJqV2O1i1ECHMFe&EF)6a(lHobX>nqfiwe@xwlTayjj`0;IQu#p?x zjM3c%7kM^SzT~ihcIVRQ4(C*BtT`I&}vI7HU#Rl7wkVCMoYtAyE_+6u%|?#}6l2jev}B zUqPeRuMsad%r)+IUW54j6X>;zD0hNKU>#Ccb38y~V(?aORr z+`9>qk%alaWFg;#GWgRW+%JBA`)_BkF=C@9w6X4-`n-&)GYTY_pjj7Z=q<@du?b|r zY1XDM9C?oNp8_l-HF*?AdEg0dAY`Kznq8_(uX!c;Q`64}tW%_3 z$D}#qnBUj9+<0l?iPbXTMY-`ZE{IaDX|RQDP!JGjnp@!R6XmM}H2df80h_d~&#J+@ zNKPX%)GiO;U<*|=2BxHwq8XJO(4QfXBh?&HrW`l@O{;<}mlq_-Op254BZjhrHF@0% zOG%W?XS{}UH2wxsQ)S5)p9s~pYM*UsueeG^R*C6vPe*lVA%m!ef8=Kd%7D;9$te;| zV-kP1<=VEs@>Yp{tx~L|l@kD~Kdo1f3ltpcuuR(X#c!DwdcGCkG5zgkyrwU)`9wj~ z&y$%TP8^-~BuLyC0zi)#|WK z2;5VHB^$dYBTqeH6tS&eT5E2ZqFOR(j?_^sMJ*3kiCrye-UHz@x{~)Js?kcdYF$Z~ zct|Aksn&BD-3|!)h6W9EgiU%%3R~~$j`skc>gM77NUO-0#q|m;>GvojleDcpCkQ%x z@#mZRK-YX$*LSU!x+Ajs2IZ8+vFuB+%ua@SHNn6%OTWxzW7ucI$Ccot7*4>H?%uLV z)pGd3j>ET8gZG^hC#R;1*@i4*hM#1O7#J-7Vtg38+@LaUp?G(YT#E2dGbuR>xeyDb zE(^7v78-JvX}sx_+xEX}RLC$UJM<=3E}p46mbs`~tN2^P5Gx4AT7bbi#0rADb z-sEO9F@pJ4aJ(G5Wp1VabJMHx`V@wEH`CJpN;U-6j z%|yP<+f&O*E!!b*$*2Z=5{ZC@1flYE#0B)~k|hH^hZF0@E-P(l#-i>tM?MpX0+mD2 z8PwlD}RuDS?F_n0(F+@6>jdsXe3oBB2#37#Ql|SK)E7 z3HIJg3e*c5#&|X_yEcSc_LHH6X`lsPz4Jy2P~MegDffHpiw;MG5WLbLbcOr;<1?PL zX@pT_a$ngfS0PC@y_Y(xS5ZGS^NM|xETMv9JeD!Dg5A18qTEyKJkO8Bn|LOlxgw-? zr=b{p<|EXIC2!0!5=Bo7`b;&Hz7<?wcJol*x~)e-bhq-y$0Xo}}^wKN`Km)Q}DL zE?8zn2`c#Bu8fqw#~4El9MdOLMhAMxwY$fa0CWutTweV~3CCddlQcu@Y@Cgl%CNHG zh;Hw;w^An88SDx^r%FVDDH#F4ijQqy`!cloC55uGgOtlL4B#jfQ~QXAscGe*zn-Ao~SQPqux)jR{fp@k%Y9^+)e15M)=Rs#EH}=mo_xL zsoa}39JP&uZktgWVb8jIB4<&Fx!w(<6g{b_@rcGQCy{QGG4asFmEW2VZhjW>w@rT7 zo%E1G*!yclhe8J3n8+$n);@Xo=`fp{MzF(H`k$s;*_jmss#%@%*Jrb3^7TmHmxHT! zy`5Z(OKXuRQ2?c}!tdei5FW*|&1Wn0*E$oyIA(#e`6WHo2>qd=%h%tURV)2G|E!+F zP;Jl-%$PfB!urXClc<(AVY2Ysq`RUJd&Zvd?WhyoH@5R{?CmCO=OzpYsOzPSC%*hz zly8zY(@mA~uhg&Ec(Rpml#~yLVFIJCo_;PrcOh)Z2!$yL1}QCdUlhDKFJ&47Ms85P zYM?zF;qv_&I{EbQB{g3dfW6T#?bh(Y^!$1$_3GYvOZgsCcOFD2c{M#_2ai)g6_Le| zPm8ix*97Rm~rT4HljE+T|X=7U(FCc%iEt<+6b5aI?wxzo9M1 zo#TIYU(#G%c@yR@)NotK>+37_i|dcPf-n5`2kMrMtTl1v?`|?a2Gs8kZ~zLp;Pd9c zAoPfH&(z)YMS=?vKvhnuqdYlUW8)MCM8$<%H|i@9$T`;U~i1yB4I6gR_nk zV!(nGoT?SPtM(oF_QiHt7u78$O{RGyWVAOtCC+~fSr2qo+_p1QVd%AMDJ;PcM+_h^ zS>PwS=@`fnz(H+)*SeF~esD@uW;$htG-zR;jlrdq?wvlozn0wlhc%n3l6p!K#vZN~ zz18|90qBT%cJU827n|^p9$E=0I()bwKEKt*Y!^=1MYZ6tP>W_g4bJt`2(#%E5nzaIym9$AT&;!F(S z!vxR|0muK6N4PXRt$&Inj-iB^8Lr;{sidUy{R_Zo? zkS3pPrD)@Nxadn}7ERT>Y?hn9Sup0TZ#G3nH?^iW`Q*(FM{!)N&g8Ab&tT0M+9!r8zv5LI^k@e+ICGrmZpVp!A(0`p%iP%Vqlm+_ z+sbZU5}uQYJd#!j9YccHE7MLCb59siqaXcbMoc?|D`vohkJK+oEXrk68Xuu46FLxB zXK_A7M=|kakix62AQA4#X@aCvROtSgG!nJZC&LFCU!xiyXMIL&HrE$Uz)QMum|Hs= z@T8^9ZBfCt?5b4*f21e5?D|Bnr9@)_?-`Dh7|ME>`fD2o50jPwnZ{i$))M#s{xCsK zl)>{5`JI?gyr=c<__EECp}${J#x$q?n%|&yE!#FNyX+Kz2={vQXD1wNJu|&>2ki&Q zey^Cv!fSMu`Qk27Un<d%Jy-<=174k0$XpL}z+&2@J@CT~drP-6vlKyp^u z*oB0VL`pX0D5zBBcnZCU{UBbdYBGaeCE^kq=&G3jv9OG&+nSgr(CI$exEgKW&`V_X z0RQvrI|n>TyM8M+Jq_`?@EruIksy9MNVk}@#1?jt(9*7^RutJO%B@P!%0CrW|dQ0`6%t zhWri65+Tkc2o}1STaEKI&Pb-p1oR{PinC;Bf!?s@d8H5WnYDS#wXFXNChh2F9rp|9rw6`Z%{NR?c+0z)#GV28*ZTyQUwfb6|En$wT8{mfChaSOld6|_ zN=+h7!B`OUL3yPP=SKjh?SI?N+-7{hJ#JmWKU#5*S&mz2b@`78E|!bB0v~h$YV|TH zTz|qg$!>Uo(Q&WP6n+HK(##M|G83a?HaCGoG=xEWIL?$qAa5b>gPurp6HLXIdroc9 zx8dsFm0Bqu4|ZX5dn7%)-^K&q!)6s8e>;jGRLrbE=ANKRlWfL+fSHp~tgb8a*!|s&BMiQwp%C;I;s;&g;XzXoX z(F&OU^flNy7UHcm^VK&d-3clJ5SvbvIv^E$wMUNI^NjbPm5z5jt#tTTD;27Xp%o!eWg}omt4+cwhp*mU$! zLOG6FnSxx)iKxwCF9}UAyarDflPL4PK-E+=zJ!7w1Hc-zZ_as`Os;Rl=qb5wDdO+l zc{r%RC=zW}>Oy%7JF@LO9gmcWCH;$3q&r870HKwXY#!sh3EHWaQ9yER5vi4XqK-u@ zaT7JF&ryma$yaoN=wD!RpO8S6kA?-i!{YerbGjH5qyQrdj5ma_cX-egCjwzO{L%u}gDS5LqLfy6uiejw=V{;yb zNxC8mq<}QyJ6ZVOn0Vl@y9T8Yjd{L{s82W$irYwM+6G*)*AU2GUyr;vo=DrXp+=b} zn4z4d$KWfI1sknDCL zslmON#3$}n6fWl$0P$Z`f4IL3Di0ays>Hf48G`EM2!lpsLe?AGoWCZ3c&1@= zVLy2cO(ud$+}pRAH|2XjyoC09BZGo&wl9R_$n0P6^u6;_z+*PYrQZw_Yj($>G5Z^{ zP!EYz?fvJBD(`%0JqG#r0Gx_HlEvH$`e9y~_jHvzIp1nbZg!Ph?gk5(6$6S|LInJt zQ|?NtyP4cnm2yexc>nT@PF~A#MJ=O(q@=Ugq2e0m2yPYcjq7dU9&*v>WUf0|s!oS# z+(j<0e_)BEKpepzYbXzUinAtY0uQcRtZPqRa+FpCQ$RpF!U_<}RBjmCM?&%H5%~j$ zF*?1x?Rvor5bLl@2$AJD3HiZOrR+^hx5+dt^(bX0)6JxY((cBgq;dZAMKW5c12!n) zG=eJeqgzB*U+|#F7L+PB z!qM0ow^%a#)DYv7G%-QrRemMJiXq)YDu(IEcKU!2tCh@s6P^>>&aJWS;OwZe(x|BtEK z-}sNbW(r6k-I@MN;pp0%>d24({I&cm{_NOHM{2(lqd)6OI}mP6f-P)d%WFRxyPpBl z(C%dKDJ;GwGBP9mUFpnpN2M+|sBf>|N#gzdqd*6c?2Ug+hq~Jbb9%Wh^9%`HvZ4PO zpi5GLraoSXx z;rBSejI1z56MhdSZ_SD(`yxZjCrJhx3zcN1iPNC-0Yz`>Mkm%u$$G}~NYh?(qmXm> zzo;oZIxz<>DHzY|OuO$N{TVJ%`JYlT8Ip?J zm0ol&u4uI*U((ZK7s!Tl5At{PIP9{O^vC}tr3a0)JkhUINrv~*aDfbatW@Z#QNhB49P5VQN) zE_FFq@N7>|@hW3^b_$z2BEL6f(peX;MJFVm>cOAZWlOR`kfK@it&|GP>gDTMMNK5K zRnTSRc;ch+$?xhj-WO(w#xXszg{M_hHvwWxS5w-u^+Xp*f^J8BVRnLiwt~c3xk-{Y z1u+edW?a-~{WG0)vYYy4gY(}C-2!6IlVP8kme1eKYw<3*Qi#wA!{jl^YaGq&Fd=vY z|EE5iZ6=!|JnCvMn|s9Y7c`UGD@PzDN2no3WF|*!KSu(VD0Nk*8BkB7uor$-T5euI0VoV$HIO|`=7S5e>O6^z!#R|8d9h^Qutt+UcC=q z?aW%gn5!vW%%K5#P%hIh6`UvJw4}jd3`1~WNvT|63>zFOKa1Oh3dh2wzgu%V?a<2} zBR}@Qo4q(uCmGon@YzXt)yzZo#Uk`Y;a|mkqm5DvSQ)|@39Up@R!VV+4qG~S?4XeRhI00 zs>Kj%Vv_d!(rc=UsdV>f0a6kC^FbovuEnox;m>JBM`J|>^QzMVYsBq^tX(S8Uso86 z@;Z4-DlY(knvYkYTw8~>0st5VM-QOa`G0Mg65V&qnG5TLMRO3gLJrRnS_`n__sc#IWzqOprk4A?vSrZaD^W0!z$r|NQP@^*1%uDuhu881U zbkGk_h#N)F)d7?o}k&a#fDzkfe*)p*h;>@-LoDMQis-L>53&yDb z9#j9zqvYLL3~_G0*Pg`AXtjLG1K-~6kqKe5Sak2N?A8v}nmq$qy)c>5$H;5h zVLH1V+&b^PcYggd6B0`X$u?bS*UJFo2&YMkK%Wm49AB6KW?!@>7yB}K8AQ5x2pa~Q zw|lF*;lcx5kJ>B>N>H9mj1m$37vsct1ugG@cVXvf() z--s|}{IrEaD99(Uy%yF#M%|#tSctq?e7qTJm!pfbiyR*o9AY}u+9&Hu^A}fBIr2>5 zCrA}-j;?6mx`|`W!#)aS;oZCXx8I$(QEK2J{EX3_5$q;@TSp4gOC}V1Py1P1M`jI< zKJ{jcY(mRLnhgRn{9a^OcCmPEc8JOfFM+nQ&L_2H2zB*<}?Ob0*h#6r6IB=2xmI?e5L~D!mz2g`Hpp? zvmNE?T3zr~hbdk(>?aW>$7CLJ2!HvGeTxC0ljL@D9DeGInkb>5S%uFh-PRLz>dv>w z4t)efPI?1j8&Q)#*l8&Zq-)RJ@d-XT{HzBBJtO|En>8YiH}QRtJG#~GF?#OR*7VL5 zps=Hd0t{=-4HHY`ntwd;f+28rB6LlR{|4D%mYZ9Vb^kcexmvqr& z%U>nufHLgFXJiHby>!NBsQpxQmBASlrfV~a2%xhGP~QncPX+DJMyTk@#nq9qeeR~I zh$u)7Z^MD0x;(c z+IQSNA83=`OIL?D2xOcq49CK05ZmZSMzm&IdFN`QweGDybq>1)M(p z(J6j(6;3)5zPbcYGKj{%c>g)aqJ>GOT{jZq9I0UQ_?I1&Ahki79eJ(qKKFn_Z>(~^ zL>Ns&9!kxweWqFee1FF!?zlW!$0d5NF1mXgqNEtDagi_u2be4JU3Y`?a-v#VqxKh~ z#sWaxXjmx2eIW6{o42<<`qRuve)z-`y;aBTkA(|;yiZN5C1@DJ>jxL>;S=&F10s_m z#+D>)Ug&b~e)8De`sJYzNV6tMrJx4VtruJyhN;BHTVhITYUiT(>jE z%$m&{SGDBHs__Z3_*vN&e&geIc`z^LV^qt{y@Q8ae}}hzIVYYtz_Yyj?=9~PBR+c+ zUL`qABpv?n9MBgWDpC}Keu1Qf8PV`asx+>_o#wxwj( zOU+Lv^KSShdNQCZ{$=4!@()ks@$^8;_L!H5B}3!nG`42^TyFv0kJx8=QmCVm#d8p! zMcNn6UPwkp%)77aO1#~QDf2HCjgw>FDzeW9S$iPhryCzE5(piAy!0r=yl_YvtCAhtSos=QzOJkGPm~stQc;oKf66zeGPmSf{zB#v|V?lZT zl{#s~;v>)aDqfr%5J!`nq(dp~0s#w22peOue(&`@S(vbi?~h8~cAkJ&-D*pix&SnN znQ>$u@=!qN`+@km%DLYwIlqAfrK39^*KAA=zy9-<`^`#|8J#uN)rW+j4a@A$?w>yX zi9A2vdG+eRAsL3~OApWcPdoKcYBwS7rTq+eb6wgw2?rK_I&84yHOUoViTIXCCoOxe zh{_-1Yw1#Z!DFJWQoIar={x3o0nta342jTX08P|0TtGGbnP)t@#L{vB3ZendQkG@}AY9S-uOl zx)^ZSSDOimZ#dC7+zUFh^Q_7bU29s9sem}1$y6kHq%LAI>fdU%Cehs>)C8A%lSVEsbF>mIQbD~)iWrtmCrlbJv7I~l)<8g2< zcV6Vv04+Q|fMJiAAo6VvoXjl_-5&F|qTJ!LllJ@&`UyhL8*xhD$%{6!UR0I3+b{e2 zhq9eO3gs}Cj6v??h_sN&vzz6!p6JAK)u*KBcj-|CIYGxBTF9VVM;WcU=#s8+;Qq@ejHUK0WYzWI{m@1eC{S_Ge&JOW z>-9tSGbyu4cR znP6hye!K8>QhBi~jn2Q2bXnMke(xVbn{>q`&9Ujb+YA>iw>L@5PSSbkxC^g$5BDZ0 zPWagJ`^3#3>^+1<0jL~Gaijx#+WN{zWS_aQv6R=7M!`Z-3Kwh;^q*V$w5QwxEO1RP zJEtGyIs^t@Fz**Y9JQ(>c_{A&*bqy0H6D@N5{DJw=iXQau}vdAg0~ek*14TZZR_`p zO);GD6zBGMgv8THq-gq5?ZRRbu>UAbvf7dD!?}*)nRjaU8g?<_o zCy9RvK15*zJ8k2yn?7`!tDDSY4|9I zUC5+Ua7?;wksDvEIb51tIIb=4c5tr_yo-Sh-0H70h147V_#^>JghL2gQzU`sn4%Yl z56M@0XPt_1g%fj{E0=Cm=8FD42`^?jJp|d&_7+I-nO!qV=9jXAxcGnU;*>zn@PmbI z<%<9vKp=oxt>T9=@_MF7d8Mg`F_(yIjjHCc$kXzDc>uB~2yOu~F z`h-?fr-+0mC&-r~P|?eWLr1!S+E~5ei1zDP}`S#`h|U(jTg$NZXF`xF|# zWh7=EBxOXHWlKnZ^p8pi@i0|5GWNOaM_O0w|WxM;;Uw4HyKjr z_rVNbSpiK3`lEdMEvcSfSSi+3o3aL=bb1>v>5u77mEC}>t&W7qqfsi~)?*G0-xA5w zF?txVI%nqyx&;v8tPg7^dh?U@j2M$d>Lsvk!vXh(>v^d~H4tdTI9>TL%$0dUxfY&S z(|UzuNV5p@$nv7TQFTjtXOJWRn*sESk3sTP-2z4{6NtwjW(IG zpK~61Js0y_IWmu^a=i54)+(6SO6N7bFfV_I{kGApxaVmZ z)pgM1s`e%CGN!ONs416Aip|{SotJI&d-DWGqj-FVQ;w&uD5FJ+?`aBBV8Kz*_Y6Nh z4=zr1E#3MkZ%$_$GV54?;UAr=vSRW!_E`z0hk_0iUhTdsRvBTjb!MpvA=p?%w`WmB z`zQE7czAkA+72tIOo%&Uwlx}p;P&*>!t%{+JlW(AVG?712Nq4V#@Nh=u!(Fk{AvnH zkR43mAXD}#{Xz0ZJ^>A#N}YNWZwQ z9yLCnDKaKbTSCS?_DUR0xT=oRiFfIs70JfHr|>fBg2!W;MiD^7^l|A!?%17g%;7O# zmyY^^Mr(m*!!K@%H+6i!6ua5e2eW-VJo%Od(s-f^coC4c?W=N_q;PBW{#7J#YTCg@ zKZUxb(5fR&bV! zVTs?@EZTD8{BbHLvi5v>&^wKUkZG)1*As#;n`TH1q}tjkp@!@Y`Enz1s9;H{flHa_DQ zV_F_|W37U*V)gXR1TBMYDO0evp{h0md#s%QSj!%Z_-HnlZB4)cp7%olPr>u zgozoU8<((ho2ZK!bKV{|W`B&Mq+mFYk47f`s1mpD)aJZ7=^mwHIXIbMQK=HF?Q%LI zRsAT(HZD@>xgm^9gE?LcuJg!TQ*l(nkN1Xg9xs*%yW`LBEfV88r=bc8>7bEF$Hu9Go0`4CQ#6^CrJrM5ej z$X?V@OVAAs=DBw`&AY6%6h?UyPgypC=`d8uuV^X2>9<~m0b5v$n~?g4JT!@YDGz0- z5_>^DM+FMZaR%)8BY3LgG^u1lYFTG<#-PFLle}-V`MqAb3Mq7r1bTygG*fG$TO;9g zI>iPKeGH2Gat`#wk}3(!c67pn;^JX#$q`zV@&R;;*>rSp#$=TORrB=P1#~l*;Q_w( z?|Bx01wMdf$WtNbf+vN-$?O%A0&O8BzD589JPnr%I5$v2KnE!aL!C623pBBNhD)N* z5lVtAG%C(@?d4Yt!WgeE<{1&mm{y%Vx>u>$v1a~BO09a|PEDRX zd6lLdJG5W&+$(NIlC|25wKkl*&Z{Iy2FPGbBF9Xk?VoJ!Pm4NA^1=1!)Hikhi$;Ez$>HTni2St6vwiDCbUt~Oo(1!#`HO`G>*u47x<9l7#lQO z%ZW|aKZ=h&0;^A#UUV$pg)vPCnE}+uW&Fvwd8A#TG3o6*v}&w$R15TKkCugCEFVni z#W=)_Gt*`YGh!EAMDaPtd?m~l8T&&3NptqErXr5!B-&WW?S-7M1?bOq9lY7IqQzmB z1<|yU(>AujSkVmc7qR|k@t1{1SN*B7e~>UQGCwRtfr`9<1<&#>{X7bEOzi&9W@=U= z2vT|yw^~kZ&jj!1W?qfukBO~oLUDmAmt^r;`7)v96x$8|bt55L1}R#JsGX65KWHm~ z3*=`h9@)Ug_-UG2A$lsQH>^J&TL3<>3AvX68hWCi|pOmbqPs**R zVsO~h%hjy+;nWFes}M(PRmCb(u{RHKDUp8GaA&SxUJd6JO4tkWcp_J<=Np^*$^yXE zUu}3AmA8!{r(@GCDx;858v}I7zD9_=Ce47T1F)N6{$C59||Gwi3 z8jn+{m)E}i?lK^U=Ss1PZf`ji2+QNJD$mz```#uVU3=T5R(6`8L`c5Io6RNbo;?H<^y7J=MY@w z%1U5jlu~Tui@F|E+})omXku&tgf26pyvwmRdPZZ?e*RD4lAxMHwWFY|XMJ+Nu*E#Q zr`ciuOSLI^jhTzAZb^o9JFkG$m!KONV;~O0@UkH>E#{BqA3na- z5bTkGwxOEZuOj-dcgYp7@fsL~QkV3lwm9*HL?`?5TMN7dG#MsP2jqOq)c@A`eIRuM zH3FI>NOkL2u8C3u`0_+_7ewdjnDf4TMIod)&-TfrLA?Kb>|KU+G}4AO7kBjL6^Jp8 z6B;vd|E=G|BOhmD5a%Zn6XGjr(Mq-4`GY4+PSa(-8z>RNm8mxFa{icKb17|V@Wf4&2Q&B zv(~YKz_e!aYUQ{{Mf)ldFr!kM#IBP{K|$Pk>n{=ms470g@j1z66L}D4gfO(}BoEn- zD8d3&uj(i1$^~Luh3}*+0VJJo+~JL{Z(}Rbzy5mQBZ$mTt>JWdL}croKPzd&L@NIB zG@{@;fB`jS421k9olbL{lr_fdt+YWuE5oAd8L?;D4blAc%ZsN=>&oBTvyar&5jyIMZ@ zKK-03eIz8h--zF~f0~VM*nit2MiA||Djh? zH^9F(q7W?j?Gv3MC~(!{c83W45lKiZn4{G*J#(|xSto)U9JrcA+bZYqx-0r01uUDg`u8B zX9_VtNMlK??nbs#E*=!`9vH~WYDH3LlQ>#7cB;PbXfn^%RFN$qceklcsm}D0mEKSP zR+I6~Gc;aWz|6C?pgW>s&ty{G4}<^qWZ?UD?hQR?R7q!;1zxhZqdTdCyFdBnLL4NK zEw=D*rCLFMAp6lHg9&T0$s3+;%0|Af4rYsROTd$Is|E<^glV5VKsgXANEx}8WCcJ! z@+5npN0vT7@xB7Qxajxs%r6TM)IUm!)Jv-WX7H{|`-ECD%D!j%GI$` zfuakU5pbbNw}kZ|4!e^}4?WR=_LlffiVu1BlKB-TwchTHjZqxL-~AGeKm7P@F32y3 z!hd~Wipl}iAfxgFnbIFUuv&087W)n0e1{VDZDUe#+|%y?aj-33^#NhfvT<zonRWT00E0*JrZPEC*9i=9>5%f04Z#V{mWJ^j;q?m*+Z`Y)1`ZHhqo^MaCW{iiWwwTpfBtwLOp+gQG|EcVCW zR4hzeG)0yxZ0Yexc1?E^pt>L(^Kh!gy=2RtQ-#ftTc3j6lB-NwLe3;9)Q^(%os&*Q zf7;%i*FV)>+LV{O_wz%{m|e?{)uNDWOT4ry#`;ui@jbcO_*tsvuQQX-Fq&UszBKlI zV;`deechPdA7JFF^_v$kKL^Q8P-hC{=V9t$38rC*x5AR{h9y4;!)Jx1JPu10IJx^v z%PQAH<>i=ZJ65CtJIGpkSt5s z-4^Rob^5Pr;Z%!`+CvwIdR^Iq7azPWRX^U_Lq_Z|!^-ceSJQ+yes*gi>h>-`GkdcG zRi}ba$(uFxhAgM41>>!bVHHU7F8s^iZHUs+-PBV{A}?2$I(fILyLLuu^Q+XmcT@e+ z%hwc*uKDBESn}qp?SLs*k{+87qN}8J^QOm99oDd{*F?OhtY0^Ej_lEdM8w#`RJM`d zjdj28KDb;@r0BzPaeC+%-@DNEqb$tRkKKm6P*P3{dofd=K08f`N*F`pUo>o^*6S!| zhbiZAyMGP;G@pl_p8r#lNgeMQ&4i+_*SjB{8kOMrx9;9$c1+aQt08@ZE$Zih^!h-9 zif#M#&s1OUULHJ7`=ZM(n>^687AZW5M7-@DK9(mrkaOI*#VC#JEqCDBU|LMNn?2#8S(xd+pA1^%$a&q`iAsU)wgO0CbQx z0k2}tXll9fb_z@`F}9Xpl0Oc%ZgvGI_ul zJHvUOCCJ@yM{CwzOI1Fp();hBW0NcB;WYZ@)MDO&BHvu~fqvLqhd z`1AYh`_`+L2UmaTv<4mj<9O1dVYvGERMzk?k~y@Tszo}d_mjm*a~%g@rl_Ufq88w~ z%dRc(9bmSY$Z}_uTSLdQqU?Xx580EQbaV^pW?=H*AG5SO>~YhokwxWW}jL$JRt~BJTv<~PBLNM;)`82Dq%;}s^y$fH ztJ{d(a<)|X*;E_w>Ks5NE-=?>D3N_q0$$=5{UA$jUFWR1a`T~FmwAHN?uVN?J^NTI z0blHlD!bkGSzZ1Uk_2j=HrFNE11;L&=WTg!TIatnK(j4h0**Dy5-k{emyCMs*8Zha zbK7Z#j7neXNLgBsEvn_zYl!4C&w8XfU^p9 zjgPEJ&8jB@-%EMqg@|!9WQvN#K*ZxZIz+Ph0C(c`{&*wMLGQNB{3_X?xDf)`vj)Kh zA!eaccLXZt3PUe?On+LdJ0x3=3k5Y0AsT|CqI3Eo?Awn#c+Yvi`N2?hkx;k&6`2ZH{a0XTu z$i{6kUhTz?>%L)(0@S8zOfV2nV|J!p>3{W9Yfb*Nb0YJiq`p73KAZ{(;^Ys2vV79i zQkEi!>h>nX{)}X4E_;Siu%|I}$;g)R8pYi<-=MHP?*tdm7HALZK^nN@EwS-}S82Z0 zJc%*dbc!;7>%NbCivGB4y?D_F3xUIxT+-#z*HWKEYp>tcwhLTV_&*G`<>x+$6Do*9 zPK;M7iWNSIW!aWvb|XqkxJ<4-`oS#__CI&UW19XQ)tKF0<2yH&PzwI>Y|BRp>WtDg z!j3|QBwmEkuiH{1f&$LME^%MDC3Cd*(W1tI%to~yYJ#{ik;?!oQ+R_bS0p*r_CqjW z)R$dWGsS?txW8TN=Y2LMjqAdC(&=GLiA8HMb6J1h(`c(gn-D!~p_LArpR#uJokW>< zR+Y{><0#S!RvjHrvT-Uc;dfCKMbB7Fo`cS~BkxnQM;b7ey^v0aA77yX#ER8w-|Z6N zy>Wu73zfxir5DrUKHZ44QnG`IZ%2u(;;W9;B4U1pPYvG{d%Nv`Q$qM;45M2vyr7M+ z!X5WvX*ku5fo83%=7pnpP>JDVI`&w0AnO8;6BZ=9j?oM#gql6I$)oE;GHsw=^Y9xK zijI>o1lB+ObzbCv$6puL%co7Q3+q3fEAI1=kKJIEf{5C3laJWNse~b(+_DBkOqfaZ zYmvCLJh57S!bA0L;Bfo4*#qZ_)C<13prLYRZd41v_BvP% ztuU4t<#~|qDk0E&C-i6Q2l-+5E@E{P=1%FBz`Sh&lmerEWz^=BQfV{vCSkY6MDCezk|H}Re1+|5yyj{+SYQ@#C`fUw)P{oXhlP!5KFeIwN(kU56y&Qbg? zb{C*W(PWA;$#qp8LVEwJn3>73`JB;54p*Cjb+NV}5x;#y1qDTeFm08~?9AKHr{bf? zDYf3Vl-96c1A2-{>gyGhtOy;_;_G6X69=O~y9YE+=Z=ox4=KCoddgnJJSMN0;Kw@; zRxNk=Fj@&z_9XXCs4&+p#{vvywc(n(C2#w_mTO=%~wcrqZfcq zd!oX5Nf}eG?|0yo?Kv%)*%SHp!#CDNBz-5TocE~Xl@E70k@^j?>(0{F-FtP(()+ld zO0qvBQ3O|Zd%n5Gn|?lL^|l)|yW?7)=HN8&xHo5nBpb=NJ8f?Na82_UPGvsoOun}b z7VOP9y`Du1@V+3n+?j?cfAeO2Gq7BzgHxCKdRm7W3Nsn2dVL`ObGx{N$FFf_Y)Nr3 zvAb^aa0Vxydo!dJZ}R2)h#&ToO0Gcd{R3w3r-(+AT%un0;rA{f+s2EVtRI1E|}o9QGfLIIJ!UddhN$nRAJv6N40e>^Eq6>(U|1w9-h!}=kALgqjT|li->CD z=$>~vs=Z|cBGF&!I`uHckIw72Zv5)Ed3$p)|L2D*67?wKyQVan(~p<>>&`0+O}!qd zm%^#D%HjH4EIQmZ20V|?C-1)f9{>H%_xR^(L+Uoa##;V4X{iH?_!LDiv~d3PVZQP5 zK_!g%3Nm2ufM2~ZNv&Yy?`}Vr?dszJaQOub5varQftv`DEZgT;P-`}jbR@E?~35&4n>1F5G3l2>csarIoh6vIH=#!DF z03=hy#@f$Z@=FbMg63KZm!o6_-DEN-=kXO=83ju*H!HO>M=FknlY78(K}u}~1}~&b zGV@A^N2Y6_5;hoIHU>O5!e!^!aUIi$h{rYz2|Fk<~o%-aHCtMwIZl5Z)Qpr#y6Bx-iAqiUtiP@Yv4)u+nd9Nnu_{ zw2o(bl6G&5n3k#gAcFqcsC5+W6+G%S4LV->+*-?|+8vBwmyO3@D~*;Xt{)UM&96l_ zvMMaA!y$%axlmjfj46zvlpK}xO;lsXIDgQ&V_A8vp{w9Y#Mm)PPe5F?$tEY;A(z`> z_0{1E5Phd9{oRx>JX)L?V_WSOey9!T+zxR|hOKU^^u5ddEN+tml%XCq7Z@{Qro$3$Licw$Z~=3_}myFmxjd(&*4ggMgGsOLylC z-3%ou4N^*nltW0jG^l{Ipn!m&$b7u-`OZ22!d|oX+H*h8ef_ot0evRDD)_c|4slS& zJD{!#YDF$@tvqRD*r$2M{L_|REe`S|2V!Va@>ovEJRwa_4Bwv5!okUYc_HfQzS4m* zcfO#l%L7D}xruQVmZ7STjyFOkvtR#a*oPd}djP;uAi?+HqprKVZqWI+g9AbxKt{xX zTXbR(W*lxf-fEl3B|T{01AP;aSZfT4QiDb7Lc9;hs=AS6RRf$?@{a=|Sk1M>;`gEq z;?U=#9e_x5jyziKrH`pLk3#`L3rZL<>$CMnQ8%&Ga-6b2t=31)gOk_q!G7Z4NRk*M z_ZH0Mj4gT$R!2SuL9X z1?I0X7Hqelaz2s@8O4mK#w4&@$tI@m#u2?!%|=-Su%)@ER|D;*p211e%psY*sP36W z(sPGhbF{>W!VWh6mG+c6F)zjDl>X5;4pTn#y(8;+|1)99I2#dS-Op55c#N{9_D;DG zS&kwrpr%+@FERP!1%$XLh-8^K-qLf>h+Pi`*nfs0{UK8Gq$5Rzz(84dE+0hSZ%jPo zKs-FZF5FhBIAgtiKJ^naZ4~V^b{9mR;78RAg2Tx2S)u=Xx?PdlE{NU4M_?SVgrXb#; zD2@Ruis;;v)NwV4kh=m?97Epc33NUfY{~*@IXE(F>0H$5G4`b8qoRq<)z(_GRm{oW zXp-|y3`&v97lWJ-C&-dl#{KpX+=oKlQ@l(9|y*mU(o6w7HEYenIB-n5L#k&0#Arv<(DW4eh_E7iTDlC4SzQH`<{H$}!R2pRac2ZryRgXj|N=P&!8rQKguYcIy>H34* z;J%?vtFb)oK1h*i&QljE9WqOwKDr*H@cD~tP-o;o98$z*N7a{7%V1vFZXS%rYwDls zvsbEl4naOVx+Zoag&Yp9a_DSVo0~>$>GyFs3d;Mq7-PC|H1P*VTS^8@Q8vyS3xcSM6)dPg&oY! zC#3uFSSWNvZYRV2f`Jpl`$?Ys_1T0XFM{EZYLRG)$b zV}^QMaVoFvJ&h<_DnyN=M0@6+UaTHf0azwB35Z4HB`KqP>;NuYs>II@NRr)pCh4>f z)AaTCJ4YSkm3T(xieKUFK_hJ0TudcL@s_AouJ(w0>XIH&j}&sSJdcN#>Azf#gOVBQ zF&pY<@hgaAJ$;y;y=b9&)1b$;ptv!gQDCE`=b?^?2TmuN>=^#5`lt*LNrPg^LW~>^ zc!&Ez6wldBJa}>|RdG+BJo&7&U{ImdE)8MKr{3SpGR<-Y%rqW*W<5hI8@-K%W!}&D zc|LeEetFTDRznN@ZM{~J|I?n|blG5WldeHEb~k$QCTS?U3RP^3YTH-e9BS?)w3 z+>a~u2?GWXAi$a@!O0L1zf|_tt3=EDo^C_P?2__w_C)72{9-8_&sfAnDP%G`&{auj zI!g4-15Drp&S&l(O0*tL*$<0M0@=srBm240Dx2y%FP zZDcFy!>_Y(ivCK4fy)u6^iksHQZh<~@=8%}W91pWo;=GN)GPRy3m`#7X!s5AOC~G( zt-f*Gc6yD3=_(L6wrC8r5DcRJE5m{xe9EUy%A0$Qf}RvSzG8|btb6R6oXkzMvr9vy zs5)XeFrqatoDU#eqAL)oSLS?OZ2}pyYZcdw>`uWl%SMTN^gm1T-RFH+d;emZD`B>0 zb!yi4X%YS=b##4ejNk=v#rt21gBMKg=f2%On(nKbk*3&XU%ZK40IH|_6m3ES+P!OI@vYj47NgvGFgx_amlJ*W4qrcy zXZIaeT_7pqcJ+tQWM6H#hdN-xBiY?GI@EVcLX(Pu3IL3nYIzN1bk?PhD*!DE3O6i8 z64LBeK1Wf_sx10N7hJZ+e5`w}x@V zG4CmM)t7Ffdgr-UP@RNzmk zXi%BVpEAYH%-%q-b`T>psNCjH=Ce)?VcS%W=}MXFilVDiM9~{ju33XWuivm$WnX1b z&aq5A$IlDO>iW|;QE^{=jkM{{Q`S#Eayx!bN)sk@2I~fPR&`c-`xB}>Z6Hf-{P`zL zwyR-4x-R@rBRB}C-}P2irs*jItN7nm9ga1UuGr~LP+m|w_-{u>XJ`GNwv4}>Ji%|r z59rN0x*}hLo4OEMT|G-OABz5T(c1K;N^gY*_YJgn547jT2DimZE0jr9$m>d!cH#tu zW%gCNp(K8dxZsyBWv_f>2WdAyYTUFhTn%c3jJkI>rG`MQLOwQIjN^h(dw<63L&iSc zOqz8M6?M+ollCP z60dH?yhE21%OvJrFJ;s(v0jWBA-JiV9G z|2}v1Z2gk{@>6bM?a^7S%%`%TpNbi)dm#gFdM}zEpG4mNy7wWzISh~xdIDAgpuq%r zA`ZG6^3gB?ai3)mtNv(ikY)?rthIbJn8Rz`_!OZQ!%l7?c96!dp1`J!SGZIT- zmtH{N$v&BJU!&=Wm*UNn5(QUKPc_AzjU*jwACmiPSGkZld&risnSZ`gpq9muKJ&Y7 znOdkllZM~n@Eg8S@zx+ioAC^j)*nfo)W@@(_C0qS9h5&;Q-y!^QZv1BG8r_+*|BI7 z8w+psM*3cA)%xAH=ojl#XL4?Pxu5y`-y@cuOgHQNyb@C=bFR?bTq?8QU_o2cQc>;i zn1#O#cDk5n!A1XE82yeWmAyZ`>&X(++0uV*vn!MF!26&bPhUzkzF6;zF&!L z=PhPA`=>s?@3VL~u$FaiMc8=ziOpC4E~ZBpknNw3zX@lg@_k+>bdke(+aexequKD8gn!^1N|DZ&HwE#Ww5ts}+IVP?}YTir24Jo!<7eJ#uMX@HBIK%X~WE z_Oa)3*>h5kw*STK^X$)lpF@|7eK(VzSvYP+`Du~=e4YI@?BDb3g>bz8PFEk%Sv{k) z$k~ix4x`(O<0{PAN)YO$`jUHBIxnt{!+}#b|09x+>z|frTc>9LyB7c8x1SZ+WiZVLG9$b?*z7Js+D36 z{-X3_k4k>~DhCQQSVrJ8VTunJnToMDFxl{Z2=SOdHgp`3Q;hxz{EA~;8;aa~Ln`t& z1*u&u8Cw7Vjq|UK#UWCCVVj{0jp%!H@~J%gsn4$&NPtfP@g2%DFC-gccwupMw3#RZ zohq7Sd#`?~2k#Wxy`9}FmOblZBVp@L=z0!sj_U>Bl_z?TI9 z#4`#IL_c$YV&Wx{6Ld;oe}@UvT}Au+)1oxOy-#VUAld_VQ}njW=B@4pLI3=hP6vU%h@`^QON_J4`v)K4aF8O zP3#gKat}m_P_mIL?-PA_m10k2o6C~9CSUku%Ct0{^haE@LqaJyn-Cp@{l2YX!>@VP zky$wnwo)!c$|1Bp_3WSYv8A~^Xmaz=^lIkRddc<;fWU4E# z0Np&A@ZokJBX&8LKnyWwuOHR=E?P*0zck`ul@)P2rp?AHT=K%i!=1cvIWqq*_li7mgap>yN?HQ(1MKh#FaYR& zwlWV*y^FC6>RB$akn|#OkmivyRJ#ELI=>Hj7K7fuR3`^h%pmCZAMmqgqlraq(?Z4i z2rf-!wVJ>%kv0<23E&Rb2H4M%z7VeI(PF{01G`omAQ#bi>G7o;I^z+|_fPT;v9O+G zzEYgB@K9VM-JVS=^Z}GQN6>7SdIPaw>DA)Ls)~QQGZyLNAzMkc2idRssY-ZPeK75B zzb%v%@8rd1D=HX)mVhUycNHs{I{w<=aI7kW=EfbmTI$dS@qJ2gAaBe*I7ef5m!u+W z=XNWm$iz*F%WJ=NRIh&zD{@doOESkB4dx=GJtVr5&q4lWIkmjAeTm_C|K(E^crDYU-LE2ia3_W(e_{Xj~V91eK+ z$?&}t)ZQ%rs<&)I<>QVYsdhve{mlzOOYc+2n#Pb6D-NpSBb!OD@qjd^mP72M5`InL zpV@gvw4_ISkZg8t> zkJcIlI8Uipy0^PrTOZ=&P%S+^fTJ(I-Pu~ApfrDh^x_XIOLtQO?S3*n^D@d48B3m= zNZDJ@RDH3x)%TX==_}dxUy}~Pj9S_7g)u|%GMppil0bYA(l+_!?6*yKYU7fE^faCv z02~Q`!9=|K2oYeQ?)o~hwNW}F=eYQ@K%#dAMqN&T%f+Kf?Hc~`#G~EOQ!4g@S8iwa z)|te8w47*WEMV$P3qdhSZOwt-kw_?wA(Tz5%ZW#Hj)UbA|0|3Ei8_(A+&}Sji;0kZ zjQu55;uwg`OtYr-&jkDnVuh2uh#2jAzZ5(!A{tI&PWbQ8)Rb{TW1Pp?s}u%X1a%eA zB@;vN+T&%DYq^0oiu15La`#}Qyl=XQ14#o$L>3BWwjT%U|VvfMlMxhx-2xqBWIV)ob`AS`giOGfU{%thSL_dLepwV8|$TaNzAdOanYFWn7ts z^m|S=Ay|))%nt+rpmcWF9OUHG%SaD==82%AV9IqPj($rijGx7u?iO(e|0Wb`BR($* zspxOvHStW*v2%ST@g?3O{WZbng9@}AgIH5=T{ci#x}eN!a;t;@x~Tu9W-||X{g8j` zK@^O72w?yiX8p}bg-?odbkPs(ETQY=GM?kOIB2@AIs-bWF@6pDHtNn}|| z`@mcDqF7Ypn0;)FcuO8pN7NPK5e2QMU=k$dEMc~0V?*+>=*lBHCneLKN||vyd?pHL zm_fK8=x(f-v}>eIC}oJ6q*T4zkWa;){E{*IbUYK^v|Zdyr@qsV5W_EpXi`eZc@Ib* z4b<6kcA$dN5d?*R-#4NpAS$HX0p6Vk_cD>_5Jl7hi2DQhLeUcFHG%+T29lodx7o5O zVQCWNTL(S4w_XDIi$RhbN&+NcVU+TxIEu)okb(yQHw17dE~%eS#dSpKG#0$*AP}C3 za^kKlJ-$&F{)d95p6>&OaOrFwKRl`6=?GOs!MUf?>=SD24(T`=@`Ih*YiTj5CCrLH zrt#nglzGC23P3U|eLsBQ!5R02Ze)|C1Fc1JC&QwdwyfyiLA7@>wU4Q&I!9SLt*9;F zq$%j{W1BPvyu3XK?Y$Ol?})M=NJIyk5=O|PGYXMDqyud*HN_)rk(6iW1^jHx(icyi z9k?A3y5aGSO_jH8-?>`RKgB8a;2-mDwPzk=I^vWD8r6 zkb0}bc7YH>R2U^Ze!2w-a0tqKbJ_6F%y}AOfk@z-&nQ(3mOhXgp#CB$WY+47au`ei zA;5R3L<*FJGtOsxdk;9V2XcXdSvHix#mRyQ;QmXut>G7YyHMU@3+MA|RLi$Q&E|-g zlUqMiXw#DRyg7Kg%O3@pL6=CrM*j6eM+FDm_$eIf!yp=f+5WhegI z;GG?#;tS~AyQhs5?L{28DB!R-$gbw_vTe@d2U|$nGo^HP5eN04OIyP`_h}2y2MGgE zA-^3#d;rePZSj&?_^9HTCiolkvB7@v=XPYUhBmBhlra?cl!|=x=`tegfrIB!# z$$oKz<8kDD`@HEJ#0lW&wu@$XWkJzy0^zkaRSDQoF-+a^{2i;61o88>^Lf7H9sK+G z5sfo{yF*d5|F6n`&?P^K3PUeivqQYVBHEy`XF+eWgKC$8>S=?Up9QyP2WKebU7;vV zC5F{^1FoY3$toct8-F_e{a@314olcOar^i}O+toLP23#%Atou@j;~aF@$pP3+8Gs( zc~vY)Y|qpND+DC{b<+96HzXvOjJ^MYpu!trX5g*;R^SA8q_aUd{gtII_QlWNCY)F1 zNJ-S^-=R9u3dR*7JzVlMG5+)cc@b6~gu{NOJL3te!iJItJh2|QOEw1H!dojfw4Jvn zjt5|hq=j8^+Y4z?b;x@y@j0{rm8e2!(yX#~sx7DZv^SLAjF}DuL_US6>0Ul_(u!wh z$D4A9pk0F0(K$TidLA$o>$sDWrL0{z=$O>5jaYW9y7KG8^Bnp;I<*wz7a(=-iHXSN z9>PIM6-!tLAii^^8<2#E>*!A(6+0#ciaS!fqA_~n#v_DK{*7?TD~PsK*0|c1EIxl; z4WtPM)zwEhlwSaHGw`Y& zo>$sU(QYf?r^8UG2~Z4V6hX+hSLlBGt7m=FFA;T=uykGPl3{qgD>tQa?y`RYJ- zsrt3C@`s_mOsFK_=t=SA6B+_5h9frm5%ecSO&({0-Tyk)dgcV+#$Vnp0e_`_+!@0BH3pekAUtF>a3@CDCCh~{XeyJ8g7For|%e?9NBFcMO%*v9)B>iUlHnDe6vrF=%OMB$KpTOWvM2jX- ztAX=!TS>e{*V^i@CUzjF;>7*yFYS16=Txta0)l1mrk-AKQ`^Med1 z4+f?|Dc`fK`nZ+?+-r6I#6kd&9*MmK*AH{J;7EZ%O_?`k7(Z8%Nvkf!Hl6MKRILaca# z*NCQGGiUkeR=N#T!I?49*Fs{=oIAvvIx!++#bp0`am!`d;lN?clPzDelYeVDf&1yW z(882!vH%FIu@4dZWB0pm8dE}>NBDVO>z!0-RfgcVUc(OupGMqPfN@(<9xL6B*ATxA zOOkaUM$D;e*_*}zqSC>RF;!7|tyV=2b-KQ%Bgd)qFI&Jfa$4XU;>UuJTzIC=NJ^Wd z_~oZqQ@*ceQCwJvU})9&&@l3H#_Hd&Yb;3QefO9I zhH8uQ#J;pu?rr>zp4(UhJ{H}fX^F~rJeSsybX-7~)*sDbX{?42A|jnb1w{m3=!h*2 zMAA_aq!NF+{<_U$wL<2!)**}eNTb3k#^+J2#&-#GFsg^MGY27QS@W50*JfZlup zue^SgSoB)$=4#F6MtYxf{Vns*XX=-4%0GsJz4?^=HU6!Vn5d2GAg1L0E$UE@S2k}> zLv~ZSj#Amq$b7X?xc!=UZ~(wH;ob)yUbU`qEu1Bo_7+BV%m%$(-cBRdtN+Cp@%}BG zctG?i-WGHT-W$irpr~*zm8U6j{^NO1_v~$I$KO4(FrbCSZ~E8WR_8sqsy|*qTs~oQ zD_x+Ln%dy6RjP7{gWv8I6)Ee5PBwgvc-C7@)P2xXm6+XI%Z7dN{r&a5jcXpus^KCm zx_C=_Ug?v}(Lm9a-t*ripyR;Zm1brH2*#w=U@1=tBPZ1y5djRul5i-PFY9h7C6e9e zpU9cDQb=XI7s1Kf$T5f^l#39vnz2+);CompJL+XO@d~Wd0UN3(8z3eoZ7`58?k6Bq z)+v~Qi-8sBA%q22bQ?-DnS3xNNj-S=3@(}@e#8cUTlph~CnZNfY+*3Tata=_KPXHv z1>l8GYMhlu3)=wo4TA6oqY|`(ze<0amB?0sy@dZRQR^dK;$Ysa8Bx-l}U1 zwENN%ii5IDbe!3tc@jdLp8m4zr=VfLilEITT!={ZFJs6pB_~(0&Sb-Xi_xS}uKuzZ z-6IwGkDyR2zI-(Em)!uOKZ{c?1h!q9)z=gSkeceZO6`1afYUzC9o8wPC% zK>~na2m%Aw!$-m+^$hWfky%F8`w>P)Lnx%7=$%(dV#=LKBXz>*Sx}%+_i%xLD~J5< z9;qz_86PRz`$4(xjy~uE6hS3fae50Sk-L4zKq5=xqe=y?3(W{++D`KrE28&NWwA`D z9??T`A0hBz7FXLnIO(*~uR$WVJS4+Q*4G{mrMd4ewVhuPj41Hmx(sl}cL*W+@Mt6x zYZd7vgV7LkElbxHAj?a=)@f@qP=I-8J1cbB2n7rUA|%mZ+QIRcEwlz6kbNjo<1g>&VGP##6WN6d{p)(RNJtprQ}vkDAvX z9MpHLrXST|Boh`Xu*{#SMbp?!CnRlFRo3g%t7veA*K-AG`gNa!j{ zf(Hb$5laC*nxD8DUWrOPlVodg4nmVzND|cbY~-OBW;UnF{TJJ@5GqNOkUXz3=FY0p zMs}Yz;6fB|XX6qg_3pf4Zw3hH;5ZbR#O1u7gi^KvbpiCSl)oPEH(MYblgOsHS-^FR zsYKH({R4khJps8*39z(ff+8=tu%NTQ(>^!caMHT*5K5S?p$Pmjz7jxuy9^5w+<%`T z_C0VolJp|y?)Rh3r6$8THy-0xPD$cuFQ!30=dfMIG1}muLS#Dbetb0=~?+nU8b17xVwv;03h7)M?#)#;6MTswjWFpum zS`Nik&{e|;%-tH4_;W)!cd@8U>@aPmA^Xk!(8$)yMg7uJ7C7W%`#bQ~Situdm zCmg7Z$q<&WzG@$>N)(^^hpr4M{4%BC8zJf{M|q|pHpB2>jlt8PNO@`&;d(Sl>m2n& ze)lk}pqz6OWWs4sQy#lZ#`NW$?zXPY&ZSPy0 zCM=5Qj%tT2HY?s}?u%1*>c&Xxs|j;fDe`naN#H(UrBj|n`c}_S&I9P_{>84TJ<%&D zZ!TBn@8=^$>jyE2Dcy6e*3|o>4-lS?IPB(9eV#D$XvnAXdfr4Mx>M(M&xjrJH7`!@ zdUT}x9c8AgPmO{1Lqqr7@$B@v8l%0yg{~3r+UGmAM(yg0O^4oZ?6qo5m&O*`IxpUO zB-fhnsV{Y|Uc3vKthM+(w$%IQq8`O1@Soy)x_%49tN}uo-Dx<)eGZ|OL_)k6j6c|0 z5;gNy+dSjdYj4jIWU*jol)ZVNcgD4tM z#a@Uy(POmKACXz|Iy4dF35QLPluo&DM!vW2JBK$KpIiPM`q#Vg5!_-^)sXYmJd((3 z?9&Nmh3vMS41nG5zz9Z30J^7w2TE+e+-wmAi5e-8dq*x1y@h2cRr_JNi2JMS*S~20 zk+{+!$ymiLZ@YUTxgo0J4~^4C+g)Q_o`11q5-ds${y2aQVQf%GYzmgN_6RBaW6grR zbmU2Q^a%QA$P6s4h$A-?oK~*C0?4+@CtQVHQV9px<%~Ne5_reCC$_oAsT2*DRhgF+ zA^sSUwBi1fgP7SAuL*^G&}q!$4o?D{!S$H#ViSFQaf%vtQt6PN73bp({>8Q}PlO=7 zzIrLUm1!CNwQMSUgb@WAl9;-2CMCY?MqO+Q0GOAHP9ahIQa_>4&s_(u9~6O?1g7EN zNLUBA*IzQTM|~u67%2oFzeHx6ws(DYtsxRR`1UV%zK9h0Gfb5N#}i~p#bH-Ibr(__ zSphR9JVgJeR@PhqAiLV=$JZynTx5?fJ6onB& zwsP?C_kJcUx@msd&#Zmf`;)(@>a&d3zjC;ART%73*53JXNNRwLfBhK^3V70o0K-C1 zQ`Mi)c`|_SMURY{4#zQ6h*H*q05fhpcDkT_9GW_@kEQ+$hl4=d0K#=5t=P?pN zsP7Q*nIHY%0@>%Cj3NLR?-J=C+|#ln9}W8n31-PseA+Naw|*U*e|!EuL9_q53jKJ4 zgCrLX71kEHn`DC&?>J@)$bSrG?44Q??QdQb18}gvbgdt36}q)f`GAk!%>Nk%2Z1#| zNqi*wWYJ34;ptl49UyA*3^K_27B(+6&An{wt_9e-T2~4Dq&niy0tXgk82|h5w)xk6 zgMZ`$$80<(r>;|$fBvC6Ps0(foCJZ^Bp7v|M_5-$7{(8EuM$Pa&e#HEwkZWD%z~f| z+z&m7&pb%0P9V9QtXU1Dx2MiH-%SD6OHesE5ope4bSd~AAf*2do!xy}UwI12tMD_Z zmBwXY=%7OBOU~hWldAS8jSk8WCkjdc(ozqQ35Lwq^3K#Fy={m#y9(k*!nz&IqGrl|ZGhws-6;Fmr4Hl;IER1W_N!~ta`+XBR>Fn#`nlG+s6l87U0VqCH^ zlg8W^=#%T(@jdg8<+${sM8^9w(cagENtc@&l0&aC(0jVUscAfD3|uS+&O=Q< z1qVn0NFDq@{l+0UkRBZZLH`A$DSct?kYy>_;wI_+m}MwxciFb)w;{JK{d}<%jXh{K zAizK*RSzj`X)O31Ms%D*_;cA0BSL!H$8nF_iCE8!R#$eAjd|~r2owzd^*eMC%tWm# zOB5ML9ALPv7#>-bw($l@Ltyh0Xo9GezkO;(F@T!H*F8;t^l%A4oEtxfjt4t1GYrBB z=9um|#h~!1xg+hypo+i48J4qsQsD1QW*qjpYyD+uV64Zo|YvxeW59q`XKf#jB#t2 zj-DP^Ea`k4d;cvjkIE@%QPr9(K9+FKVpkNo@EesOg*gH;ACV{CM#BH7=JHB~153jm z&W5=`@=sfZLzaR5yyzJ;T5Q(9ivsjomq*1%Kemw;b_!m!By2#0u$0huN5BsJHgg^j0Rjlw?-mz74W(mOLR%{2z<)>*rno zk#yVf^tS}X6vJ?rHfewBd_WGOyET8U(etm399eE$y@?Pt3iD-UVsu>B%^QJCy!-rl* z428XkhtqF3l|swmeDH9U8tWxQnQ6@iWsbi!#B7i8hh5#2mDdUNBdQEXTt;9PnSqm@ zdd*%lyo-j2=gK`@gv894v>i+9&qf#DESYRWeh&!&0x%_;nI)pqZl;2MkrF`z5})+n zWCguJek<(H6a#)&ly7-;8CVpV*MRbU-XwA#AE=?XQ?H?<&s_}ko_S7cL}_db$Hvn) zp!22fs}!fHbNB(7ut5G@HfyRUHQUOT5QZ7NW-In4lZQsGr|R3hIjptKZvz#&A)Fk} zxs|+6Wc+CRmCg6XEMxE@7aIsJc2N9Q6as%0e-aeAU)Rr^tM!&Y`N6HFZk$5M*cNOj z(fgDr)JB`-6)4Qo4!fhV4K7olV^;@SGdzoxFMC$uPy1F%CpcdR*KdeVYXnD%!(~u- zcU19LOrdgCqY_Q~j&G||R=c&#`|yXxvG5M%{SFA)IK;-1Q95I9UI{~ej|r(6K-Bi? zKrkm!Jv3j3Jop`WSNX9B$z=oa$Vc7e@eT}8l}~hM=HD)#hbr}E`YY51FacGqg~H$B z-C2e$c^7PZ+g-`6U0Ac8#o(Uh{GQdWp7ph!FMoSB7<;#ZO>*tBU;ShT2vezon zIDxCzk*f#l*L|V^AM)+;$(A{_H#+*Btg3JNW~ZC&3_PO12iF`;)zqbH2P)`77I$0^(6o*PBA01Sfz(#uF* zEULmwT+%9D3n-R6e(SY_kg}V!x zgCN{#V(8UL2hd|Ukl4|g$t_f{T}BXYxvshv-Dp5WZV*Ri4aZXyQI$tY{YH>BW0iko zO03REb~KGdVp9HNsS^Aa15XJQZ59AW4J>!Bb36}{eZ<1^=0=I&PjjVO|dyIcIa z3@F$+3h((L$+R*1!yB7M=T4nyNIQzJ4PmFu2*zD!37&CI<~Ebm%D@*jCqIe7QBpd% z0f-Lb1FZ|h*b?tq1HD;Yx7Xmr6!9dmVRG4=ogYasahD~uB)Y*QEqx@){VjbFGJV}W zZMZpgO+5XDYUZkL8m&vMVabP^p!rF2_ZVhKK2MYP*l4dzfY39I8CHyoR-jxw)Aw>} zbwtS$P`?=|ARNMbOyG&d&%FVOq2?+ZpbxY@amiRIUOT-Qd9LGLH2 zKdY-I&9B~rgC3Mox$N3=q>&q3EkI_2E*d!F4I0CBzo5#U4_srkAt;lXrC1WiQ@Q1) zehxooz;Y;UUeMay+x3~oB+YNGE;oxGhgMQPjelubsrDuS-KSl$2c@0Ti2;b>Yh4=P z0h(t%%HE3PnyrAuC_@?lUu{_2St1By897bk`azZ~^tV5n77+C^AZ`g#I~$Nn8|a}P z7&%v&g(S;)6NrSNB#H^@@hjmaU;#itTrymJYMa6iq(izKmXNJJv3e*%5H>?rxPf0! z3*f{kGh0GTX9%KZX?*?p6{hrp`DH4s=$wn8HpQ?X$XAZN9wZXmh*=AjLa6Kgo!@d| zPUHkU8$}-)AUhtgppuTrDbA3vZ8;yFNQ?bGkNx=GeZvI7XHJwEZu@cX_Pb~SM(X;h zZprVxKvb%Y2A&{$-8pchOKCs6L0i8;y9fc=N1(s!WK$}KZToK?WT(7N{@kMVLq3D& z%{xPX`NBQj&su)5d$=!no6(!Jsi&Yj@*0Z)g(JQ^nm<+TJ-_%K_aV=`6=TY;*TTX)di=GQyaFKOHYIvK5CeSQyh zuKwZIq!c3w@SMhlF`_ZVhyIIf>bVE_5)XLUsR0-H0DoEJ_N<-=#Cq<}b|Iy60ldu; z+@iU<1aDRWWK3+choOyOD{Q4yCuv-uBACl-0QL^UH`h|4MneGRUkVAx1>~YJJ z%=%v5TQZPdSNj%Cmq!xq&#a`eq)0aS6fAYl_|?FEWqRM{vjd1NoKTodRO9x9RPXOf z5_!kHBG6`(qV*{n3#NUc!hqRQCr`2)wKUXJT8mc%p@6)4)vCkv#p| z8X2Yfs+0&Xsjhg@gVpl?liBb#*HUw`4HC`!PK@xk&cQFTKKL!HjAIF zFbqj9H&_qpEm%|vUNtIHVRo(JA8bc!CPRvDtg>y{NS#ZO3871HVbix2onddmYu{X= zb1Cz#Fir(26Zqj2WTR(S_T$4uGH$%=v<-zM!8zYEz%pdsXamRwEp}{}GVJSnIoR5< zORfvG*&}0{0|M&ItH7o?$gHYg`zZAzbPMbTzbhBh6;FAH{;hl5+tLjP35N z;cAl45oYbO9!8W7cXuf{B}uF@J;i-D>oPX7+D%FpUuPL5UA^ZeryEdf~%I=I=a%h0@)t> zn#zg14G_|(jS{SpT6ovo>?_`*9#Mo)9DXo9Ubv+9Uhx5~qpy_iKjvxGkYm zG{|W3hpY!#iQtxe^iRNqVJ90?Vf=2q9`h#@++ZF7rU{uVu1^o*?o)VtJLDxjiD8O0 z_Zky$`s{N|PH0o4Qi<&+oYgVzXSUdaMHeYh@Z)aIb@m>ejS9R-4){hJoO#z8d2}IH z75X#r>kUJ?^d!Md2*ms=aFgfpxRtgye^9|~Z+mVuT%I{!?HMM<0MpMoh(CfH<{T;} z;O^Oo+86oAuq=|uRZek5RLIyfts`0u)eJYLf_r}PqO?yJmSSNK9lUHJFbu~6Hi3W!6MDk2IA!FfO|eUte~0pCVyCp?J! z;!aKNIF+lUQd|>X2gbtP&Oz)Dq>j84kRoN1%%4{gre>q_;)iZ>uty{%X+&0=*f*ep z;2g_b$KZ1VC4B$EEh)*Ip~nW-sje`JtXf+x4Nx&qe`t%n-rP>$=iRCKgeC170Hp8a z4|nWA!0Z*{h{fbn0wgH{jsxYXZ{+32GVw_#F|l$vo^ky%0IG$PSh>!|gkH<(!~zCF z9ytQ_R0truW=}Xa?KcHdzJdEc&?FjSN`e>N80mr0sc0qdJT0c#$5w%C$=lvV zuPk9g8Y>Tigy&V5J$N6Bvg?_ND-{gOGX6GBG^$8Ot)&o4+=3D z1j?F0q~{~0vHqw0N{%-glotHn2kIsLqWnzHM^slvnUW5lmPK8!ykg>bqHfy6j*(qj zY*U*RR`G&loDa*j^bsHeYG@9w74N=ct4q$wQ+**dvgE$v`y;KXX30shkLED6^Cw~nQ#Matz7J9(4lL0b4RTIm)mrT1Z7QapymXxfo-^o^I%ll)UbeooTg}MCG_rJ=<6_g5kwO>3Jkx$c~`zL5M!=>L%IHDfu zsi8#|{9L9c+jZiUqjSB?wB)ly)KTkC=D$Rudu{$gGFZ^vc69wFXcqTR zp_}5Jvtk5-z_-r2nF8t=r+kw`;(s;3q0$)jbOZlx@(EpIpm?-u37IWl9zs-pBJhLb zakdN%B~sj9{!+WZNSmvQC;lKo=Oof`*@k|WnGcg8Ye!zTK^!Z28Y#MtVK3N%8$i8t zgywecC9X_7;p?Lj9&KS8Rm^%U-JJW+fktAO!<=`@J#Lt$S!h`5W8kHCR5YW5LTpa& zV_~0C>K+FI|2A#X^d~{|LiUI1L37G}xRZ*9J&xM`tDlOXzE#R{PWsuacWdjuHHJM- zM(wK$^)bG6_XX`L?e50cSkbq3Jwj&Rf|f?Nz&!oL`|E7i3xl`5OnZJ1W+Zc1=t8G@#v*6 ziE>D|M@*APpO>U-qbz2B%MyHYHR5l5Mz-6%UCXljpKJ5dZz*T*&;6cMv^Y2YV6ZRu zIEdieq)o-+o+vw9VE8~-cI=8AO8D!?fsI>&&X_3{M%kQ=rO|Fq)z;JYtC0K67Jtw> z=L#t^%v+G+<9WEd|9*N~?^t_X`{6GooGSp&9aU{|5=VsszuAh)n*o58 z9&1#^{UrKuDhi~xjIwG#2E)^x{^D=_dgJT_)0jiwtbM=`rUWsNM(JW4TLbDP5`zksKOYgJi~}kIc!E1(q}8x^-H+d<5tD3VFOl-p#5^_* zX$qV2!t+AEkYv13@@)6}iMkUnoIm_-P9t-H5^aC*tLYb*P2JhZtTt|*c}k_jXo*z4u=#0GSm_@s+CC2Kz7sF z-65);E@UKh*h@H(%e;!4n5vI~YD<2Zx2EbWjNm_X=6<2dvTPq4(*RpEpg|>cUF98$ z!LeM0vI_l@7b>S4Dd`YNxewp~bXiKr@pVSu=PzTZi}DK@R;z+>bbvfCc>I_MZU7v8 zGrZjt?@f#fmjy-=qoTw_sIYhbD$^5>e%gRIK8#fQI&pmmnGjv1{7iKilbV}ZEuWK` z03j-A9jErtI5KrmoD%yaq8t;f6j$Lg_T_3cl4^_sqxujw>^U*qLl7MXFRm(3e;qOI zo~aS!64#_Dd8cpXxkN;lv&YuK>bFN4Zu1x>x-j7=Dy*^0b(#2oMBs0@b1)QMt)JR?&Mz`EVRy`3aIe8b2O4hnNa|Y_1 za!zkDsx%toBWBP)G)bcNlP}UMPWz^7JDB`L;;qc7?e8;ORO$Sr!clxrih36XHR~U# zfRS4;JkooBHoTu(r7=xwtTSgR$L3kP!jSaKyx0Js%Z%xNw_XE7Q!vAU&yk$VaDshC zm=7<_FDfCBKoamZQLQ8V+5>M3(@`rqO<}F*roqH_GYxPhd+{eaY9Dnfo7V^jQ5x&8 z^??~ar(Ht+hp7AThN}(Og+DVGqmSNu@4bc?jNW^V=$#NHNQhBK@1lj#OLUSTA$spE z2%<-i9)!&2dEax+_b=>q@3r>2_jUa)b*_-dDI5`^{U`L|xumDl={E|8T}jSuNie7( z1G^!FJS`fa%N%5Iu2t--ISE+Q%YhiOG3!b4&r11ciLuYYsKkD3K^LCPuwTv6hh%DG z>&hvE^$`>1_Kd!$iHOs(4zuu+Jz}Y#lzKG`hQKPoX^de=0c|2{DK#Z2)4XBSywQ7f z*=vW-0&eMIQVb7Pxzg1L^+b}|b=-3x-YR7NjdzK52ttP-^2Q9;&w#ka&)B>58)-ww zOO|*OIizS+?U!o7@x6I#q@KNH{0ri!+{(`-#q%q;ATuPklUZbw;nSO;xLPPv!xWIw zEY7Z^M6+>FGT)f$eqlV=*z79iS886}T{5ZJ_ka-79I8dLsDh>?{F1!yF=QnA9pB%q zQivZDoe4*36= z()InR%*@!h_e)wb@fyztoW|y6xNzq52+Um5Wv1eCO2a=ShI8{Lee6c_v{f!YYN86^ zx^HANs|b1&rR${?iB~L0Q$t9bvMnm)692xL$`qPpkwY?gV}D=F=FH)2N+BnmGND_w zXd!L*cr3bGnV9Q>00b&Va?M1(29oW8^iHF_7s(Y73CoqH{FnOI;UB&zYXZA1)%#`Q zZ!8+dm+L}OhHFjKI3Z;hJtvMPN@ z++c))5A~i`)@?4&KOp%;HmbjvJ*VBsZ6@deG$yR8CC*u77H^>S&6a*f2>sLprc8Fk zCALwoqG+PmfclNaxs17!~Ex5KEdiE?t%(55*RjG2J`QY&)Ev zXNpLjvWdzR+V)1;#;KSR4{Sr3lGlqvQi~BZ4^x-@EH8Ptm2@1UaUw#6usXRbU`ZJ2 zR1Pid2^-uD&a14^k0vN=XecOhW>hOCpHpkW!B7VIv<{ueBtXu*`hHD%hwKKUBmqNF zO=CeB@^zZF(#ZMNpBWx_PY;hD>u;p&&{LD&y_xdBu+Hs#x?7!cQ)(*_p zvnh83fErP*ZyWXzzQhs5O#kLe77bcWU9hti4`}kyr5f#}FD(wr330Qt_S-{Xj&>ZX z!3e4p%onXO94mOwRUpcCY%RN~fRyseU6N0MP~5Kv*~PkyQ=i?_up3`C)(IUtS$zEn zLl9iX0Jvfb4>~>;^VID^v9A>e)t<@=9|XeOZe=`L>JB<54p6)M+bPa3EsvDXV+K{^ zW*82dFMhEVczj5UjaysPZO6E(3-eAk8U7H3zK{j{6X!NPmyEecBW33BZSo&|i81OzA3gg94x#xk;*8^X(Em0O)KYlmg^!N+)ic7X)5%upPTHjmXF^z=v zm)kVlvfnR_F3xo&uMAbMj7+bLov%z@p|4D1uFT$DnO9s{e7>?Ayt4Z4Z@eWXJld-M z_v#ttwJqnh-Jd5-&E8~(dWx0-_OGs;W3COqJr#8ikpFP)GI;IrJwRC#0iXEnMXBk0 z5l~J%aGl+!@wHFO4+4dEnQZr&UG|yZ_rK*z8B9@rjqHPw^;@mu5 zyHJ9Ww9K6SxQ=qZiIxO(S`i%s9{Fn9 z_%2I+W2DfDTspZ-^jK@YW{w_W^=SMj8A*hZQ8#}^~z}9RxE++I)c}Cm4(3-G3ekbd23(A*{AD^Fd@CaTHzAfiINY~QdqNWYY8yr zIO$b`Cm)e++6JWBwVmm@cVw@x+a@~QrW~H%eZ#)D{Z>oQ72yhqKV*5+J2>Ahc;EXI zzxeo0{A%%E>-=)IK!W2|vEkpr)9E>xrSK?*f-7@WW_;K)yf2a!md9n!*5^(j_upe4 zT!=s_m0-4fZi14KAF6Rn^$+H)AwmjkdsnL`s5dL?){?d-f}PDWjh>t179^<1;M2&1 zwzZFP4{TW$E8dXVu;s-+|9%hpli5U=p2oPJV*Vq4doKC*k`a9_kM{K0+I_n9Z}F#) zMDF$^G~S8u$He9W2nk?$Dg6XZ48Y`b7JOQ2_ikG`3`r6K^JN!?#cBKw!_=F0wN69I z*W`rH<8Ah5@R&Av?4PrbWs5k^)VN-7OytYFIzQUK;G9A!#*pyy7)I9GdFr6Zvi)2Q zOEq#vWK-E0c%+l9FNMCV+7bF9L=No9RSmwF0X4E?oh1xq4Z!Y3MnT2Ob>6`(29E0A zi~C5~r&f5&IVTDvWQ>feC2=$hiTsIGOyy0pZL?Xq}sIE z>+wk+EjBsN)_Kxi<51@g^&4_TB263YM9ZJ-{?6w~5;Rz;3P`5qtbS`+0JB?p|GS*0 zq5{65Ox5-)k7Wj6JYIdyaXUn1)-sxvI&?S!qLo>(-2 zOyM{@txORFGK-|IkEtErDNuWsKC{uX9U^&6{9cGzhq!!h1DWTFB#C9TDdNaZsW-FY`^8m_ZgAqeDA*;#90t@(8gI9vfuzhaok9A z6={NlD^ZeI)iXsh%nES)G{G@M4N!o@y+w5!Z?u zu#FO(p|VGKE0{+?XA zH!V9^R)13vw-oAL!%YzU{IWb{5vS(qSECYARjc3ioMQ@;l-Ir9;r!b-tVujTkL!Zr{V@=x68h%oLQ2NZTUGRYF(Q#2+=jvv7tHnsG2D2! z9n4CNeil{nrt>9|$cJr52puDpMum53?0Tk@8Q=TCxn0ITeh0e1T83AwHVa4izIgPk zj0cj2tp(+H`Rob-?dNuJIeti5<$$zTcNFq{SDe}0=7;vcem*bamu1D8+>gas;|v9Q-uw6v z4zb6S*QP7|4ThQ?i0doW78ak&5UC$3qCC6nS;xqb%)u5yUuqju(|ao(Rg^^!K1e=- zY%>AHBW$Eflo|FSmD*>p^#dIFXo$C2mWac&1J1Du{U9s|8xSqi7Mt%;f5-`h1P{kH z>!X7%^Q-Ash-L#AYVT&3cFX@ z@wEs|SXx$iJoi;94h#k*#eYOFGnwqygw-OsF!jjhoenaP+I0V>3@2orewPlnat4sv z4Er5yrC9X63QDiqzTY*ik2Rjl(XW~J;F0`jF(HA)14*cio;bzk`rPAB@iJ=Oa?35n z@d`$tSlK5TmVzM(Y7r>oalNZVEpkB+m>HEZoJ_1alvC%FR3g5byCsA!y%=%@ zr`R!Sbxk@?$h7rGbo8Ukhy=vT^z4_-b3t*<{$1Sq>IZ1Wegi~ftM-sx41GjOw{cCID&)a5f~+3w-L*h zL)mel4MHvFtzF?x`$fzlGZctR!62C3A7!S#bt)B{A4v98Cq1P;HL zaR}y=Hf6*kmz)qJqK3I|xR9H6ICscC8}#iv6*NJ)P>lMR9nD4dkXE?}Xk}#fw3xCp za+lB5{GF?wZ>9X5J#9rCgHHMp>0Be$3ES?+!~j- zx;HttSEV^#o?G$6YTvr8bv}`!R_kF-0cTfxIieF8Pz_Z(y7ZEacv zFv#^Ugw{|3V-y4s!MsC%id5Cxk=6}1u@(K(rTX-em^<_yLC<{jOKRAhG7OV#bj>yF z(S%U0U%n`~dCW?W`4IYLzueF1Y>_%KnfO*n`u-Ym*MIp}C$O~>VpEVsT2mbk^#v><=rl7Q+zhX%T5yp6X;dgC)GykiEY9&^c0$5ghF>~qs z`MNu*)Zi&yjF(ooK&BkXx_<~>qw)N~F$6D{9z&w=_R8$X+4S%6_%Lll_+pr#kfO_Q z;a(LyArXmR+9Sgj3zAs^frjx3X*+CkfCG=ZGZVoA)5S6UubtHofdFO)b&-^ayB+P( zuJ7wN3G!Th&F1}2#sFd-;UT4o(@cp9mxb96CsJK9@=fH%;F0Lm&4V{dg*!d3QcXn%%f@QH^7cAaw*&Y#3$_ z75x%1gB)Dw4A&TeOPH-h|Fw%mZ9I-Gjj+@p-nd5wGb|)Qu@XP>YEM7HSaHf|G0JgK zhR67+$tD+Jj+{BU$kk$PZ1I+7384Zt8~C}oq$v&vB|tl+B9v&8uy%>#Z;@B{TCpQW0%S##L;S|v`Ltj{hG!sOo>}c z8}pjj?#$%H)r?-~Hs|dejYvM<(!!CjLl~AmF03aERm}X4#>9?!oq(EkdDVD$$LSxV z69;t@kT|o=%Hn*{qF{4)UXS3}yIj`-#~Jp*xdk{$LX<64@f?S;geAOThG~DD*m|{n zY?Qq&N8BphP8-OuLZnla9f8#)n*kOn*E~Q1_%2i7%TAgUlmQ*P>T8b11@vy<=YfQj z@7PJx(Xs4~p@yd6nHm9Xn&su17VKI9rGYb?9$BO)q1+PT_|O8AP??evb(Z%zh$pAv z3=KdLs@}?_oT_0)<(y1b|An_u1$naY@-nP`2Maerfs*hov~ZE_{?%#Qps)Gp4i$iG9WH<+CV#|Dd*GKaHe&uc?tc#Ve(sl1=C0=MZso4h6Wcjf+U;juFv}wg00lLw1Ch#}$dS%Ij@vPnF8C;}`Kv|7f3^n_==*8R zfdema#2AU%*uUN}k)bJyT8*1hO^3dT@*d0sMs%vp5Y6*posISrOrrDsMu^QqILm0L z`xnUUoog7Wivp)dWT$oC(KtCDV6n`L=btt*)1VhOA>0wGOYL+!Bpdkj4RFb41Sh3- zsrWbqa{j@2%$PSc8A} zI&6D+T-R{$oyD`tuU0b*%+|M{X;pyJ2Vl-0loUE`NHdQdaF?+PRelyiZ5(YyigH9~&y-p>fhPaqXLj_LIw^Pms`!C-+ z$JdPvWsD6|MN+qqA<94-WD_u!k`OM>zqtu=TrNN7##dY2PEzpEJbS&hW7Htru_65N zQoOdEv58XVDG0&z9P- zRSKI3=2FEI4qV1N+p7F9>UBk&8^oXW+C^{}HD=h)0l_!MMn`q0A1Z~!+Q%3Cv4e{h-EWxg0qQj@?0$0 zCLx6nq;#8~%-=J`sm!s%l6<}gyx_yaX`QsLOnK`SM~5du-^+r79xukRNxt^h<%Vbg z)M;+>Vf>v_aYiXZGszF@$^R1es~s?9v=r%F6emN5IR08c7oU2ZR-=I@fW3uX>V~*0 z&UrB62A zey)+o?WSJcIUBf>a4zA;mw9R)+0#cV$Rc}))d+YW?c78$Wo$4h+9v!FQ4Jmf z#lz=_Ld3LuOolu%g>jLeiy~r!3$qzy+z?!cdU36;A=*Vn5w2d6ut=SM-Y+h`Ys{UWAC#`~ z9EZ>Whu}VNIQwCf|FeO8%dN7OA#sE8rQ&<`7ieJ$r;V*R?Y4CegDCAr40LR?T=fAjU`~N-GR{HUq-e2rer4+uqNnc^6vy?C^UZglI46 z{m_vjN0Pl{kleV(?kPOZEhU-#u8DK`8D`)gjRfFkyMyJMrAehzf91XRQSivul0Sm~ znhF7^jI9F@K+5nXR&nW|=n1~Kj=0Di%%zQTr$)8PM&?h{6ey%YWd;@h?^)uCt1%g? zh~LR4;AX$3vPj-$LdvVGyiaie?gG}hO!T=Ss?rS5kgbgj6J(1JZGdsHDV>CxmA()^ zr!vtHlOa9d#dE0Nj_rm{VfxRACjSY1Yu(3))fFnSC8}szox9!8rjaJE{XUbX0L#MJ zr9XtPTkbTRmZ?%vSNiqfLZ+og)=$;?pXRiFkp=1|g~}FAvWiqVxz06e=2W>U92Sgl zs_MoUeZ!)`~*eWAvFrFYe~!Icx-Tw?%G$|Fg-7BqB9L;>2(LC}=d8KbY%fHexAOYrkgOUCcq$;%7M3k9G zJlb4=-xoXh;SFtY<>T5X?YH+oX7BIYefGa-SAJDaO3iBHJGv zR0rn2%&x-#&zeLFdhr zSu4L}d0gs8V?0Kf_K6o$ZC-g&$K1ED8LTF7uO30N%c}e0`_xqHS2_Qw?hrDjTaM_; z*F8qk$^#B+S#e_{tG?Gpo}#gFukBZr`vB~h{QejfbE)#$M(9?=Pz>y&#v8XM^`QaD ze83sre?Ry4XdtlC%1c5uKm_=e4YTNhWm*Igb8ZS3v7K@}5vRe|)@i$e7(s(6AJfA= zyksiT-1XM9oqndYx!RfOG$-vmNy8f-RonsXLU{z3TqeUszeF{a<71G4t8%%#A#NGf zN7wm85YAKCGyB{QM=J{hES`W$6(rXLUGA+|nZ^go_`v$tKYAk#O<9{Rhs~Mg2>}_!M-F$qq{9nk?WYu{~ zyi4apwnzi_4c!Xi=aa9RzrR*Q@3KlKU3oxr1XueW^)-!nN9 zQeL$(YUrkLzSQgbskPFma(6^}+D71?;oU=r81sRVtIB#;Lqp~&)j2WP&gI!x+h0`i zz36QkVoT4TsiN$?bdjMcygz>;Vu(2_AtCxzc$|9F3a2~$ykvTUedmy5MC9!LLRqxG zMPK77&U;aP@kGoq^r$NHvG9moH=fYg-gYGMC>IucvL*+f-0vwJf<&J|1?sYvam^~L zGburR<14LWFzZJneOHlbqYIWy@+sXP@TTdBU@QY8wN;0PNe#~jVH2I4XWrAe=B1zD z*2K?r%owGy^v!l8v?-SVW?j{pSa^nHS!%saXk3xPa!j6l-ksrR)?LSbt|*Vt&RFG{ z);3<*=Ha^@(etGjw{i^9qBIH;)&AW3yp7Fl8s=zqWkf*R*($6lh;P2Y^6EiUPrJ&K z+%%4|_QHB%CH+%_E4i0>FG8NoeEw~+GKqc)waWFbmfU#y&uAYVDJPG2Nv7;2<2UAlFV~@MBVbdi*wZSX4-sjLXALF2{O9!7#J=`-m$x}}euG2*L?}G}bEf7jFB`aSdnIQtV=f+ev=&8| z@3?@xjlABAn)sm;I&meM*lxMrt`+T zo5>Y-e@f(4*LEHRIYa_ob>2P{(V0g^3vEQz`W$Up?i5pgd3*Na_2fst@yG>{{oiefZC9r4$h#q}f*Qg;!uVUe z(HKnv*8|qheyrPMMR`inyYahwF9jc z9eKuwpcZQ)PDZwDvu`c3oF~{2ST)#xM$M>@O))bxra0*TcRP_%?6PoYMl0%++32Y1 z>iBh|*Sbm#e<}X;hmB2|_%un&bA9hGZ?Y@AZ3y&*aI+h&hHAePRoFdowzCUSH`weet41ff+QRy!(MT_+6tO_(F=|0EX=5IPR?m@GqB65`%5 zPU{)8?*QQ%I(ktdbBMA5X$6WkVGI`!I}ZAk53)yyM73O|x^Y;IontUHOC>AGM9dqQ|)%E9Y@mL%rp4G<8wS;(}itr^WTD+zO(?$=QXD$W%1)!IW$1H+o00nuK zvFQOn9S|qk085K){{PA;a~_cbkY)Gm{}ix$xEVLC_B39VG3?b7>P&&2*%&fiPDMPH z$CF2LVOZ|--9GGg>(3q5iRQS}BCcRy?Y;;y4nQMPJwFxmo3RM_R|BY$YfV>MV9UwN zLXWb~oKz*B-i}4BZQA621Af9YS9CXaiy;5OYF`#$tm1GnxW=+gQVzl3 za}udAuQext^&I?3=qqsMC33>=GDEC(cx(4a;W7OvaL-q0pHt0vyrD3 zxQ|!o0qXa4KvQj9>@9wY4dg|JfT6qdqezhM4p)Ms(ZuF5~~xwsQw!Tc@f*+6=!g56#W zZmbk4RfQgt`XM6j9d`P2bok0Brv}6Bx6!LHg;9GyiTa?i0@N00p@b&=DYE?4r|En1 zyG+wF%vcD3g9I2siV7pBKOGd81?r>M{xaN@6Ly`xWxNMc_Z<)cD`Dc5l%Hj`GK{eN z<14l>g!3-37lg3*7($?}Sj*(j-Sv6o`7R0Au-~iCc%edCXPPb{xPg!^Qrzbo5}ej( zXeJV31I)8{aqY(){c<>_)H!&l&#tfX1+I{@LsHnTgH6_M*4XeBhpV+g@&X6AStQvV zw0ptv!ZBq`doNaEd!2cE9qT{c(Y8#hhsoj;OS6aH2?{0^0_5sOXaERKfbE;GGCvp= z2=(Yy9*vw}>8wMBfE{y(#MwL(Jqko(F6>4qk|qS>-8!>1C)nTnq{R;B1VOnHMv$WL zaIUj)Uya*xDv`y+cmPAQ{I8dZsmN%?{2yxB^NaApo2ZxbDTh0$O;mH|H-U7h*(T`ypE`0peE^#ICBuf{i;W?j&dXn2s1z z#auyM`{?b*g%_q07N#u^jZM~fJpKZ`Nq04ybg>6o2O`!sC^F@UVs#3eb8e}VR{Tv4 zUPX*uRGxw6;9f~4Y$%G#0hpQ?X)pnhJV4l`T@6LVBvf4>D@!DP=i@dcB&aFqLF^2a zd0v-v3Z1{@6A>n{W2V_O$pEpz!`asGS#V#-kWBLXhjXsSTt zDbonZm@Qr^hIHlyXTxR>k_9YNGyzV}&>ChkaLp8(WhzaVu`I6gD55`7pqT@4OU2eO zY+|DjcRcA_bLn!oAXE(xZ{()j=XBPI3$7InfTf*$uR%lxlk8x#Y%+_ydocutpgTg) zN-_J(pzxD67|0HpphgPR8CCfj3e4^XkFqMOpCUoAB4vw$sUloZY+OXFk%b$8gfY~O zR9_bnO>rowk|{k`P%_P64+l_S^7w0Iz@-&y>qZzElICP=)#CkFWsMa1H<;AIph)4W zbwNND8N7;2G3WA0PC9!6jJ#l-NdQJZbbyJ1kwaQA01D)DWYA8Sh@K4^s$mPk2jQyO zLUh7#=EQWrF#&7EC7y=a?HgadK91oOvF>OT$jVF$uNdaTaENhECYXI^aYm=3?mRMkMHfcK@c;cB7zZj12cQxyvW!h z>WtzNVDU#oP>L(J`pTl&-?C8L;;$#`+(*j_a-1$>t6MkBnl9Wf6r&UnuM`GZ%Y61t z+{)9|N}2@>>8AK>i~2WjDP8QB0mM7G()`6o&LCkY9d1!ej<(cAVfMfnWfkxsMf6r1 zB4gtA&E)pe;-<5F`qC^~g{Unam=-Yzx@JTE?^2te8C1-q-Ce9}Ttt8i!UYk7A0xUy zP&oKK&Ik%Uj8qJU4U#?-PPUD!V3wocCgu_O^Q+9~QB1%KV}=vUffux`M%7^n6}V56*m8U%k`@^iq7jk5u!c{!FJU;>&-+loa-p zY{c&wu-?Gb*K&+{R2Z+J8*0F8%LxkL>F;u^Q~i<|@-a<*%%gmsdkI2=157*IsOR-)@D43M&c5&|J(e~3M< zu~1>8|1L4Du9bC{;1I77Olc{-KZ8P9H(2e2sK}9%jaat;y z>qxn`MuiS)%Be@5HoopiBjPfyesLh|_K1})khSta`N4KWN(vxrRtF1Yctu=Y>K64} z?8Ej2<7HOv`nr<(WyHeavn&HQj231h z`Bcalyol)$!#_8S`23+3k|a7^U^*|HkqzjCAUg^5x)+ReP8&&eOG!ta_CqUe+_s0% zIgN7@lgE5jXbU11pa*ZG(+pa*8uYV?d7AsDONKXfzXY#v04o4UbtJ~>m-Ft2M7red zeRV9uwb2SiTPox2ePmG14}AU4uh<3FNje|ddEU3W>u+oUxO}!8W3L2mE{SQXLWqFS zUC`q_LPU?BE; z7f$)Q33`-CqYHnNnMf4Tce+qQ&~3qd=Vsbo=3^Yu6UO*703Y1##miFeEJNs)OXii! z@KUCSXOu~-EAHD(=Q&<*_cY5~LfNxmVLF=X%+u1H_|EFX_9pi)_$MXZG+3u7#(&VH zzX7Pp+%JpEwO7zO$xX~Yi;~pba!(8FLf)e12X23II~N^Qs}x$jVPj3}3~JrSU3M%` z%U1m25C0SxC2VuG0N?=B>N+P{It#DzBuCw zg;?7k*;zh`eu-G;V7EpwkW!%8z5ja5loN>bfF};7t) zsE#5iauHN?;qCOaO&woa+FDtT+N34JF}}{Z2-=X-^|*eI##zh@S)~je_0#%`iSTr8T}dHvUF&1?(AteR2Yo1(4Cr+zCWg zRgn<36mREW+=91qYZR-IqR|EX!ZKaziOht$3wV&C$lmK|U4K$8*lCV!nyQ0P}ityiyf z|410^__|&v(Pw$j6n>A!aCT5pnz zuc+{8)`QUMl0id3|vaZUKHFhC2GmxPAkgBGOIL!19|pI z|MQc4$yQILIo)S6YUW?Fm)Twb7Bs_WNJFMEP}8?7?QfZA0};eYK>8-}9IGOXfYlcN zhv}lFul$=>QtNMkKqy}~cI^_JvD)5fxfDKeS_JMlu7nt>)`zZ+dFn41{pzn)hz@yMUm<7>n+6cRx=1w z(Ym^--T(Y}=07 z2|G`o68JGqdV+jRd$5z3X);W4CUsc)h#8pT4m&!*j*@^%p85Yn=L3$ot&fNiN0LKF z-$GfI{~d4rb3N^3>w6KgU|n=hJ%?uatMl^Imu-IZ`czubJ;VF7<*s1Ye*V0_eU%^FD53ss9yojtYR_Zmzso zCjkJ_wBKr1u3kZLf#AoBnfD>k1xdh=q{qj1OVY)s{+&$AM2DO1hwUe*qSPe%wVuzy zn?e8fA5K_klBp4jeP6!yeS5yi%`gR^eBt{P&`?Ka!EpA}`nQq%u=e?XO9TK=#%FI& z5S1)a2)oq)tQ$FO%BfuT3I_VGG7k?UY1?NYe_tvu!1G5Yqk-ovF$`OcRM5IJ@yX|tXDx0^x#&PX zDTj?gAXUBb?IQf=H-hAB-qLB*PF2niuTJ+(c&e_ahP<_KT=U-7Qp0*LZE2VjMJSMU zdtCZuSdZ7nNSXV0u$UEC^FHU@_OT>0Fy&C{jg`+*B7E#>=^qG;Qq8gkfE;ijk*apl za2F6e1@tTnzd#qqUattSiQUFh7eQR@?E}S6Zh1Tu)qp0GH%dl*eLPAx6bm@3A)lW) z7e-NJC{uL^DGANp1Sq;t*KL!VL5jj4eu7M8Mp{e+sjqUW`9K$cTBVhiL1)wTjBm%p05xxS7oi=y~r9zF`qYaUycrS5%O zq(GyVL!u~eW{=C)A*4S4&WOg(;hj#kiyo3yqLesK%pAu6uZ@eHE0%`&ZnHSBn#=*! zZAA^s;_|=RP6poZS1J7!6TzY|fuHr@Xtwrh(iNRzw+_NEAKMp?pb&O47HOvqu(5?06f zrroU7Ex_k|x@vDWlB=!pX5osq8GZ)MW!Vf(pICylyU!`Opqz=8MgE$$zayg3@!z+|jh9itI}nA)7dvv;u#&U;mo< z^HW=%{%TH!ibm!`u%GAHiJqpm_+}5OJhx(Ibe+^zlDpr>={J7~0x#Z}oa_Es)uo1E zJN_4ly<$Ip_@f>O8xsDv$f`Yx&F>z5vz=FHd56|g|AvDm*_UU(ZX*ii9~~z~ev|{$ z)j!trpDX*70A*TPDayLL#b!Pepu7b`?nM?5TYvQsZXZYzd^1R)W0f7YXy7XnfaxP35R zRSx|9uU(!3@KAFjMFunyP*!jAAEw|jynjU+`86y#v;~_9OMR%QR0F5p1)K045VI>E zfOFy-BSIGhqILV=b;euAN@&tGF9rv27 zIO1xGKR|p@6N2LDxODOaO}dRxPE;Un)%46Bx37*ynNZ-c+O2Qn@Q@Zol-iU3QKwe>tD5C~k}}9M&Yd zn`cbC#1d~9;AoM_@3ES%Fru!KSC3Og*J+%Wab&t1QqnB68eG-AId*rw!(Ht9w^t9j z&9ot+<;h@?sElH+w&8U8)UQF&pps_8-<}JSZ!O7BKdy0>=Ut??LEx9@;koF>FV9t9 zr$#=jb(gHx@9n&9jC=3t#_VqSqxiAGewx_nkn>|5_fIP4O}E%lBR+`WRoj?noo}JL z{$Aa2^~^D!|A!w-At&TtRtUV5zTmB$#=ZKw&0ilh8NYT}{i@^0v;Or@yq`BCuR1Sl z1%r&cuyw*HzukSTe>*z*6J0IQ1rlg*$EjH+u2E=v8Lpzz$!kWe=Jo{Yc^Vd_49C(^tVgTr=3%IS-MTE&x?If2_a*% zgh8_UPoBNf;j*1RD)A&4^~%K!D)Dc&*>pb*Bjga^d{mT8-P!pDLx_d4>rEp+a5$!r?$KTJtRkUBkkD~YISnxGpdsVm+41eGv{vKm(S^RnS5S7c6)a#-u?UYJF+6N=f{0cU` zEB@xQ*EJyEGYje1Ao3!cdW&9v!zd{Q%yzVmg$`BH`EiIH;gNqAXP%un(5=35@n%f}S+$Np@9QlHi?V-N?FOamb>`s?g?k@n|rUh6E6 zg3Wh>R-twqe>5`~PO+nRc9K2sWV13Deeh68RKyLi3JFAin zWpx^~UH-n-@d>-6x8NEr*|q=l)G3>G)zJsR@5M!EOl83y|5a5A4YAe{h7*M0vVvhc?&&01W`xAEij}9z&(Nr;A9}<&nUpTE=+R($ zR&sqdicNx7>AZ;z#ETUnD-}^2-{~}@UHgY%zXZ*a6wmB^NP|Xc*!qijX*D z)-(jr8Wlbe?E^r>Tp-By-aKo3>vR)|!U&mB4N;1S$iMMR{Z5xh6&^*!9{`CAS_}aW zMX7H^fwQA=)Hss-p*#&>Hz9D`Iw3xDT&h{z*@BibNi?4t?x#v@yTydq!0@??SZ6bq zK(5G@S8>dNu{n!Iq78T*DKzw_bVlLWdoXcul%C0%Y09jpCmSY*4aSf+CsHv=!&a{j zEu8FFjKPA!^E!+EBWcUcqCv5f6tN*sN}POgBYlm0+w?Ixd?Pu5Bx%bmiR%H8&g@P8`KGjj=YJ7BZEF03;P+q)ic%&UL5sF}rDY5g}~E z3c6KHUZ%N$Y%$x2X@Res1JZ#h>C3(;f{hv4)D$F*ghefB+_o9Uc4>krZgptt0~a~- zX*5vC{cAU&E)(5H4i7#BwX98ir8f3Vy-Y3jHhO!CxpPVXe62ys{th%*7p2g<(3JYg+a0Sr8~;U)R$<$i?Pmx>yc_Z1c@ohVUanf8jDR4Iair7$DQHkt}UaLg95%& zzfSAP`?jAa;ljMY{RCOqfdK>dh=XjPWR@Nl)c{ zbB(BF)lPrJ(XRzn8;D#{#;T;k|8NM$hXk-k;`7fLE4g1dm`CM^Y1?%d7Enu&X^^Yh zp=fY<9xe+ld{Fc13EU=cxvI(RXYi7+ocIrlm}E+<3@8<_N}UW!rO>%xwL6<$^ZY?+ za()`X{BQwxbRjes+7i6bVw!B~Re2YZ2T5R$aO1X$wv>HQPgWIRQTmo6f%wX2-|FU3f;o-ixc^ufwIimsG zwUc*Ur~yXVz<9)6q*4XA)mV+hn0VwpBefG6^eH^Tf`CR0eQ57;EoojkQ+y-tsDuws z)A)X!U|6;3#s|kDPzE%_WD2VtR{zk~Sa{HQ)SmnQG4+;FZFLQ}Z4xX&1Hs+hp|}Kh zcXx_=DPG*&-Q8V^6u06~iWe_lpwLoE;qZRv-f{2mjIn=a?6vkY=S=Zz`t=bHJ+7$$ zr^ZpCRaK)U?wBb7#EPp!Y_$|b@&}K&f`PQP_3Fa_i)WeHWWtxM7E zNZQNS|4VomS%{Ltn7YIm4q|6__`kPLsGsX7Auhfnu@hrV$IxJ0NFYP#FlHK|_JwWvy69`iq zA8kGh-qc50mYeQ91O~$xCKClEpK{tPcWBM$tkL#T>+U#Y;KyozCfCo5u}X~AJ|D$F z9h;SXXmnL>OTCNdYR#W}!Cbv#d7bkB*Gu0jW-5zp>W?-zeGPx9na*S@ls*;!$Od|u z&NpatNV=AKKc+{@(e3h190+HZ3FaRNcKeBbq!9?05@yK*eSm&&hlA1N#D_-EMx^MG zTM%8PL}t!WKT``M=kw3y45DofK2#4Ow}`;%KlO!)RR3Qga57>IHsJ53@aZILO`A~A zg8*^~*6mkJTkA;R%7qVYp7pt0pL5$qWe?U|q&P5#JieiE)h`M#k{&)`^np+U4Vfot@|W+@v9 z{#Pxor$|EO!Kb`sLFGQ|wlsbl)}b+)Q66-{TO+C*smVesk;HmDzWhn<{NccvalfDA zCU_GlA|xHeNI% ze>iqV zSZdl87o=xb<7Y9I#i4iV-jcF@#d+`co#;*dQ*+Sur z47Y{nr@h3Ofk0Wa0tJ{9oZ{N(GEmWr>)zZJ2| znN`Hu;{c___A#LP?AQERO9_Nxb%pI_)Y+e3bS{-BO%$?tLCgsYwOPC@+e;(ZU&vHS zWn>oDq*u>y{Vqoh0+6&#D`;GFbR1rgrUR7p=}Hm!uF<>INpR@YsZUwD{0J10H*=_n z9CfIj{XrFh8&q8C6)kG%R95{78cB;Hb+aq_71-)I)kyaoZttiIx7E14)fW=<#`!fk zI7??SHjKw?U9+}S-{@K)ZR;J8jHc*lb@b7`GgF~?Po z=Gy}kTm76P49W@gsOIh9h(el|IqV?L!(Zx$a$@>}%=#kF2Xit<&pL=z#oRiDrb;`9 zY6Ax;T1}R7M+}ym#9~HWsMYGG$Ie&BuIy$gnBP5BzrS<&{yy=$Z{v6Wx$l8j-#=U( zKbE38sp7pLE50B)ScaIVcARVkXvAQiAg!KgYk;gNTaY`AqONfsHT{`2PP52Q1Mg3- z=}y82a3Ze`Qf0E=zdK4nK5dq9O?y|9Sg6ka%MfmBVYj2lg-_q@V%ir(J@8D^Mvl)_ zOxF{sZ}S%I5+L4>2N)bMWQ3#KK+#G{K&Vdw7M2%p?RgcOFJ?^-d}uG`=d98yE@s#< z=7J126ECXXT!nO;seCzOc~L#SHT{8ff2QdD;iG{)vnjqdy^&w1Uy+R6dwK&<6U_v& zDfk05>b&EVsrIX|vm_Zi?On(oCDNlN9|@a@P_EqO`OiP zKfM7{(+$nB0WsGNUXvl_{Mqkk`JZA(LP>HWYUir-$$N1>G|BO&A`6={f24k~%jPra z)onjPy2nUrWzfvE0Rk;}4J2@gwVDj?>!hcqLs(Mo4ae@4lUhyZZ#!+z0(E{#>E2&o z{d7dQ3#P+9rSz-$4X=#ytF7YCi1LOnP|;PQGgHu2S2Z}+g@@6f8$%xvEK=wT0i^6!-U z0}S?kc4N$YZ&g6x*No*a>HSE|e72G-hY`@-Zjb2 z3%!r7C{VY=?UC_Q834xnT;HD)mpI7)Y&zAaN2rTd?-1k9&S1 zChF1ph^>7&n#syb_Wsa>P30(V#?l+9+VtwhP9+51z%vf!WcK=OC>5bQ} z_Y89E?^|MNrcCO?m{+Tb$|g-}dZhbIln!N8X;L$GPT`=0wFMmWl@iUg36kc8ySquq z7Yy zyqj2%Ww~ts6`XL3q(sDeXSnDkU=gNxtYB#({lSU~&%1CTWC4-I;5(|Tb&6x;>j$}z zkK%_fvr1r?6TC8ufV5E^1^deQedURAH9_}#LT^IL7`A%C`S`9h#u;z3wMG=rNAq`Rsopk{DDO}x+|yGU@EeJJ8JBVqt;J7G!0tQ-d}Y&!iK9ww z9m|IAU+RBw%n#@^I}wa&Ukw2}G^A8ADfdzt8ojM$!?c(af{B9dO1>NRnhzp!xZoFXrzhdy;GEd?Vm=OB?HX@36i2+rCN3Z-cD>sZhf2b|CH>VBTw=8(CPXT z^ye}A9iEZjGLamah3_ez=*{j|x&gDPch&3P=~nEac9Wrbf3|(Tllcei9?4RehhW_b zzl%6cd=K>}Gn7Wa#y%>;1SW^jwk{?*C0o(F@yXtJw88rDge)K;5GyWnf_0l6WltGA zChPawoUV?47b_G8A$}MH3t7?A@=~GdFn)|LkU{XKS(!E*Z-rk|+UhHr6MFy>HKA;6!@%%qGAM|d$JkIpzx zC6|^pFAZ^)M<`9}cNyh+tWmGY5y=*=$iWn96!8;@+kDOlpuPoAM|#LqADZ;CyI_U= z6NC*tl#CpK9Y_y!53!LedUfTc@5y(uOOdCFFt|x@24Ux8QtrF7=1u-ly%to^!Eoda z03v<35n_@(O!J&N@XE}NR6?dq%hT52RP$}MyyC);ez2GcCj?(!p5@>u9HHnj$@5Dt zh2o@8Vy)#lKc8bstbgefl6ts+bZM;Tkb%6RIb(z++1?0oyp zW=k-#k_D9PSX@eGX|ov z7p-B%%&DfHufWNrH|7?B8z=he?vjS0TN$5Lnx~iUs4pO?`_Pa&tG{T-iAt!4*(wUk z>o992D1NCx22}djBS<#zY3~Z))spO4U$#G%L~B}<64KS|*`4a-INcXal~YAI8S>}f zu2jJza8KyzKIVL+ZnVodxzb08cP~Z4NiC%l6kh*o9P5&$e@DkjAxzN6uG*+nk6@yd zt&$^rro;QyJ>xsF#u#4oXU3#m0xP-nG1>+oN2=lNg@%Y%fKM zdB+?!qxXJU+8fwO#d(i@t<5MMyJT>`w*(0e(Lm}wk-dB%UA{T2#>@)|QZ$kY0C3z3hLGQ=C#cyu$sU~Uj0=R zT`G@5c!S-KS68SF%Fer%2lq?5Lz^$OdzfJwTpED;8p0a0;fvny?l1zsU@f4E+nF{EKee70 za{UaYlcm;);9|}cq!o&#JqcXpAi0LJv}BLB)8HFZ;|0q4b4y29@g*&68aEa=>#KRw zFg+l6f-UIq3`jONY!yF#oh{;Nmc%f0oBbwW2+^+dqhSC)45Eo;MZ&g7`rW*<21bi? zKM@N&Mt0R;7hY7)jeXmQ#B2O9;@FJjzc%K=T4&Ngr$2GFUxHcL`w==kM0Oujg$i#(PbX^_0K3oWo)oC90?t8%OifboTuy_ zyP_2Jg@pJe>=Pwa6lF^QNDDu@4qX=(f2pQ{d`BDDbF9YT0!hQ8oDSguK%{!~@ zfe}go`aV5vienIm+%7|$cM+qZ?a|4i^KJdn@G6JJLMUOxJHTKy3rj9s^3X_xup*Z@C1jgY|$1<~U8J*B527df7fz#k2Pd~SI zKTm!fZ+_zsH1V!%Noq=zaU6OfpLix_@p%^T0?&|yh$t5CsFH{1T9_mODX&d!Q^0Z} zfjnzoG9+&|DYrXYJ3jiP8FHEob`XveG)&iNmq|+yt4E7%NQ~TPJG&>MkQ! z8k4Y$XSqW1tk3qm03`82Hr5}1VG`;09vQUFk1^3salIS=3X!u(kJddLl=L0%c{MNd zPM8FYF|o=L&k*~NmbtxNlR3ybw2!}Y9~&K(@h}FxPewvp3Krar+FTnpxtU-QobWT2 z@jV<5$S?5MmTzH_lXVx4tCxF^J0a>n>C#7AcS`5D79LdJnG!9dORC_9E|=jY=hQBr zlRuSnF=n1GW_rl+d|S0&I+Y^=SmMbe(h$t50PODQ+mrG|U)JJ$L z0ma>-h>R5*wiFvLrpuKpW7dGIlwleVktNrP?aE3WGYa|b3OSriUDjC!hMyhU6kXR) zZ3Pe$%9RE#ls@&z_l_#P^cySU3WQ04^KjNe@BS){J1I{X<2L6fQe-3h>m#@MD9_9& zmxU|kugr|Z$j+myEWW;h)KxHjnWj$YIqh4jRPi1RHWqV6y z=R)P%lgci->bf>iY&ZOqa`cyj&p4C_@|(lxYoP`wpO2j8%5cXOl~pU+=VG$w_Lo)n z>gNod>bsv*Z&~NRI?;ZLpSeI+i-`ty84OnQ6n1#c2PjMz#JA5mmHb6d{1*>?3|H&q zC4%k7*&=Y>^r@X1O;K?+rnlCv%3RA#b{801ab^Hu>h=(LH@Q z7)R$s*g#VnyHLYCalXh5~s(H1YlC zgfpf4cC}x;WUqQQ+s$Fxd&Nt;B?hE8&d=VAm4|`inT=2 zv-vz_Ew&ELW-y8bVVE@MqQhrU0Q40D4Na+5_hdvwf0NVDGsf3Hb-DmxFo!uBr03T4 zXd6^XJ}k;!YMJ;h3luD~S!=HoEpzLXow{ku_e0eZ6uI9lXCrDVbW9cs>r8O748(H` z#A~w1po0lRHAT_%FBMO+zZi;=c8xBX{84|tn>$(f64R`JViC$v%qKxM{|1l4ykhEP zH!o~zK5Yv0visU^w|)a$o;O5au|!(^i&$r3piQHcL1RZmC8}4MCe-*xd)rcnm=$Q< z!1qo?&nrRE7fHROR!{rrtIm-U7A4{|@35h02z58&(E@7Yb!?YFYS!f~2{x2T3kH}|^^!pJUK8WG1>nrTsI?a3p=|K* zzt9|XlbFG+fqwn&>J0$gxIo^hzazRfnry-Zb$AW69BHwopKvK-YWse^&3PM1a{G1t z7m>Ur7sKMM)o(V5)Y-deNYOaox%CvSDXi9g0jJgUZFmx?D_16g!O*~x=m zJzKqb*tiBpcyrhWOc_53VagByfp>ZPc`_Xko4gQ>r(0Jd zBSa$E(5e=jNRSZAJ>8iK|G;Ng51Sqy3lt$% z4t$TGWS@lUz(OK*ZOvSBBpE?LJI2n6&-8$4br0#ud`6x?xn+-!3tzPdBH1Y~QHaUo zlGcKb3ce)BfMQKT6o9`-qVg*%l<8d z+x0P-GCACV(WdSC8>(tkv;c@+2(&o}^sElUwj*%3Ap0bkJqV#V&Cz{`pu=ICTX^yZ4lheZvpicnVx7kiFsTaN7t3Ap!d%#NXmmtWFz65H*UQ|XmFN?5?#rfKO|(N26WocbOY;0NgY_Cu zWAo*tAT({sa5i&4E`HqKGUV@ z9y-B?{oL8e^qNGcfY#$&?3T!!9ONV}<{B*wUSE%kFSjj-i;$t=yiP^H;2 zE$rm{N+Z{fnVO6d-pny6|Iej%U7-!i~hDVNN`*5K8lKhv-;4uJuma zc~~>hjuX)=8POYn;B78SF-m=+$a2aZg2Ix6rB)Y1toCx={=+viF?Pc>#EoWnd$8?LEStgYC$h!=83dhD2w1yg)0c#tj4 z)-+mwb`GrMi=1;#Q@pLcb=IgJ15y+e|M8f%5gtxuXGd?CV7?iaRAOTGPPm#DdRxogBtFCJ@?!e>YXefLl!P8OInR09S_luJnXqD|WEn4UZ+o5rfeH)9 zGi8frelZ%48$}-E|CL>aw;3A^^7UEkM^RoQSxmM({^VuxG|x@NG!oBFNl-cu$CS^y zjt^&bnXc^jlGk-MBvt zB9YlObqfoA9C@cVnO#*R+|1-xd^DKh`sWD9pH0DEVD_eAxmTjBFTdzdK^OlwJFGG( zp<)8R{t&l2nUH<2)b7@q9Lr3;fth$;R z>H@Q_IZsfupuL~ONi=SSyg#RpH~E!ot-OtnWLW!hlo>rSk2_Y>eR}e={NYb_QR2rW z1yb<8#iWnD?*+cN2BvoPB-Z)%rv+{>1WKJgDUk~y41bI~T>Jic+T3|;L0|f2nN&i1 zKoU^`^IJ01rynUdXxYf*X<7XFuU3TYGF=B>fVvU6&qwU>@<$vQCUn*`kC#m&%ZMO5 zbuF~(xaa8IAk}t>;Oi_GqGa5R9iSj~sOEqWybj0iKKMqk4xDwfz0KvQeK^oNk^a6^ITpK*u0bsm z=N^I@D9O`m(;r~)@+SN0yPyu-_S=l$`klr?sRE22<152O36I|24!i4uL;|fkNz6vr zPY7Mp284Dp^WpnKSz1|DV9&Xxiwf{7;f$}}{dsWOr%CajW01qCj?On^(BB;u6mK8z zvnWFTCUkuZZ7V1a!_W7JaSv9PnW;DPvH2h(~- z7Hc6---Tk5?L=9qV5>&Q8KIU&Q3neZ>YE2a$WKa*ge0>m(vJvx9i6J>3kYkA|qAJJW5HYhpqQj{7i9J?@yd8GODXK_B-}B#X zoSNrG2u)HHbC>4i7kg0?9|StrLP|Z-WpiunK@J=CMhwj*QN$kM!)la;rMb1W5sj17 z+NO<)q}8RV4i5GCIq&Kkl`?%<3?JGpMxo(XhKG$4$W$>xb-X_HZ2^*aj;){h{?>Pl zs(ila*gTtN>ME+-eTm|~T^pI9dJfFHs%^-n+)dvsgkE(Y6*@ZCol$SpHQG)6oEJZM z%Dn1-kTo1-ih^!Ax7QKB-u!;!dukqq$!$=4#I>qF?R_u|xEv(%tS_qA8b)oMVkpaR zon~pVHLfSUNvuxZp%YTsHHYfaZt8HE!fvpWSzzxUiE%BB_@?t)Q$=wqCZSxrG z1fPlLik3Lr%@-`12Cr4hxa7}rr)-OAw6%rmzt)SGEtG_R$$L(s4?V;|Yj->{JJ+2f zE?q@Rp^hGFRt)^Z#K!oK2)uLcg73G1o?E{*-Sz5f_oA!(e1lPRs?K*q%9JTi3LT!` zw67!tvFuc;ZM}rDhkyPUF8IA9_t(YA$6_Uc(D)QE0kJ@d3 zzp!$<>C?;Fzxwm$qF;Rl9Hdn9{0OUveb=L~{J&TIRzWNdb&Vf{4qHr%geE5_HpR~G zJmbXAA37=dS{O)&NCV+U0F<@NUsMdH$?$@zYZN#T-;Q2vp|J9970jwXgsPEjR{j{( zzqE!W%E1VPStB$ed1IP^y>!8%c#{8or^84`3DK>-4A+0THW1vRs5Lf6^LUUV9RS9o zfVnXIy{w&>uBSwshhmU!0AyzRI=EE=X;|f!elOQ3-k@X2vKYK-T!v{H zpShp@>io&oZsB3*wkTN|PZBeCh?%h4hrOjnkVPLMfpN*+Y-o3du{pJdaYUn}OXUC~N(`#g!lB9&tU(7;G3D5z^A|Kr zxm1&u15G@`w3S>2qtbST0IZ8>8-QUmn%)+7oZ(6Z!TkD!Iw&{R}=s2ZBTykT4l842)3Bwgd-)MUm4ubUnQLNRJ zuhnH~g#3Jnk_oC-P9Y`w({(uhI45|00R^%^8L60Lm*n_E3O^jtz9cmL_uf3e){Q z{EpU$3mr#GlM9Ee-V3@$_Y@^ktI)v+M;`d(i*k;ila5{d&#LAcO)Z3(qM`M{YmMQ}O437QbYO z7iwL=kz_;9EKf;W0Ylz*Q54GX_<^5gX2~bHv~tR+FPP&gX32-NxSGqj#$2{RTXEE1 zbC&#qVEJJphe`SC%l4`u{PmVh$%CP3hfC#p@q(jo*?#>fOmDbVx6chIs3`0OLf75Q zG1aGUrpm-RQze86Wr~7}?$Y{&} zp5$JcyG|jTC4WyxPy%X>jb+PfS)S1xB`%gK)b1d%Y|~qeSAH!UYZTB%M1U@Gd3PQC zArS?Rh^qc-O}4M9WTQ0m8ToN=gCrU#Mn^zoVW|Sr97w^jbif^9KbpMQ5+qnovK1hOn1Jkv)MHCP` zU)%b@JQ4mhMDa-gB({ViiJG~674o%vjS3bT)M_)7--fAkcIVwd5qL`7<<(L*Rx?U5 zAQZ1Qf~NiLj<`KYjKx(j+Q|%$Jh%+^U&`L;(3NdrO)!p8ha`cQ23~9yi1Q}wdA($o zjS==JHf0v{Q*I!eJ$RU8&u)Zx5gRp7{e43q{U+I4Uf9{Wae^Z|?p=kNbu51d>f@&$ z%97oIYMezs>9l{%A_G@Pe@A?KD4BTbctLtT{r2o@RGyHFAV&FeV_Y5%ec~#-H!)r6 ziQQg9L6W3&UojKjHoMmOmh^8pOaKWrxac8;0#a)Jgh8(Jpo<~)4c(;}10F{85K7yO zK-fIeSFw$FMmg?I`IeJX#ctEpZS!P33jLS@F%>|!62@RI@wz@h=A_ohsZmFdhI45D)K9}w*4F9#+L~%`Supcn!Zm1ZL`*AB7neugW z2)mH6Fxn+gREa`e(SAgN%*@JS2S}n%lOlFRq|8cFvQzn8mr!Vq_^3}KEg@2eS^&J@ zRH4MSHKq2fmNV}x0W&I$wGb7j7h<+Eh{+U6Y4U!l`v(0SvESW(?7wkxWCV6g)FyYs zcMm0dfrbr4#`b;G?sk@d2g7z+OZ#2(lz*jf=7>MijR+xzaDb&4QP((ittbtR<`)rB zDTiN?&NJ&WZfY9)W-|LV6khg8zDNr|_3J-%4wG(#n9H27fTg&47Tj=yXQpKI&pc~v z>4$s(o#Fm`HJol`gx0zQaFza_ZF7x~3aIaj1i=)M;uQfV-BV0B>%PjtNJWOxjICu| z4krtMEY%_Sq?rW}@T+!GNMCYBLKa+-C+;-z!a772I4wV?!^$it7i2A;iBG&WVlp;F zPMRBfBq_kVMw36N?C?^KFDZ*ZioiU0SRNjtRCom0vR1u11YJ>Ervr2h>2woO)@N;W z&W3czbG4=wU)<=FhY5_4^ech3dgEw(8|J2~MssS_!8Zq$jPwVbN7huKqPwBOj|l9e`MSuJdYQH!qm~<62plVG38E1qg{7oi$EF)} z`m3}b^yzKKt?eAF=uSq=I;2=1Xk;TR0H6h%N_}_92sKazCZGzxa*#rVfa(O-e63=mJ2>8z%C9)uGK3dQc@_Qk2?-MGM;T;Oy^aedET#c-oAJQY%9@3YHU# zA;O#t>*g<|Z)s)x`=Z!e%fKnP{vR385dl`_V?i}h&Ua(JwkWDj6M?&LMWE6j#*vL> z8O`h?71pRHEKY3Uc4OM(e!SHIl@sw-h~fi`aj6sGo!|Y(tNdr8B||3QfRGp=xg@`c zTjkTR#)FXfU`^$4J~t3F8GtKh|Mud<>Y_^K zI_fm*HWauR!$g-mJ}O03dtA5q#J zk%2p1R!v(#M6uxjXi(GjHu8v)(uv9D~RAe8HE@F(0ijS{$Nvb1Os3>Hv zlz(wZZjPzSt+g+Pl=DGCI2G#>RU3rjnqw8tw$7@$DK-SgDuX7&IM{Wuqr%mrn-b%zZj-C5(c0!_Ae>1*eRjKEA>&uL*I^oy>~np*Vog43@4nI~BF5hR z=Wh$=1GkQ!O6z*B;`)-~dVJ28yR9kaZ%|X)^X=GWFuK&xLrpQ+a@LZN1a<$BWOy7hm)1XHU>R6Mk;Mm6WqPex>`!*!mgc3xt39)Dt;CB2;5`n=#hJ>B}we*j3ESug@qO$Mu_q(K}ZS8y?Z$ zP}wuQoWI?&^|rHaGhgj5UCyMktrxN>L)q5w*$-9Oo4_tp^=yjJs{_#0My2zyF8e{M z%PC*ugf07*ME2uKm)T5(bC;Pj@5XN*5gP~DH!K@Z&#tbR8&*SIVh0;8Ri`e4X3i_w z*2Z5S^+OSzegBN@mb>Z3f&GW3>%B+)Ha=uW_UhUtI@ZVade!9$e|%H%$8Bt53opmF z>Bjq3*T1%`zhc1;sZ(*#rj1VabKR?CbI3(*!}?O=k!j;!)N260)AD%iK!Ee*r6kAB zPW{hZ*GJ;92Qdyn9XrtI8tF$8Fm2}9RQ}GpNjKm}FYon=hcnXm9}o@>Up;QiOE`Q3?aimU`s@6*%ftWp>xoe4(vGrn|Xz2N$e^wgh8jMCu z+pp?CMzYIE3g^zyZIqZD@&5LnM7wS`!g&_iRQQ@*VkBDB!@0WK-vWJ+UkpAc0#d@?_m8qzHgLI07xyCu#y7cv4y-JyGLEnQ_Btz{*U79A_ z-YQj@ns~`jOs1+=+u4h4;rphyVH-8Vm3=*wuNi2ZJPx}8Za=!F$=8t<6U(m_5SiQ- z9K=np>6eDbOYVx$Pf%nFU|%t?$JMpliy)M}lY}UXNEC&EY>+IqLC$L>5?1TqlObGC z%>?nylj|)BwgEyKDSAIf-EdUjg(-pWD(yM0=YPP~jtu31DNBT->ZNcn(WycBB{pv< zTwPW66%(a&k0W>4UoEKulVjl?e-38QQm1uJD-!3ve3hp!2TXANHXoTnW0uT9 z;)^WDImkmScxiU1b6h2WS1BW#!%Cl7iLGOk-05&I7rMD|u&!(tR`YpPB2oK-mpBlNJYoI;sXT3gSUwKZlxa#fGu*JTuOpeiyp z79m5MD-p#>F54CAyL4X1Vn?}bhuvsRW(xhG&O&W~#TpdKNs2!9e3&7~x0fX0lTYawVVF-ffvDdkwxW1S-$DpXD;OnwmVwY6NuX7oN(QFUgVD96lD!au z{Vn2!Koep+I-*FLGb$DP0eadz%-B@`=&y zQ*gX#AEjk1gxr+$juaLgO)qbZ1C<65ysaOXyO0O%lir3xr7?BGPAw0uj|XWp)J-JN z0oVz4l;)6ep*Y8UlsqI z11q(a=})XcCu&wqT6uy~D-c&BL^#n&1#rC}MdHU2^CTI=f>bIerg@d*h6+IUUO*%S zFQ#>q+TeFBg&O}?Mv+GnE-VgH3;Bt3{YD6OP5JhDvPS%{DwL;ig@dcW9{&kN!!emU52rsHE;~(NgJ~U$ zKve)s@RP=2KQ6WR2_c{FLG(l{&A6^f10zdn#6<$jV6VrTe6HlS6e`?-8N~u2p|r%- ziV8RGX*k;SYt)0KKD4Js)iXA!I^P`g_F+O)F@$;S@V?$4wVs+DJV`h0YvZ*FCweGo!?6b%y84Sw2KKY>CRX;)XgQEWalVZNtR%av;xJBC!p>oghC^KEd$K;dPf#f zb=UWe70$e=4A|IZHEg~xnWn3g4gMEFiE@t|%^(jPa+CA;*3Nv#D9aU64z8?7O1u&G zmcg-30U50?;VXR9aR2~F0Y5_d`@Z#skZcu4GVpqf{ni1}wc19opX_T&KaJf6M`L!A zW709(w#0GBaA|}~BMx!OIz}lUiYCyty$QdPypII?*xhjT7o3HWE{D)hIMM2$L-4I+ zQx0V-z(sZlfp9~LT{Rd!{oj5mn2h2}egW$!0-lGDbQs|8I1Ab2)=AK4TVT&LmKj<@|kg5;>mtx~@9EQ<;k$z~}dDZB-A?h&Ls!7KS-F&M8X zoxAoqA{~50G`1v4;9@AM?8SCwTzEh@#5kbRsK-!TImM-XFmDgEXnbdU<`KUv%`qMq zM!SKHmU72SvOBFRID*ahjt24YE>Q}wkmZ4K0Sxz;3>P*Ynhe%5=0yD7^+^NIJB;V} zi0H?!u8~U&u}uHH+r;UmJWP+2w5>xiR-T0~IEJ-Oj-&g`|25kgX);M{e>VrA zZGGu_zHjS&9WC9oe|<(qS%$9hg*R&p(atLleO=*$K4-($cJ2?}2`HXdu^hB{P|{_O zd`f@p5*!_wF#jF;!Z|tgS7qfZ;t>Y?*C+9`3s*BXUa-u$`*73uPdn;gC_mNM?FX?h zWQCu)p+CD}UQ^?6z!47tv`=A(pcBldEND-cfB#pvlwVrpa4={MHy*@HY#zuA3>apR zrmPMa{rZ96r*Fx3tXC1=y2Xd)uXGsE{*07ChZ7jb69MPc!H5Df^zc`Hg!`2rkz3)p z+{HQ#h84JjsNj(2gD^N_I4d<|#|>*L*|+wKuYPuf7dMVh1oefxH1+|75r}MnfV$~j z1crpU$}q8!wLY?d6;vR+2}#)$%)+MyT|3eq4v$=ul|^2R2u+C^xFOWR!|Aidwa`~4 zjxZr!CBl2@!sk#&ST*PJ&n9ygAd1c=We&wDDAVk>hzwnf7#xiNQAH1e7+&{G*DiXoe}sm zcF;Ki8}uQXcb1^#2h#UA0O4RHYfjW}gEmC__7JXwN%=&SmW0m&ktajQe#OX^BUok? z@pt-3M%|jfvLjFIpmfD%IY6Ah`p|#!ST84}zclDL4wKT|85ph+md&v~t0xm^GC;VJ zahPC0CJHYLY@1A|!amB5Ime6}I4e7e-Zzy6CxNji)d?G$ZYmWoHKj!|iqjyC&o@or zB_~a&CrxBMP3%t^yv7+f2FmD$90iZxZ7=@`7TuB;#og;i{HsRP5PjS|l^)XqIwCX1ilEtNZdFLro!>dOa}rbtm4MjDYnl z!FIX_JH;ufs7@-9cCJL%R!aHPDAYxs$5$iaSo(tx)JmVhQxf|X=6Fh0#GuNyIHf>Z z%6_=X)~JD!sUxFVK@d}N@=miF|Kw_WCJb>yyN=1*-)9vM(YU9k60Osj;m~g`ra)5S z({tkQJ#%0;pp6V642?X55_;R3SW^(ar6vj;Q+D5=*poU~t|Y%^Fu$3oaQ|hUkmrGW zC6)feJ{J8*03tovDiaRNPpr&PX6`}oNHE(kb98?`<_ZsHZ(8(AO3ZdQ^rL1}N~u7k zJS0mU795{YZYHB61e z{NPsmPZtO)LMTHZb7TB9Ou-l;EOJn6E05^i2|cCCB==OVq~ZcLmjs;1)Ofy11#};* z^~F%_4g@Q-A93i4;Y~1MYy`N`@|zh3=%6fTsu$ezr^7tl!=V}38Sw7d%9Z2)$6?>5 zxr2pXC)|QIVW1T&(X}=VI2;~S7Nqgkb&de_w;M*{3sfl)M)(6J{xeOJy0$tV)xUwu z*{-&dGO7YS{M9TdL43$Y=H)}J&F?xQsCJ`aPfi=4zA8@fR5QJDTh+r# z?(c9nS5&^?;z_tQ{rT9`evG=s+~j{){~;ZbC)4n>UA4Cp-_t{>gjUtiOVQ?Ma#%WVoyEai+>xKN#*@$r{Ou{;w0Qs1V&4Xr5o**%XM3&z{c@(P z*@-9Di#O8IPAJnTNZF`C0`?JtF+%|dR!GD#ev27qf4`XNt(sDCncu!MmKX$o0r0MR zf;g{1oWW{$Y369-T-36b^qu&#fBV{A%thst=*eY2O2R?u>R5%8zMPTh+#bT#9db0qdTXC;E4>zCw@r+`;4RdVj>tHO+_a1Jbr=;1+L1LJYZ2=716X(DNk>%On+vlv3 zQUlO(v!J;3hAI9amq=*kFB=O!s3|XI%S!p2xs=J%7`n*B6tM@5o(Dse+77bIekNth zFl!(u(YoI#Q9jv`mTROZh~kA6O4Zlm~I*5J)){Vhk1Y z3rm1ZB58PAF+fcx2+K$%D*|volTs_~Vy+XRZk|$%C{JwE*YgMj9Zirib%zFR(fZL= z-~If^!!_`N9nv=eY3i6HqwhE&?$eDkGL8;L^`T^+oP$kfR@*@dvO|`N*+$pN!!Pi* zjl=j)!q14prJ^Zhe8Nj`@z77OFbM;J0>Rfyv=5}Tim^3N{W-^FO5rE05xsy30tdqC zLWD~qvg{?0dv1*Zl;S0!eluWM9}+pLtvtz`-CC@nC`gm@;APpJ+3!P^I$3g_uV+*J zc6KjazGT`Z9Gdu3*373htb}s0eEG#m|GvU7MIdIX1n9YtTOc?qPWHty8uvsa^2CBz zzhB#CHb=nZ>t~^y$`sJJ9-2!(!}deM-kN!TNV2-+O0K8FA5UGA1Mi#{lPwHA9wZ zLzHT>dwu<5K}Ib`WTpvKkIHaScVf~8Ot!zyxgg&dgV?`kbMD{P0@?PG==O@q_G;kv zTEX^4U|MZL%GgUFHk~^TdBbVEH%h1eKKh(`4dSiJiMNI6=wbG|M2!40d! zd{aB&D{(97PwlM}beIlKE~*Aig=Q|4ai3>ABRpjX`{vsWVfpqVKg~9AEX2OB&h#o~ zL%5;k#y`l(b|uk(2P0%3NKHCABre_vjkTwfy86ahSVc&lPR#|f1T68m9AWIyGBepk z|07)Jf&Qt$D&$1Yf%BmOkC}Cj8oj@nkbh5NrlU%=Fx)#H(2epY*L=r#xFW#&ZY^gS zlE0d>&;H^;Kc90lgEWHbL`+&rqp6ypUAWQadlUpI$!$$xO)aE95WzhwLak-v4J*^f zISUHmCg3>}q+&{^CSVII<)neq#1=jNDRC6apXD#_?bdwMmSR{KtAw#?>IeGj_7g9?ToS_xu z87#HD*;iTqR!9Qmc*e}|NWZD5GO>#h!TB~%lBCHfscJ_A>D*;8=d@a|S4Bfw#ULd# z_vJA$pBW)@qgoAv^-;@q2gk}91#%5lELxTu@f4&@zs(Oz(sG1;ZxpWkd0X_csuKoK z`39-+q@1O}QN|OYcjfVC@R~Xh>kkubV7_fetzOpxS%0kcyAgG#6iXSbq;-|OYZlW= z!oBva8t?hJHuPKHOpCDSjfoIk=ELny>#wu#HHNuRDshRD4N2IyEU7gu5~7A)A9mfl z2CkWwa|dZ!b;(j`MTOl4qhHE54o0JL^3}KMU&{CN1KQQ8G9htIkJC+b|FB+SnaA4= z9-Tp^(pZkn2xYt0pAp>CUQaQ#kJ7_G!T*P;_wZ-y@#D555)zx(TkJh*k0N649coiG zTe~Ql*n3kHjZu43HCuacs*0*zMHN-FRe5}W_w(GZ=dU>De9rs2E^*CsHP57_KOaF_ zkxRd00<`8{`3C&b%fNBbkN)`19iW|$Y_4wxwp?qo2>$ub*fNJzL^2(yUI3$!e_p2D zE-OuAw_a%*#l1NpWY( z#$=Y;Dcy58Aelwq0Ya(e%R<4zs?n@lD&~}AGh#*rVl+E2DpgNmRWC66W>TSFbU%jC z{LrjMA8Hath;*hvo#hDw&$p3eKIL4o<1YvVDPZh=a>Pv*Ui26jU2jEC_} zZq3KcQy)M$%;g(dvB$&xG0z)lAq#EaOL^kaut2!(-H?l3Ikr#({DUrI`GC;vYN{Kzum2ZOKFhriR7Wz;D?Olmf}buW$l1H>aa12K?-QA9 zbn(fhY}G=Iv@{-0l8QY6QXGLAdVs`YunBzr_wmJ1A`&V4>0e(x(=pa=>fFv!&GcFw zk7XXL84%ZGdAi%I$@U!NR~0{_umz^>0t~;Y$Bf6=Q-Ey z>8olr;7(P9rwUnBsbnScIh>cYltwy~7X-M5AUNsm(5i$f0?9^i2&Dx2laBE?O}K(NlV+`W)pK^$WW9awy(OD(;?(u9^JG)ao^p z_{9rsB*Ee<9)xU@7wW-`IgCgrP5St1>z3|99SdX8H`9qb)%gG+Js&cu+H__P@+gcr?}R$AHy>C;aC68Y6QroN(fw% z=N)4mHi`P841E6l0t3wtK-^J999oWrkV61vaHda_@fVQCBF#2Q#YmeAK6Z+x#K>WX z=6S;z7Zl@@x6k&{d*mx|-yg}&c*E5uQZ5?FPnD_8Y#@Q1l(fLcx1p3dV~?;2oC3JA zy<;O@s${KLx7pbG8A8u`PHSnYbjdkc-tFxDl`nOAM#0XQ2?IVJCk_p5EL)v8IM+^u zn4vtDw;_0oYsTM9xvT89v)zriuzg^dk|xg~c`+y*o32lcKEfC*Z^20(_9|?1{VD5M znY@138~D*aE?o0%y`^dQP3h`u=Z*KH+thac|>qU zP7)!F9IG$=RGQ%?KeJN0FkZ^D#CgtCC04|zX)Lnob)IyhnHM21V$i8=!N|uc*?2i0 zPSl6eicxOg;^0iikhdQY@>YSHwC~c)D?jN6xWH5ZA__4Lj`;r_Lw2KaPq#T={boIv zg1Mzfd2o)Ax3m`YTntiwyk|xGpZ08Cjofc;=ya%jV3(wtYCLK1+^Hn*n@bd1IE3uV zZ>#r+CQaBhl=U=PGx1-Q0sb?sXr|qC=+lk?RT^fj!D*%Pt-2$-^SV7wZ*Xv`2pjZv zl0};fK1dkR@JuFbh;Y>M-B00kK5D#D3VgAmP(2{=&l<~P>P{^UX{sRmi3z9?AkJB~ zg86N5B$PQQ&SO1_sgTcPkS{Y;r&x{V^(PlzWh*Y1aTVBZe4NDn*S$!1N#+Sfd`1sD ziC?fEj7Zz+1AC4xIXK2u=I^Mq+kaX?=lG9AY6@-rNuqt^4yw5EctJ=0Nr;n61o5O61Vw}F?k5%Kz_hTMDT zSMDlA6A2}=HcZfK$;9h$;Zd%fe(vN_`ZoKuNXRFHGQK6Y3L$-f8z9l+dXV)qYWR>@ z&b-FrLyfWVitGyF4H2F?=NEo5u0ce0qmI-)h6#$rSUArxdbdtDpV}xImhHk{t~AD2 zVR?nD?w~?)yM2hamK#+`@S}-}s9&|>RcCJ4py7)y8Nq>*&RO2O?$?foV%WeytM-%1 zR{RiE(z@n^4{vxmBkT34l6LhwO}^J6CgL3prR7~%>g(|~g)F^yP;=H6^L8@om9E*`sAVNXsdU;g-gY`Usxd|GA_`M`He zlcXXpDoSD#cH_W!iHeP0>;7D+f*RBenZr~f>?yx~9OBgwPYn}Xp?*u>`6*Dm#i@{! z?5L{m-iU8%V2?BF)yk+cfnR!zq6?hvZY`(Lq9Td&i3>NO0Y8!*@~!sU@m&tKH)GWy zXctT3y4M1F&;&}#I^X5{c`)gC;#csY@C%+O6*wSXQ)p?`=0RIjc^tOEggQAJhE`=U z^ZEfIi5X ztw!OjHB_1(`LHx^mvwQkHU~=V>?I(ptbG<>)=|pO|I8fP$R4NEgxLpB<5BnDx-`-y z-Y6F$rEJ5e>+ z-h|rAcHvk~F-*cQC|+E5sNeUik-VY-=EEo;7x->`oV+4U>dr&H6SVy$3kD?vr8x=_3 zj&xWT5(HAp&!^l)y&e@QK6Q&->U3Nn@3& zKzQ&DqgA#VCwnzMmr4}9$Nx80+|i!xzT47?iLqq&%~lzyouI|(g5B>dd~^H@dVpqE zZHqjqV1jewD5>DP)dTB~1H8aIkn#bc;VC{$JdsQcC^#2sAluzj_5Lpt1i!HKGnP;- zKFa8Tu)YMs1dn0{ww*G)KYx}96N?lC#NEq? zs?tTPGL-jIEk)sA%*6l|JY-&+ydx%Rj>u&T`s(gD0%IvfBlr~)xxEnMr!|Ts_rIwj zGx6Djf+GP3YWW?&C+Xk_Yf`mBdL?%}+f!%;djd=hn(Y9I^T{0sCfw#JKbes9BC1#+ zN{HeoP125J6^px{u5R6y7SLMvO|~1*hmY~eO@I=kvhT<=JeO-Gs?C&wj)e4k7)6)Y zc2Kl>SmYc>(bM3!PB$-OAVBS^JLCu~B8$oC$}jv~OA%jK&i>}PL%QCY6sJu>ZZNcQ zb>#8Qh%@wV!Nq7`6K|-OfE7c!muonm$3ttQ^NlnL0aBY~$tV;{BA};COnn2G7)0_e zz(y0-?>DBXJCqWAAM-7y3z|CclknAPI(>cyqc{nx4^@g7k}8A*_9?v)J4A60ujMbe z!&*H7K*6Vt56@G#A_1{>3=dM()*le%QxgMJbMgB5@H3K7;@XVO8G`^i2MLWsdGaNW z3c8i#8Lg;UEZbkaL@p!|$T&R^ln6)1Jb^UDs8G^4X4D@~Tx;cj@Etz@AQ&=q22Zt8 zykxsW(k%1S=&I6^bs3Q>>SayirAE5#E{YhjNw9l5b}VmbcWjcY)9D6WT?z?6QE08b zf0{st48@z|Yy?nX9K@)7RkNh#wId<4rq#pDdFmx3(E&e8aKw^S zTnYNHrli-*xoHx#mX2gkF?1m%bT!$PfH}RH8#+x&h-?N-z8R)C59^0dswn0@4AEkf zjdk11n-sg72EM?~&B#?}IS>&oUrh8xf0*6lV7ZmJ?N*gg?4tJLG=it zRc4}}NJES5)-xB{X%lMD@DveE=fnPl*g6PJAHJRfUj2~p7l4YI>U42>n!{Z7YGU?; zFRk|0`(~=KMUvDtui>SHm#9MZ#m(t2ToXT0q_o`39pcQ!0xa1oERlUPea)mK-!;_~ zy2TtBUSJq5sWc1c88DDpSb9Mzmg%+iB+oDP`OaFt@ccYHqTirI|1}tHNiJ8+abahb znOB&!$FZ^Z+e}rviqPJ&viop^t+)ky69!r|T_aR*dUmV!3eCGh^ubQ|> zWv5sK-5oTo0bzUPTW*K1q@zu2j_aI!D(Gu4$u%bSU(uXD4dlEj^ ziVL?+fZCttxe_TuS(oLS?d!R?^Hn-SoLi z)5r2d_+J|Ha1--lA=McI)kC$Q_gOD1(H{d=U)z;hVLDbT6Khh!WA6HyVe6|&1=i&Q z$#JDdSh3MSAI-A#G}{BKiV{fYLs3^ho^~4k%Fs0|YJFsSWmdiAlCO5MiFCS4@S{8) zOB3c%U(95%;Jb;QWvWc~EO7jRz)XTnXK1~3R{W@B3a(D42D@3mm^tQ{ne--8Vq#Tr z-nJfD^zKU+iTK**T z5L5OcG(lrt_t`(4v;S(FNS*FLWU)zZFqCkL|D;3V4i*2BLL^Z;?deeud>|9GEs6t* zQM)GMr@jFZY~d~hhzFb0|4JD75z$Du3In#8rJay^@7Ux7NgEhf{1Ryf9YIOkoMb~k zUptndoLIHh1@^Z^F1JMqcEo6R#Cdik?h>*ZJ2*))fbdSsmeCG-$QEnH8RnFi|g;NHu8TzKi31jlHll5w;W;#wZPnEYT3phb;>L-l{&Wfn?6~xF?&u z5!HCMeL2SEj6b@>JzL{k)Dr3D9@@pqd@k4?3T;%x*QoEbrV$t_he*AeuPo3=+p$;( zXB{n%`k;htbSwC8kz)AcAyn&bCBUv+*8vB9Dbv#Rb(1r_G~VqAtCn1^qx+gHhO?t= zPA1}KStc!(#_kd4OuYTG-XQ=&Rv+`=)sTjq8wbYc_>9KXtNDb>a=No*QJN3wnt}Kch_x&W8Xv9y#SVI z^uDt@To5}3sIFj!u}KD-#$9sDKePI2^WS>Lb|19^#*|)dD~a%VymZeUQ0RHz-sRjU z?^WB~L5%LiukVw+>6jJr*Sx z1HaR7PR>?jj?dPH;0U3^aR8`+P(l`Vp~j3P*9V&Cla@Cy0t<|FkSrXpO1?)IZwrwJm3L`d%_pEyfq!v@8G?z( z58LiJiB0~_<#klg)7|FL%JC#HH<8w+3~3c8 zdnI28M-|lujkW>CL=(goGRThpqU-Q?2*Yy}8e8boSZo?2P(30H!SmnsBjI?ObK(a+ z(bW=fTuPW=lv|uop&^xd(wffWahsF8Y8%G(3_pFP z!+kFxZZ-wiULY-}>u`VeD835gUKOWH5t-$VhjRO@7w4(nY}c?$GK+onXWRngHU$to zWB$QoLCQE%Lwb3gu%nf_=a$-i5jQrcUTF+1{Fquc*uX3*5Ts3h(?sy24?>ra^BaO5 zFblPu*1@f4I~pfdFsF{$Xj**DZ&n-)__}@Q^0c|W<7?py2AWkAi-9*D;~%aUfZ#m< zYBu2a)%1A^vrpV&ISq5X?5lD)e3x9G|cM!lQEt4L9)YAae=Ha z{Hp;wPXs}V{F}oMxgH-qu=$|F)m-^63BTs5UDYcLglE81D(aOXPxNn^T!}11aV0e2 zD*jS!KTufF7WrDY7q*%2Jhu+_LM7d&k_0ur5AzqarFPUHKIt{cR`GAlE0%q>s;D#% zx0F@#V=c-^D>$Ate;iB;CZ=?1^cylzL)ESRC3jbFd$+LR=iHpb}S$sm?RE$BTwsAyW@JZ)k zmtbS>pFe_4-S2!NsT$jM4CN;Qj_yW0a{wKw+m*#>0Xp%^SH^sOzRAkB+5VF-`VbBh zxhtPms5&ouf4#y4WQ*0t;S9OwE1C?@l-l=OumjxE&}^b zAq_DDEFZFyNO{FrirSnUUJrhktGPCxCq|~k_8HdeFNE;Z&mtdgJ&r2N69Zu8{dhHG8Y{ciWMD-C~u zvN31-R4*O*ZJhW#X`|QI)_od->fznqpQkbEx5QGYR_z}+y6?ACS4~;(a}Tg|Il<~N zK@E%c<2Ene&x$u;$imn`oP7PzVzR(OP6ypcc4%N-r1Pj6>lK0)Vf$-SmwMeCs@_aH zUGHOLY~5>>#orJm@MsJ81G%|XrHsDmb?J8P2GaVfP% zRlR4^8)Tgfo(H?H3<>)+C)wS==?6RHh!cI2pe|VQ$*K{lmgbblVVul6e4}y;&8a~> zoUA{qMwONc;vJXzSh54vs0+6!h?h#(9%N~)3uUI&@b}XS`D(0CETmIL-?bU4$MrA8 zksZMs&msV%kB(2^oG5%+P6)$yIg0;Xv8c3!a1YuzedA8qpDf5SadpLp*2_Hy)=VFj8K|ITMybV6*>E!i zmJKgE#Jjx?z=#ZR#EWym^EzF)y&0=%59+fD_v^5~13GD`GX`1LH-|X54`x6A?~H}? zT4k7Qq&T7h4{Pu|Sj0#$@37LR=u%X#&du*~!d18U%;W30hp6>$c-f7}z?6 z`TRy^(f|2{}A zqI=f?_97a|Qe58sM!0cJ?|EKZ*5UHB+ur?$g>kKQXU1`B33wn(J933=U9}~9noIFL z9gQLvDF)f3gD4wV1CbF2;qKIgw zsI({*K}0Zu*qMF^W-y1xt5*UGfG+-5oA4QnBrtuK#YcOy3p>6ln7nGW`$$g>cXn#g&^ zE9~0+#x~xHg!Ak>d^B-slVQ_%kTHPEP5)mt;bC4h+n+6{@+K##c$9XfGKCFC-f~r;RbKh2NNSTs;|Aop^pmxW&YWaOH!`IWli& zTJ1gp2%38p`bj0{58tGIqtoRZ<+%K}^5MWi(#JfhoM2`rWwsek`_vwAiY7Y;_~?L> zjpgDbqHHfp1zE169C(0HERuKx+4z(4Xrm2dhY=khxk~QJ8Hfe7I&;(!DdWO1Qwj-_ z=tnn)L@P1mb~EowQ_6+7?N44rbb8BJxn16v7AG3RYRNon1(A8aU8`njj$cco@SVR|=H+_a87|EmKP1zaILp?Uzvn8kO zZ$2Md)RsvQhTQD1zRnxVPRve|v@xgppJyiHYO&oCRCsSXUfnlips97Kb}P;3s7c3_yQR3}toZ$99eQs&c0j;gN!ByNCn2O#<55{Z9c z?j|RH&~So#3Ir4-o(F(fd2`_)^lay@B;#=aEZzQNPOT&NNe z0M|-VN0Z0ILbrK|FeD3`8~+%|PHbgq%P5$gxeQ88&>+cnLWu#KvmOYK(@af%3Xxd^ zAn5^+455I(`^1q^`n%(NxlR)p0cWaHtVNDR>|f1Qwbo^{8@0$wJb{ zhp3eL*&IfQRDrtR?L@qikHf&GNH&1COI63Pby4ikaj*`n6a0jWK40aLRvH7mKxGQ4 zKbPvSO{f<@12hNC>qmccBXEFdgqjl^ih*4v6MCuutXhlEa!cJCy-IKybjiVz0X)@) zG)gqOD74zZD^c-bM_MB-WYcVyKz*p8z5q_*#P|vKm`7n?p$5y(-&u&X)`@qqi{3F- zBkI=HNdZ2lMu-1#i<*VnyE#MJfj?#zIjSPZmxqG{biboMQ_s zCFv9icUSt%%|wrNt<=*=H(=gvIW(jNHy-hhH;N=FER(1hIsDXQ0!lqm!lk1tmRNS7 zY7<7^)u9pP=dK%1RGmjuV-sEDqiyw#(Yl(Zz$2l+DY~{Vu`WL4g$8f&cB&1|xXda{{o(oM+b3OCiqUszJas^g-r2_qZ~Q(bP4iz#(Tp6MAoL~(5KB}> zv`-dSFj5K>{6L-h;B%C;Ueq1@{z)rI+9%Rp=x_;QUpI63cIj|~-DB8G_idQ&jSck2 zP>jKfp8g5}vIg8PeWvzHo+V(0bUGy|OfO(> z1A4MKGV*1_c#{B4+>c8q8INJ>z~}E%@e*rLkUjAy&?SmKIXJIBGMIz;_V4|f*uR`R z&MjmM9&`~O2LIz!1%W5&+owSMBs{)8TU0yWB@`2^$oPV5c%!R?K?cqejc zkbbT+*FHaT3?M!#Kf%w)m=G{A3OBM9UOde?S(l&t<0^VH(s(l?`fs`M--hVze&g+l zDDJWm*G0fp9r@$-M}wCrXHMf|&?(@j4uMy{kYolPtO(gq2BPGlBO#UC@C z&F(|?@Nq-oj|LyHze6M@w!#p$CK(ZvfG`t+qh0kppYgnPkDex@a4m64LdQ2gSFRcA zn@yf`ry#nshCW;k%ng&Ju5RY0LZ!fvfr^&Fz&t0Mc=MBix8#i8igvRBH*y*irvBTQw5Nv|MAAl@Gr5Nhd^9GKu)meMHothm8a!^Y|I zef7s~!MikU=j58|wDv2&!87%aGoxeQ^s^VtR~3{`S{|G6^Xw<^!g442vh_$?^v!vx z&^f4I7M^V%9+(UN63a1F%`q!UfZ4<`2B?aKHGG$DsqXra3u?3;J2zF&+3xZadru(o zW!EE5ORpbHMf!uNRgCk9R$gP6JJx_^m&M z?~RfSOZ~*r@%u|CHO7P}sgo9{V6=&!QWA0b=u6Xmwh7glS3UHs&pmCLP1s7yVIJlt z(WRZsO9}V=-WL5FkN&S4Ag&0BP8r1-wW&{`QpEia6-U)y#vose)RtUmDGw8+Y7sng za>3Bb2+iocUu~zv95AZW$nr%>8N|$AZ0Dg3k{m2ABwuWNaJE3IL|N$x#;S_0tn*2s z)t6n&bANmrxC#Kr`QkC)@SVOzDDzB8^=nHZ$h|jKOzkv61{|#Z+h=ZqBadE;1m^d7DR3Io4`)h! zVv7R5Vt=e`69e9Ed;BoZXeGwtp-R{1g69?kPvQ_hkA7KjUO5B|r{U4+g|RvETe&DR zm|7x2bs`@gO$SbrzuJ76Ps8)R2>pJ`13I_XDkUSYhhTOaP;;CJH{+|6eamqt|Fh(E zRm0MLx*XlR^#0l&7*`Xe#gyqQ^F*^*l(79@F<@hh)bU5rXtJ%|%}*(*z`#|Xu);n9 zGsIMW-;c8`%gr50NjoMN^pxbQ&HN*4Ggi;azIPu471Sf;zrUN$v3OCj|pZr9do@RyW!)$_5A2hH~7PK=R%59%D58rJWe%wumo zQ}~}xm=ORVjBjpUV_x>q>*dL_Enn7(hV;Gvd9T1KfzpviA&A^Ql+sf^mhGrLriD(o zr;1MVom#SP#ei&2pIyCd8XdSiD$|;xT=o8Afm`FZ#c8nrg^@hE9@$Si}q3h=ANp#GI>hRQab$k5b;Vo`(clgFr z<^J#WFiQ|Gj^yURpWJwLXyP&~$|%h^{@p~-uoW8R@O#e*-fLzQb4-`p_ZB>pH}dV7 zkMGi+1w+K9X@SF3lOwxEv5l_G^NCrbSRLpeO*>LE`iO%lg0aD_Rr|t|olAA3tNP7- zhsA$41$>E@1*6w@Z=2~wTJg7Dl`(Ub{#V-XGQJgp`>K%>V})9YD60%o?x`XCqHlhH)v(@o zb4|P%Omg|_o$`v(?(Q&Ma)D^p*Jt~CRI^PcW!!0R?F`Bc9){(Hh1~VLD=ps_hd;ae zu{HWa`BlWfKRkBH9j3x)CGWz^b38`NA*YmBBZTz`PYf9sFDn=(uS%fLY^DLBzU2JM z>Lr^nhL1=+8)bDD`BYm;2d8!~c#gVx#Kb|pfF^sYl?17!wZcX7LSupfgsqjp;1Z(e z*ZjMnrlEnx*Ofk~=3%p+WWMMg4m2bB<#H_xmK$OEbC5z)s99WQ`aE-(t~cvK3mSt$G9k>=_8{-CA!XIR}x+!=P3XRe4y}TRbt=;eZ)P2444l25SI!I|~bM`Y)c8{WM z?Omw%n?-}ncT}g?IASmHkA)}7C4U?lpXGj9KONsWS`Ux~+pqunACdpby-{hc-`l?N zaJ@05@ahYO-0pfSL3HDKC&SS0_g?;!jo)8OaA_OXOP>}u$-uUEEipCO+=$RG#02)9 zhp&YliFJbv4WFGnmthP4v6ZHLM7fMAx{nUOUr^Zvd!Cva?4;TZd`Kp&y_WlhRjrnEV&-O=^2vbc6#u0D+5XtK&ok)x3ZX+kF6My@6^M{i$kY$^z z1;prmSL%brv5%z36GYU;G$k<;nUAUc%c+E5@0M>J{m|$7O>RnxX?r)zF!t3r(DO}A zt`&bE13}XxRWw+tJ+m0jDxPpl1mrAmOhp{9hbx`I>E$f&ZRd8NBDLEz`gxj4BaadV z6&cfgKEiHUWR)l?pc$*Vt2#Y`x&Fd7beCfk31 z;VW1|Ygqm|msJg($t%u|lw!{8J2zU$%v*BNGVlR=PVJGZ9oQM#KqtM&Bp}^mv0n*X zb<9pO@~4Gn2~99%&yxoqJZ1c!yw5CmG;})sZp(LeAJ)-FyL%cZD<7lF-2>W=Mz>28r`+GmRWL#|Db25*?QsU9{~IZuqF*B7h%q3WrPSKU68nhQiQ9 z<{X!bk1Cnp7^HN;AX?s(QVKqVPa=PYO9?S7oci$D6r&-B@k62|3v+RQxQJK8kOHyQ z#Xjc0YVfWxvSi|jpUB~+czrC1##XZ2-2B7!!z245hE>=9pVAM6{Xas!ITi=NvSAiS z)I+Tj3Yt1qQfH%R1s#m!e?z?C3^mT;0-FEH9Vn(AKl{680uq$@KXL~WmbS|`&A&#x zcnAJZ4i}ZX2`>c}J$`6cE+y7_c;*iqw(^ZTFBTp1E`OE zbn_t76hVrrwsR8~G(8+i8r%tUu>k~3Qd5o^S3vi4g8}F%I<*^z{b;LH^t}L8anQ8n zpmJoLTFaDUYIxPh9PZHGdKYEq+)7(%5DP-O3R6BH+)RJ)jXzEey9hqxT1&fCEBCEG zY5^<6oJ{;`c;y$_WWK}vc5?t4Fbt1)vzrn^26!z@%2Phwy>Uf`M%M{bddYMxf6Wa* zA&L@>kI=n}46k2FPP-d=&NoU&X$alK(0Ejgel%f1L9VBYBmBX%GD8C_8m0&AC5p{j z3Xu(l6bGQh_35}vxeGY_IR3P+@K&r#nP{qBZfu9`iccLe*R~y2UXq>`m>AXx!vy4HB%+j&u;Z0lb8wTn4eOxz4QLcg0Lv-fQBvF?N-AyEC1je}u$bwL1 zlROZko`ccP;n`ye*HvSX?DP>jxa&q-H2Q%NmKdkf&ood%T8N(*=4~5Mp?gZsRiiIN zFneAsN?I&TSdem7KK%%>O)wY<(w)PFvIB&EtVtNIOH5S)`yl!hVDofv!tpHx>}}$z79p@QM!oP!M(`a>_qY6tIA5v_Q@UXS&5*ndN?fTgo`eFBlzuY>0 zaqGKm*S1|rasc|V6HDZr`pkEcI;RfNk(UWZBI(%-^js(@o73oi?-SPbQ#5k3t`uI>0=NAc ze@3O09jEkT&O42Y$jLy=eNIUYPJlrfD-Q>S+$ks(&0Jh3(6DW2)JMM5PZh8!IA}uA z2#ivRq8P&rZX!eu21Hi)6macj3h#Cp!%K%Da34Upz>N64g7b!Ho&`}3;x5yGu!k0vH(gtr!nm9|% zls5k1Qli(9nsJiHg85m2F9PoIQ6Q2|wrJZn-}(u1joq^uf(c01Vme>~XZHjQv1T`0 zX2p93(W+|KAf+aR=?%t|Hkv>MQ{ClAW9C}4dZ!jpRdw3|A<$Ik{ngajsw_(&Cl#um zE!IrTy-o&&)fu$UgJ{I69-hP#M>z>KGv*XZQN<>bzrH6=7-X#>(*2T=}Q zEGNdi8p2aq*fS#hr2`^!2MXK#q5gM%&oqkHRm1Jkg0t)Cur92EWirN&D&Aoz`awV+ zL2>^-l(X67;4Y6=(z+_Gi&)}D4EHX#Ii0(b&IjHI6Ogo*_FVlccqJXkPPLx8j9FmbIQo238X4YQ zcoc=qQJnZ?BlXKp|3F4w+u8t48m&i$*vj49$s*tZyOz`5mC!ROe^!74Ar_hNEQf9a zC*?=C!P}3|U7vu)$=5N|4rrx@Z3me2Qxmka60N=QokTb=zUHCyO&`;{1O~XL1AkC> zXhYDL7mBIfFVD@s{=*1g+(W^38bg%;txC+)SM!ZQ*R@B)+0qY7a-+lAA98y-817r1 z!JbAhW6;Z9UCT;Al^)?Vp5B^XWtU#_(%v6}yg#*jf4=nIl774w^!Qu*(%y!DaSp7x4y`7~0SFJh5@lY7> z8O-F_^bR=kX|N-mWv^+|4Wu_cx`bIky-0;vUR9KDBrN9R2?4*u%G;n1AGgWo10RS# zM+c$pw&Dk>AMrrKJ|83v@ab*Jl-#_qnRyXh%Y}X_6V=T9WOC=JLNZiKID@SD2}>0( z&h1;KgnO1-4&64pLkgF@9e*yO6jbP-u!JR!RCVTV&r5apACb&+$$R60B7Q6z$8;`q zUpAUkJ17M4bM=Z*n1`9y7;bSEIQ|->a?O8vB~%z3Ic}CH7M%3?X-oPh?FArf+Y+J% zjE{4-Tj}sBEKM2&B{ZWF@MPm9Wz))r(H{?sh6J!}38Os|uZLa~AgUM?sx2I-FSUrgtkg1wa)2g341n@?PUFZ9Z2>m5g+?f zGLgwk-R$3oGh`%7NexpV(YJ@$ig*6vZBej%CYr zHc5XvstHj+Iaeo6RAWQ*2Y$c6%YQOrmi8;JF@Cb6bZRh_1b%0aEI;lw5dz5fK`XEk z!^f>s8Yx<+A zxn<#!*!)j|RidjA&zuCgF&e7*A>Z&W9lrPiQD?KYHVLLt{u})L3PZda= zkH9?e625Lo`P@R`TslMhkIb5#1E7awgwAZ7rWlQWbg`Hzu;m~=!B9(##@VCyU7GIg z%2%!(?OPze27B+DG>Dt1G$^^37`z2IMsq4US!U(vW+MOfG4d~NZesE146y57!lGz! z_gjjd4?aDW(;&uY(nIp6H40p9+aPHy)jo$7e4sl^a7Pil_}CkIQ=_5$n90+}ZUfIW zqaLaxyy%7KO|qoBRb?}hB;|e7Q4-kks{8%#I>`-TOckmV$GoQ%EzC9bcRh0Vi@^7V z_RH3z+@sKvj=0q8&6z&-SuxL3i~a5b*p2mM%r6^JQU^mGpGE$@#>v$+8VPnHpJ{)^ zo|Hj^ibuaog#ut@_McyBDO&0XDfHOgq$k52D?+-yB`T5;uI)Y{rUJt z((Y>N^SW3IyV^@&jGv(ESYhw}-YiWiud4!}@bGq}2k_R35HFCfJnc^tMIaK*OYkF) z+~@F+03?>rD^NfpK%tKN_`YbM`)Zx>&-w*|{MSv-=-|KHT(-!63*<$*YNgbM z&017@UM>r(kD6k3yQj2ork)B-Geyl8MY`xgll4@{|A|^easpPAXi(o42A}EG(9dCP z1s9?YPs8;i)F}1Ql-~|0!BGU3-C_QxtdwGly-rhP_yrm#6INx-W$Q=f^QY%5?(I!P zy-i>6Y`KD%H!#DXO&@@J?{eTr&IjB1-TI6#%HYG1Y{LV8Dn5_WZ79Ms*-0f1d>(Kx zEVzW8TZ#PsprNz}FXg?0Z$$dpC1&DMCHP-Z@p&$+y;1<=-Cr+(e0?$p1`nwK;5{_o4Evt(=U9rh4)y^xp#Zi19fHipFM0Hz6O9HM( z#jP~J?`7v^E6kNG4BZVk7Z^EtbbdI$J&E7Jjh4f z(XMcBCs%HEK_D?plp9u6)+)uaCq8Gg5_2i@Xp!G*am;g@H^`LFq9Z10S8V8>u>AI3 zewmP~&!m3kZ++MM_$s~HUA;ca5)-z^#&H}#tRmsg^e|9tbz8VDQ*!tdV7llA92GG9 z%633Sd<5UUh0U6$OhN?Ty3X0IB1;eto|}LX5O}!r@eXW;3H!w?a(*>NCs6Yj^EvXc z*2?1DC5_Wd7ZlieRQmib5rf5d(@hsY+}A@S048Med+pN60YGgql58!*tz%GpA461h z{olQl=>M)c3tSW81h$g^BrJ%q0LtlJr_k+V_cFB->mr)9u5e7@(v#4;lPAD-U&E6VSEyq=<)0fz1lr5l_X z8tHE7Zcq>qhweriq(wTFW`LnXIwS-{8U#f}MCal4{ygjZAKdG#b^ma#v#-5x{O}F9 zjy<2H@^g}0gDCcs1K6Dl{G92n9#;CYITjTOSExfut|RfHJ)P6UbJNAM!kYYC3jMfQ z%?pE=C!c``eLW9!W!}rrFGU}Ey;^rJe)02@Lxwgdv#oPUf^&g;S8kOgnzj!OC6HMQh zv|JJJT3s3&xmt4Ti$RMrhPsZcsOR4X#kIES3D)=SHC7_J#87Z&(<1ftre{ub442+x*|`nB_nY7QpJHR)Z$D#6?Hu(oRBkI&|3Jt>7>I#B zu8vw?&`FQGyceu{;%(OxaMYIt?0KcwU}d)y>MqqDRbIaB-p~HZbh}d=S^_(59QF?R z+W(UD)-t8-d4B0-akFw@FWye%gI#=4Rra@^jeAGlWpz2aJF{Cuc=y+aNqC04WB-0W zF8}#($2;Q^?@`8DOz*d}XV@1v)8{K2K03O}k8bvq!ug<(LDSRS*T3#~-oFcDrN;cm zQ|xc1{@L|4h^l_4r%@_nw?V`9dG{i5B!XlXM=jI}L3i)28;mIZ5Phi+ za~Bn|Q+m+mQcAP&yB{HiX-#-ou%P)!PtOXgg%#rGYoDcJ{3C?Mcgg(n;vYv}joLW_ zqk;K5cyoHH-e>V@3LrZHK9rIeH>IMDh%@au@0cJx4Oqo<{y$ifv&nh%#Jl*qBHrj>i4-^*sGlJ#WIROBtD->U=>XZ-PdHn;zt{X8{#o_Dls0`NI}KFni35TzsOO6JXN+2lQq}oRWs*b ztH~+`UZQm~eL=AOAuJtx*yGBT_1Wc`FA#HdESo_~N_!=gkMRZlnfQ(#zCC|A^l63e zOM1$XqX^=H%W*BEW?YG61jPj}j_H3rk7i}k6#N|0bO4FH?IPsAjPqk)Tk_@*i6=Z( zBv20|Tmzr^?smQ0|()fl?wG$-mh``|!w`09u6*gbG2ZyaVIT$9Q)okX# zPql`~{$572+-D;gbVi#);GPVi^p%cx%M55h1Ks8zQENem+AOCAQJ`HiEIOybyI9@o z&|9cR?2Fi-Uwm0*mhiJ0F}Tt7u#mqF=jr2CwKJzqYBT4Ym{*P}w zTZS<8-}8+v>aTKca9B9`lx!b=w!V(M$0CR2;fZ*Gr8vse=jP&{ZiTGNe1x+7R4fRM zKdVR@VibhcSZc$Z0?e+Qw8Ou_ol8H*T;Gf-MQe*YFhs8O6HXh;4CX}$?*AI+3&nS@ zNndPTU{x=tKkUST>7Xk=$_pESD`Wk^fd7$~NI+G|?=zSDn^IBT5`1jo$k}I3;cp_H5t1p>Bwr7ZMnMAFf|Dn$aaB2SwiDX2&ClTR>@x;ViuxUHb5%`xfe$$g9QEv8s z1s((ooxb03Pmt<6Oj!?$ScbSb3nfXDYyiye_I{4O@BiH}v?M*X|0hS=)22B}RIB16 z{&3_>ODT>>Sl-L~QDoSJ*#D82H!D*PiUA&mRy-VUq!c@`kr$pmJsY1$-&^3$+GnVT z?#iT|Q*t)ukh0oynmRJ0t&O8guwe_0<8UgSW{>X5t;2kz3xJt%QF-R?VPJls5GC)= z4EfN@xfKf28f!e*bR;zLDmAWN?v^9cRtvg|s){_W zV1+1_l@gU+s!A(b_P=B7s^n{GBPi&liI)3uxjgDmTF-bTly>;@RX+8x_RA19*CHh* zpM$Z-jHu5XEI(>p`c5BN{(04J`=jAypG71(-o4U+=1KubYy$u@-9NsJ#p9}TJOxX2 zB1l%*L~llP|3P>DNi66p67cGim7s7^eg$VpJzwX+dPhom{oc`F(i7Fa#ACmwmM;AY zr~z$3d}oK@(v-FtQsnd~{7woLXj#P9IGzh}ieQ)>P%rXcgGF?t;hh1Z>I{f>iE`vn z=bs~ZM58?`OpenEXJ2t=F9QWP%kj)bF&ePONNB_Ih?+%|i-zUgcfDMwL#fN61cz+R z)WXgyc_kz%W}+F)elich@TZ3yN8ZrrOGCRs&kt7oE`B&XytG)PieUuFX*rt&k3K6h zh)Ej!|GjyZ)UKoug!o)Qfe8iwPoCBYP=s`Rj5>ujw+qlWj))Dazzc=v5h8hA!1Hkg z>XfoK4(fI!)c~SCxIQBuQHzSBdKNZD1eZN=TsVW>^YbMgyNZFoT~e|-zseO0Srj!Js434*N`-V9Jf8$~pnw8a=?kDzgmpwR(R8z89vs`4A7$Rmeha&DNDswn(+ z%$k&9!FI7wQ@Xm0GzWbemlj%8X^KSOw8$37uXGD132o}GG+H`AI<;ybbwVH`7C9ONLMxLcO=H+p;xX3pmsu2-o$>ZnLW z>cI_u>3rrBKhSDk%EmaB&B}=!-;a)>g8(SSUk1rx-OGaWWg{sbY3(cPU5ndYW+7y< zTlJw*87#_IPz2C4petwEFIQThB7H2;hb?D0KJN*Xl4FmI%b#5wKv=j$Qg_K0ZATkV zp)TtWy0_R8(*n}@^0Vo5GIvORr4uuCK~vbc3w=TK!d%~-xhA=FJ>C)hcqJ5=6KiwC zBW^$<8OcHB268sjsnm@jBD=Q3TOdiagY z0Lj&opuqhU)sA57)aN#>%%;*W&b-Rc;M2-7&yVq=O}KL0Jf=(1VCVK1;{WF7&gOY) z>zS72@NR-_-wn&qS;o-2%YgRtd^HC@IA)w>%k_g;I9pN01=3gyJ4MPi?s@k2*IZQ7HsbSyIklkXMyTDcCN}I~plyeasjjjF@`G zEA!D)l-+~Op{l(?fY-l*CId!qTm2PU{+z!&_7KJhe&9K0?!TjK%L#kN!@&n&>ez|g zaHxrlqGNjVCZr63Um@(O%+0rhcL669wv~_d7l=P0O_Y5@vnJA+1$f7IZSn~Bs^kUU07S!mL@(U0ai72G zyJpi5_oiQoO}}fK{!BLgJ!zt9Z?K0p*M4urx;JCQno~of4n7MLBvs>hJR}X3Gbups z4>zCjx6Bi^T!}WW3^&7)q*m&u>NgPip82x%p39IUcuCY}B(S z)wM^q&1<$@gW7lwv>Oz5sQ0`>MZF7~ZwI!w>Ik%DeQRs3ZPBu9 zp|MnW?9s{?P%;9382-33T(d>}yL41tXKbj7OOy_ZsB@aWGmFSN{@eQ(0$sV5j&U2E z!*QKtNnHURT``ZlD~P&Z|7l@zY~St{0X=W8{qwGEqPw`xAlBnu8-aDvRF@P(Qi)|( zre)82qF&z4_XBbrvJAaL9zBSu-Va25>2>caKz#+&eTVklZ+p6eM>^kFc6%-MeU$6X zV(1I|-n+ukpN;Ho?dggX=skGcAWz>_#_(>u?#c4wzNZ_#p9=?S>Uvj+(1Aq#-wd!F zn>_>je+Eu0^;_ZwcI$e65W#K#^iXKsuXqpWWOZBi^z6PJAPgG_c-*`#P>C(%PahVe zn0{FMXOL#2r=$QKCO0%0I>1%bA7$9jz}PeFF|G^I^V5nka$Z2(unF2n&lf-k6QM7}+f0&7jXZ~%3x`*)8UMLb*kJbKgWM*DmE zJ$kzjdxoWZhYhEDtcr#=7{*|mLz0CUflbVVun{vY%->M76r{&uv&)rnoGG-G$s^*E zWiPU~!+;o5@Mk#gY|LA$cC4p2uWryu07OPaAJ`=GC{U$d8NkPV$;fhV@uE z5{ukBMaFwaWO~NV&boV(T357cr<1EOR7Q@4M{Q%~=EmUFZlGX{clsI%#O&z@)U z6EDIrRK;UOo*mx3?Rm-bvuE${B`G#zcGPe{Q}dYWNT&zi=iv z{;zkTCiFu&(IRW|s9w>+VNc(H{G#UFP2HMrSL;)NY#v0hZM9=@>T zGe5_$)lVcRO^T2VBMkf7tOn8Y^I{E?2L?k@FQ(LbKD+~uU53sftmo;xdM(a}ZKqdE ztXI7HmZFm&>t=bLw=Y75U{kKGyGaGY*yK;n9v;@+D$_n=z z#>IveIWMYy*H0Nuu7`M=nqe-mVSFQbeISK*#%evWZ#}oB z-^XC1E@T;RYIA>D!b$F9i}gs4!s=|!W=QwuOv9E>ibR`S!<_b3iqO`Ewd7~*?ZcF9 z?}F{qneFrQ?XM)Cu7o~)|LeKkhuHbM9b7@UmDt=y@{Zc!({&%#4(rSiz`K!<2_!GA zrDg*|^niClu=JqPyPRRxiX{(P;tsZ8l>nK14wAw)SoA*emBmB$Svi)<-4FP%V|_SP z#2&VJms9&)XTTPWY6o7cv;G-%YzyWxdFi{ji`z7HwRB2e0=dbi9 zFz{4f8BnJbr&ySh{6cuX)AqFpR|)(~>U_ zw_r$e{07wF3$)C$)We3-!#Yoph93TlZM@nFa1?37Y;ljDK9EKjNVWr@L^De50H^@R z6vDMUC}7$JAkPLyE(|0|Kay?~VWTF#)5ClA;$ayA3qYn;kYB>W*rCd-WPp$gupPJ` zjlUvta=I}7uJ~1YDf6y{h-~E-UjvX9YoT-a!9@R=l}Oa^zk@JM{`u{_&0^ToIYPH| z+!u3%PtgJ&XV14uFAznL2;qy3#_;lT@RPa2pB;}>liHbVfh4{_DPbTHoC0qSK#lr( zs|b_21pM>{5;gDm0>QMl$K^UFstzYE8q9=n0JI5!KgYub2UC4L;X?zUU$5|NLClv| z&;3CntXEK$Bc52&_H4%igtJOl;Z%bt67 zVtf(0Lyo#Pfg09s=g-ZxN3$6SpQ_zqus(Yj4UutLAnT74VoD(Vu?(ADM}}rEk($4< zS{^MiROF1sRnOH&nyO7VF`K1K1%^_fqjA;$vzXekqY%-A5i&+Csx!1I z7n@`0GLUb-+d{<@(`SHV+ztFM1}mR-|efL_NCH7>2Nv4u0w4KMyM_ zW3DStP-77aoq-_MdXG^Il)tI~l_p&69>d)$6>~1(ivw`HVjMm}Q)Z#o6 z;t?Gt?DeFvSf&CLBK~h=dSyX?_bzDegtfF3$fH)e1N;=e+3|zq=9e9T4o4{|^^+i`S8S0szvu)O;Q+g8#cb%;gYvoL6xI??f=Z)O=w6p^nsJ zF*-wAQ3epmHBnvcIqp|o#%h}om#>0yt#QiLuRe=r1hH0CLx*_hJ01>S2M%kJKkmlF z8ih;myVmpng*NkfKIt4?e1y)!gO zWmB*alJIUL{VdnzB@<%PCHAF2b#{_PNdcW|sD&KuJLvzZX@C38!JhFt(AU<9*?*kZ z+1UXRWeQz%;v12{yY5dX)p9dn^+nKj&-Z7qzth5Utw5}B00eaA^y?F`bExim;sS#y z_wR32y)nO7QE)Is{K#z!clqvGpu_6tp|dVWN#6OEGisFl38x)iGM*(T!xeMjo<`XW`hx)g|rfm>4fgS2QeZ=c1n8cT}TSwxRUf;EcleBqsPfh z4RNQBi^YSJ%~y)r*J?OZ1_=witBT2O(*gvtLKZhijB{HUVQK;Yn<(hwo0HS(rvy&# z5uo=-PV^R`R9XU#dormIdV|Q_C2xzLr`BvF&()Pf#w_mutEL#^EdSzD_+jn6OX9FM zxI+(tccck0U@fH;xqyH6kKYj#ZN^K{cnP#!Sk% zZ-L=K^7oH=+a4>Yx-UdHJ)6*0cXUau;nnspKo>}g7BVeiDH39L7TN!Z1LgAx>f&KKHcy8P81{p+uwp0$R%*)v3hBK+ph4=U`9sA$C?3``JvBN5SKnkRar zBMtr%RCGq#dgxfwo6qzm>O=f?ZD6?z?lh8nwYcycotq2~0A6LHolMBEK_QW0Smkcq z&^`WL+Cbro9|l?*M(rU0^(TArC|2?)LbxH&E(VoiWxLWgbO;ecosy92RYAph%L8Qx zDQ8VGVB}>iF>3{`<&qgc=3B%bqTVR@Mu3!Wz_94EWZ_?iAo0x#+K~8R<^rQh;d_-9#397vN z1T|Ajv)1}vL}CFWMc1TLV>Ei`qXwijj=BXQd?%7xQefNB@IvVJ=C{>?qM%#3_SazR zjbSQ?EuX*db z2*$2fi}#*=$XcdZli{xUYF-2Kj&~H^xGqF2SPG)v0VD-QM-X8;pN6*|Lzr2Y3pQDS zB(_^1*{m^!B$tuJJpgfih$APDx#hqqxb(i|<&Qv*pY5#zdo4;|5Y*&&P|x+;E;}(e znFI5wXa{{&c$^Y_>^|JY@N2}cSzcgcU*F6zkS(<&oZx@A<-Lp)9`_^j-suv!m)*+j zwImB(t$#pmE35oJ6Riql*Ec!y)n^(30#rGc@hESh(mSYzKuVfIGw?y8sD{Rv2*{Vz z*zwr9K=@kT^6!b6>EV1vNnlllR9vw140}(Gc-7132Nq*WbX|UdWn|}K_P+{Rf@(%z zw+s?gkaw??T!~izmGOcwd$z4I2qy8c3(PTk^GN5HQ-3L)QQx1<7nk;dpSJJ}p2uz_ zB)Pd9wX29d;|9|N!$HLT4!5@GQ8HgN4kjuBZ?2PD7>&ZAY-V&S2JBIip-FgsFPXmT zPjK)&pAb=F{vOZhhE43y|4vB7^e-MSKj>O&+Az!1A3f|+to5q=?%dGP0v7P0Ec0o| zMScMd&#`*Yxc%W5!)iI}eG>z!`K0ZyLg6FcramDHX=c=o0@h#jgKoXytI$`WeOUtU z$5vzmiG90BlKe-cld1O)35D1MTl2-_aP|Iwiq z9=5K3HYf1p=c_0G3Jav3A1b6d`kW`z)VLlP@jQXj+3ry_rQ?aU{qXV9zxwX4`1k3G zTWH8^NzfbE?Uo&B&8Jg98FF=YtL|yw7SEI4Q(ueU|GT>5GyZW+q%`^++mgCzHD%aY z_xD?dPw__7qhHgXuq*ospI)4leo8;#dHH7x8-Av&_FF7>buEp>_uP=_-=D*mw-QTC zKTnzNP9K2;;b3_tKaKd^xQ7@d>3QoNP*6DmSUI+jCxF2BE zPl-n1HxCdSp(&F4fbH;a&HYEGsl@4`OXF$nx9C#<#p$99mDT`_c=uN#fSYFK32lUg zG~b#T!h--|+@1RJl*E2;kh7hVw^HyN6+t8{ijt#s5RtB`O?a@`QtdJHPbngP5$X9B zsZ2O@mxa6t?IF)5k#rn7n|mZFE+bptBcDw2%@@zPG{$_b>q0$u>3QAp@6@FVoRxsS zJzCn+(Eb-#*H-@Dq-zW*G;=uYm8sSFMGB6H*X)WPp(N0`7F=l&RzY%uGsNjp3Y1ua zPZmg1m{SKYFbgCd-BnosKByBYmPE^V*&`xw*JaH(lHOA!w>fkIQ1R3sa$+nK>K##V zR5hhgm69KEAeL~y!`Stz<{@PI)SZD}=}p^5Ss1gnGDf*7tBgbjTr0=)Dn~EJ)n715?GwSBtvG$l7$0x| zaImPf0$w2fPHr)Vu=%#tPbQsTE)NNY3IPlO0D3WCVj&7*+nERuO&1ucVwaB10*HI6r+(9Rt=AmhFVBzX zua_QPzSc_S&}JwEtOlm6WzXR3%xoIzoCxv~OVi+$0#25sBLe_$%ggu0X8>6B84%yh zmwk+b)|*`Zw@mXldKvZhMbE$$D+j!-F z!K6RtO5SkRbu5&08011xsMpGkr4CkU%+nskPv!pVvK)hVwclUkZHULUcP2S?&q#R-I&7?(tg)e9*aZ- zmNCZBGLI6Z1L=mr6S>+21VwePcg0uh!sZicd0va~Jnya`KZ^Ea?R_o^Wr9N=05iNO zky;Q9RRkqmLxPTgl5ZmWCx++_L7(@0dSs8B447e)-jlpXbwkx0dq%LqmO71{Sw#VT;q2CG*{M-*2P>|`&Q z%$Oask~;X2X5);`AySz#a;FW)#31|%5=sQZ5!qj6O32r3oEXcu){YE}EL1F-tXDD^ zSMJN>k(?S}>kVwS#(SYT;Eb`$`G;G~2S=e5@QCUkh;*N^$!W7j!R3lN680CB|15(b z__^WkdhJooit%J?`0smhgns04CdG+BfURjc5YM2qscM~s!HG)ix^nmQWgVb`mXiwDN0p{Jgu0CMTuUb1HajL8^~?RgV~54N$sRw>7kZY zb29mRX6&fEf_*x%+rFWJ(e*eR3%^zotxx?#P*Om?xhGSypOE^D0szv0ph6!@a=8Eb@>HDC@*lOY{mw&En|%$szDLi8gp2FKA@E< zim_!V`%L?JU2)VZlT?y8!*QadOQ096)Xqjwb`j7UF|HsS)29oiD%xz;fYS%ZM zv~Lo~sbbk{6em~&epfxsSCN_$gVAMqtz>vVu-=KGONs2w2W$Xr%mEM9!8r;U66W_7 zH2mGPy1vgUIRG96@FF?_pPm!+^V8D&$MCx3YU-kCG1CU>k2)qs`0&)Y>?K3%+LR@h z*n^q42O~_hG`i&d^UZ7uCpgkn@FkbK6PoOgqSY261 z9s}qxSS>o;Ei;dt)=sDGODNh&Bs;4M-NBKv1$(h?9a}U?NP>fQa1<_2IsKZxmE$wa z#`w;G7eZ1O2~kTRQYbpYB_3B&b3j0`nrrQ}|KF*Q4zVyv^EtTk_)H{0$=3K4>M#$r zRMIk(IZWl@6`4Mw)8_ESc$sjm^9WQc+6RbqEtYYn5pq1I?HT!~<4wH%F<-p}w+3K9 z)lX-59$Ryf(iZ<`nPut0#c`N=JT}SOpttandC4fXu}+65>3gC6s#QubGSNyNQ#jW}b0;(ueWnGpraC4EgfYJ2` z#oUeWrz0iGAE1y*QS7%8EDA`Ju6@6&oB6ZCc){j_6+j^_Ke2G;3!Gl|4GPChM|Rg= zjI2uhYk=t2Zx3G|LritMNu2KbeaKdP2xDd6QSSno9@+pF8 z(<>&~rsW2-8YhzeH5AN?k}di?SfACEKTOpA_^Mk;Zgx#p1z3Dk`;0t_RJQi5UlLFg z9rut`{k6ce&z3=zjMs-9GIrT=<;7jUFhqJXlCClGJAWR%3|bgJwZ-!`((dQA1y$E| zJaU%GyQv#^`n57-wYMNK*yZ0Z>lI_3q9N4Ajdl1g1B7}_ctwOR&4o66y3M@~ZDx6H;5|^kO8Wb;;;m&jk@7|@a`+}G^sDdP z7I}zL=G{Qd9oR8!`cv5K*RT)RusO09^DHkG9==$7^x}hBgqSqsX;Tj6T4YD`j|_pZ z3+V_VX+V$Ki|q-NP#(^fh{nfUMfB>6JzY^K94H$fXSO5nr5|x$9w^cK+5Gjn6FOtZ?|#Tcc>@lQ zkAC~05-JTq+&V_sb<@LWB>ROr7iOkO7eHkb+aVxTcaHG+*J(X;#Bg)YKTJ^~jZn6(gq1Rx zpjj^mqi}`2qqXoOhCsAH@8lrX!50Kq`(JuKA?8?#iDHM-Ot+tdglq>PMAI-h-p`3c zYXBgVr<6cAJ{Jupg_uy_K)7k)FF+I+SlF+p<7UWcp`E#S821fZ!o^lPDp{+>~=}V4eW#xJvPMI8RR|1zd}CC>ANQ&XT}}KBy%ngA|EUT2Z5Mxe6JAvSQNp5 zwCs&zn${dJLyypxF@Q|6DELhTVz#Uc_T+KZO1@DQlS*ZAT?Xz)qhLad!m>E_f|~MJ zY(XiK#~~y){gqk44uZ$i2@oq=Qw5KPD@QKg>>_rvR zQDC5>Zz)6;a|B1i%q}yMDXF9oASmr0IOL)2B?`i5W@{J9HDh+#Fe3{ej5(ViT!44X zjtSzle+*$zhlVYc5KvRoG_-0KL^r}oIQKJ}3CKP6yzXn6KJ7O&ZyLbRWVzRB^AAuF z-~*?f2Kdd;Tm%}T1uW#ml>4V?OY0QqUlw`iVtfC*FJk7$BlJIO> z4E4@9GKNaKcLyq)g()L=lu>jqg}iE}RO9BibWUgUOaEBhU$0VST2_WVCgODu{r20% zBPo*U-I_zzOgerSs=EdE2-XMTFdn=TUkE|yW zgm{#D{hsQN5sT=3iHkM-+S2a_FV#27h@R{t5jBfIZEN3-805=CAmHRYr5 zcgRNwTGP|+=iPYwmv|&@Ym(IR5J?475;oM##sr7B+;HZ8W5rP*9jCS>2fd~% z-iLf}FrkX-V8*+4vDnj3Mx^paEJcWjOh&=gL5uZ~@LA4xqPocH;M`1FqQs`8ie!3p zAw_asYHwEx#lm7b&+K@r;Upl8L_arbK}RtYOGX9w4VQg=PW*)6pdgqDEP+e_Fq2UuAIbOTla~f zrm)CSdd&XY$TvyLrK(5ME=aKgTp0#oNEA7ffszo`NdzGjpc><&iohqIXCu?TUzoRV zbx5bhQ?-ww4e8i_2rGRYj$A+yZQ|g1YiaE>>H)h0NrhiSIn>ZK^}@IOfK+h@`+wO? zVFoCLsoSZ>n!`>aTdE4+4(i>-bJ#YyX!@ifStxyLRO+5sVsaSb8?pa*)bvOo5tE}G z$dI1C{vpm6%O{JGQPFP64i4(;#Hzjo!=yVA%(^#G|E#*QK=#o^4{MzGA`K^HlLO+k zCKquMWVDuTQ}Rur2cr4M2)A;H26u(*iZ#9^nuB4|e?(}r$;jgBFTQc+{$*BGE94aD zJ>z6cWA+Tnrs;fmf^e`)3%R(ST;xR}@F`Z;t|w2H3H{joMZLb(E7LV&;~mID*OG&6 zC;e3oR=M0BW|~i3W5#_sRZhv-_SS`R1lNtiItgzP+557&HoW$mQ(k9ip8n6~A9Q%| z5j;B?5Jd_JKaNqrFU&^jIE$zx)U*w!?t4O=q7V5|><|(g zWqo>Laq98ukIjhe3wYxHx@7Y2U0plXM6Su>6xJ}T@Za7te-EAjIGp4P#|u1igBA)x z@NhoYPW4uf_l-?D0jU8KN3`xGk^QHVZ|khpdyQq1Bsqgv-Wr1g=OSkGTUZ5|`%hy6pCw^iO%={y8Bl?N%z1Stk1Nh~h2 zMQr4#ULSF|WxA>fQk6$)Iyy+#xf@Ggv&N|vp=yX9e4NjGCvL##IC${G0!Qr|P0OTI zR0ChO`G*spJEZJF<}5yckcgOAt}W>m`G|Wm2eJQs!>kbh`oWLHbS)oxJ~nBC%U&Gk;+XyC5FMNy1*&m)({g_yR z$NwntGYK z?+G4>)taGl<{yPkK51sM7!FODz(|d+OoDcRBMw42kTs4 zVHnIOzHq{J0C=Fq>9aY;WBsqcmBKFQ>ZbSsV!GbTpG5<2-Of*u1KNw#!Pjs0ao82# zjQd`FRH?g0Gh){9^?e$D(3?3xpS`)Y!GE!u&*M}dj^|+pbh=T2e$nAwf_|(!xO%0e z_>JR?F5Nxi3gh9+&UZQN6B0;$v%~wHVPe+GoqFW?hUq0oez}R+hgY4eg9Vfc7fR1i zIHJjW4Euj4%bKb~TCs^X(F3{iS4oc~|C1@VzSq8FGl&$jk{75cgw;3M_dy-A!AE^x zFU64aUp`}gSmlSPx_%n@pSpPmWVN>O$6hwPfmcfL@9C4m{45qptFM~h)mk~u6l_X4OD}~=0kS(_q%A~q2V!80W3hu{Z;R``gX?dL7qV07gr=Du!e#%& zM$W2p{p4@e4jw(`yFLLC^5dNl?%iOiM>3ke`L%O==GN0szy;Rt`s9Rj2zg8-un|KB zj}o)p^2Fp4lbYZqQi=Uo=u|{O))+!B}~p_5mOO8q0ot}F`K)iC}(5eo9Bw=thT$amhm#O zOQAIk0^xM+6WNRyfl>CNaZNVny=<#J^`mlyar>I3Jt2#sF|H9|nhFGCMImQ}YEd}? z@L4?_&2q*_#jH!S6Gf{$NX}_WjzW$k3=0bD0U!~aMn#guR+^QcW#4^%SjKVxNjA;p z;hPPrM$+#Dfvq$-RHC&nm`;^S&gVkA*4%FUIU3m=r0mLJz*h_V4tc_1Pa zLqH3pF&m_Y5Aznps%dhlCvs|BjA~R@KK70#)B}lW4XJ-q66j!j2q$?&HADcfGBR*t z9VawMb<_yAH`1vRGODTZBR7`{9<1H#n}0ai8E#>rV^3PgVvE3CV@ zY+@71@GCtoE2%PHT}wDB^V|L^Ru!FL!D%Qn4krGzw#G6Ch80bEd1~Z_X<+m2hNU6! z{34CO8iK-3f`_siVsi=pq&X`>2?W_e=qlHHJH zx;Q4m!Zo9&Cf2{k=}tYJ5fX)qit2~s+v+{@R0~=oC?jzuy&Y#+QexV9f~=p(Ow2Kt zcO$2m_~r-slTjAbH>y<@g{X2dG#&?-oJltmIquMVgu9E;B@kpd|7#!O+9**ESAxWc{{W#w~TvHvlPHdo1Wu4q1;V8 zJx;8;Mm&3@1!Z*My3?8?CV9=4vLLKIA+Nm@Xl@u7&E1}oFA+mpkD9JDM2_<}ER+#! zo&>EFA=-_@E#Am#MY7!M1a1%;y~@f(a6H-wnm~}=nf}CDer*V2IhQhuJ({pSf<8Hd zy;I3pHi__Tf+mpNaYb@UvvLz3PrRnLu;`9GDSspXZ-FVnDqbO0Ad;O&GiCjn^Q zSXfXG{5104nr?wIxhPxAtGg&g&&Xx2dgL8%RVavjLho&k)zFc}2t>-+bkd8E9 zJC-+4s{8%+!P5EG&$_aoTl}K-nU#$K_1>pHxv{~qx(vjV$NliH{~Awm$1Y`+k3M=E z-C*{b4>A;$zdpPxa2|Im4-3HGB^2VLryxO_y7#MOZ!GE2-Y(T~rHgk9T})3*B&wK=c)Dz~CGK zA^nppePVqzT-#Cadc+M{4FAXrh45h()mK#JAmU$URwizPa8=YtR{GN8aRhz2VjMo> z_GGAASrB?o(_l~?qoZoIlb z3whf?sfA4fo@E1xW!wM@JUoERJf*bJvX_-{V8-G(lGG=7qS%>)Tng@au%lgq9XeHmC zm3Ylu3;>ew8%MZEfcHwOTKONh*mQ{g>(@l;Im=yFM=gmGZUvMVKm9nt`tVn6he?^} zN;7e5GLS9isa@_fE=bUGn0TCNRl66ZS(^<-A|P}kxCHRsy+WD;B0V*EsS{&ws$yAO zu{&98SSn`rGsb{;URmveztCb=LWitg4=`?kRHs&W`3%IPY!}35uquo^dwW6mp*BS+ zGu;h<`PrXB>_H}}!J>@y_96D#kc@kl;eeHVeNkwL|BER7_#3mSVe`8(V2_F#b#OJ-?5O-Y8uwTp?-#A@Flvsb*J^CfA@=ICpm)A>w-hpSqI1L*%KS~>_ zm4hY8T9UVW0rTNciQB!%1+N2&P4y+SmPJp5CvrvUoJ_eaz;(}#yX`93Cu;l6i_T4b z=kkkp%`tA&Gq>w+L3?6Ek`AtDX3j{4aRhf~l)%}14x1-~%pfh}1eG#SW$7kGEh1`M zny(z4+5}FGvJSC{vt(>UTD`;b)Fv7M^KY)d**i4xDMIX^#gvtB9@zn00c^g6bm*bF zescva5Y1fxTvrAwKBk%26r42B=>+LYA$y)$h|hPf#GwV$JCJtkuPXlBRWbl5Z0k%K z?i8?ljw9x~95Yya*hPIU{ra;L;4T;@bo*Ma+oJW0^{6yP_H82BZ6RJ_^vI7@Mz{K6 z6d9|MefJ5h7f@Rf<@HJwY>;M)8Dz71X^%JjJ8BrQ`QmgLV_YPdXkzPQ&QiLLQ1<7EBhs&P58h}&Z3|p@J zE`TQ}2oJMe;*cbN)(QkWG|G2zkDy_9gLpW@a(6j@Ab$UbHNQKEGb{MX!sb3i&z&|* zG4%MnpWf1sQ?xv?zw!ctH{Dflk}-ZwaKyb|wLXB?#ccOp&tl;oe z5lVX7Qg(CbE)TH%;qUeb@%hK{7}$%U;hJEIuqCxIMQ?=%ia73(-y*94Bj!a;(sBvg z+{)K?M9F}W-%-r|AaXNPp&EjX=G%w5Bt2PjA7uHvJz5?-3|>)x0+SuV?icH29DTg1 z{{gYaF8gA>5L|i~GufyVU+5N7&_4qHJGAI$6i#VU)?$k4S0UB|>3%OYe^VxexA+o- zy*zD*V(kQw;r#ni$*+m?!*ZZZXfc)2MmQDscU^Jovw^ZNR+6bU+5-=kg*1F72>$^w zNdO?EB$&lIe)I2$v(08K4EI&MOG1M>&lJX6@2pEQwI4asS4D zHz9#b`=o|9YY!1EWR075+;%vXBOHNy-g(6{7oqWz^h;hK--jAQV|uO-iCt1bN7WaP zBbWG0S{x>FB$BRF2xTwa-Q5Z zM_Rf_J>UI4ZikEbNXJUY$;_mMXp2BGmw92OG@2KCW8$Gxo$@Ij^zR(0Tu8TYrWai^ z4CCy7{Vdj#`ub^DBxh}C!fsFS+rPgIc+t{Vt3dd^J#$*+&hh)lVGp@&$Mb!{dLN~SznC}q@#`0s1r5MqOhL-<@p z=|dBmVggY@&hr;Tgj18gN@Uaq5o9`HP~93*K4pCjjdftgNQ^;^BLj_rh-xX_rxt=b zx=6PRrX%@3|6!$JyJGc;?G$4_!%?I8vGEHTV=<~`6%sKy^4}VS3aPZCt@^{TqLVk1 z-~4&rHXD5B5(i&WsIx)Y4K;sma-KlAJl*cysqRVXI$|=D0Us2x$>)=T)P&a~KjUp1 zyAFOHE9sTn8>xepYs~GkpZM2vj$_1qkx4p{$zOW_qFe2ToF6RiU3%-GyZF!hfylm; zK*$hpM(bnz6%!$C2{UsQ4@54vTL-miZ(_{ZLQT<-SCxw{ZO4+Ksl`td(d&Y>}~X;BKX9PWL@UAu|NjHV zKsmp8^uF zFeeZlE;^SQBu~HtA1*q$qZxVZ!NwAtQ1D`ycFLJ038BM1!3TN_u6Q5~&{msmhfKg*Z@y&$ZK!Z3&wKB_BU0iOZ~RGT7U)udMFAZ|F(;qSl)=k7%5L zh;z$*$}{xQOFzp4LR3qJ9An=65%{3_^C<>J0=MUyI=0Wg@0C(LJ@wTQ!v9@&f9x?v za#fjMeKqorV@yc*=qnoPGCc5PfqANkw8oD@Tn+_@ zn8y=Nz=Bb10|+Fj1U>XX22I3+6agp!7}S9ZHRQn?zz~S$Ot6C{n7}benh`oC5eH51 zBN8VVWFZfkNQ7trXb71RAN>eOpg0nekCE26#)ZmAUW5qVxJDw}ApeI(2+|DkU_~4< zVM;xc@{go2K^*5;$2;<>2G1yl9@G$rHk_aXM#vTra8iO?-og!RK!!1pR|!g55|a)w z!WFBO^H$r#LbI?L0v&13*h#o&^PX=*KQ4QN9;+zzI_@6*bq_yD;#> z6IvL`ie7utGoS-a4oqd*k4U`W1VA+>I*E!^i~j}yqe^W<0Gj&Ls7^Hv z0U*H|)G!7`xT6%kQ>#$_8P-z`WTI|8>L?8nhnb#rjTSn=*52r|C8*=9HGoDe$Ox*t zcwiLlNWu(u(TkD(VIE1aSW6_r6CDxE2FU2e3rK5PXrflNKO5qV)&T?^fL64V!^x0* zvP#^vwk0-V#}jl=B_BBhS03nuXsPtu(Dv@Kk$~%FeWF=cc+DbcT^z3_@?0I909iPC z?F(W%Tc1>oA*>9SaeIf8pD35P>Vqp;V#`e(h(H%hkr=2L; z2`BKb^e}Q<`eXwPq@c#DsAsb4C9wxIM4bS(2M+x&W@I76VyPqEbd(nz#+`Z z{W%SNKEa;}LfSlYZ6e-PXG=iIVIF{^Z|IHLq8n`yUv=8kn*rB%K-~-QMoS?*0EZ}$ z6ejbLtI|(CK@eaO4`wU^SF9Y;2}m7Cg92IyKL6OUYfPeTN(T>EB86+H7Xs->`-7?~ zxHJf0VZv!w``XQNx1`fho=a7Y+oi54xW(-Xv3xlYJm8wL1N-eXgnQfy*|(wl4U<|& zdEWI#2*A5N@P`+CAw&T3j}s1N8Au`OgNA-pPNSk_x7p*y({YE!WnU9<3x_& zM*DUE5A=A#AA)Ohn%9BU=A^>XqaXxNUvJU$Heu42&U7J|IogXp`t?|{>4+vew#G^b zsY~&}Ki5I)XEy=Rg--OkeR|vqKA<{#;0{qtLb`m{nov^kl9nA=`eY`r7I@&jb~=;xc2;Z z4W4Wu|Gp2wR{ebWvwkvx4OQIw7^VPJn99xR_!hnn$$MjX;wqvr!kEt{oX_Q;uf^Dq4BgNS69NrWk82uj zAn-5%KyD%EFgcE}57#VI4Dket;1lL*{nlj8>g;kRLf*20ANpV-W<($OsU2d$1WEu6 z?qQkYU=I>3@Bokj)6W!9Q4dtH4Qyl8`oR?B;1SZI5<)Q!Bw-XIP$h8S8KU6{4sQlc z;t0qgrF3y-uBxH}2^2+<6g9CDIMEYhZO5oZG2#IgoG}IdV4vcE5d6snCr8Y}A*LR| zAlSs$it!jlZ%~%88F$evtpCX!ctH=e$p^a;6|Uk0&VUuvKnoD57mOzKWK+g8^JN-!VlnTvLRuTu4GaW1Hl6-p&ljy3%;-AbWI&~ zQ6nsJBN@`7&`;wA!bbdI8z8R|rKRU8YbjoU2*}|%KCu$g;TZhk9J**C-bNV6;U2hQ z6EaUK#^oI4k}mC1A!r~J$RQq<0SIP(~BS6pp@^aWr z05AoUFvTW0gk7a|5I z0Uy3#wQTPvApZg-Qld$j4?23nBWAQlm+)XvA|rIb2A<(%A}U9>CkQdfNRjj{nA9Nh z11linB4R8mbfQkkEhipQN-yL|?UVfqiXb4vO*O(ybpj}KVn~-_Ao9acA9641^ibmi zQHi2Z)AS%v;y%n`Q4^y)&@@lwFh^qyGA4CUUQ{ezLo2w#D-y!y@&ai9W6@${F6u(m zOrun@Vk^4h?eG*N*npX2iXQ?&GFJ63BIGp&k2)<;^E_l&J=Iu`^-g;NHB^H&v?KuW zf;aafRRc8uf@3&dwRciuHEaj*K!Py5L2a1f5W>_ftaU=XV_1>RXc|Rbk=0$_^(11W zTTOLd|NnzrBj!!v^(+!{U+dLDs8wGBR$vE~U<=k@4;En)R$&*GVH?(A9~NRGR$?cX zVk`DR809w-r9L#)VmsDjKNe&MwN|=iI9|>?Og3awR%KUKV4-F@sYVam17%s3W^2}F z-6I87(M)odqqIb4#X^i;FotHRhHmI=UMF&JsEPK6hoa~Nz6g3+bs?bWe|!jTBq(vP zNQ=0rS-8i6N(XA4LkEr)a$;w8QmkxlR&CdoZO=jmLg5~+!4a18iQral>Go~oc5WdL ztV)WS2BNN@N~+>*kNPN>+R7&3DycN;nbL}!)+(J~BCZ%$lL)D$JShNFfUo=tuzClj z?EmSO95<-Yi5G#%l!%Fr>3(sCa&(0TvGni$AfDx*Jbm&MfI{1T=gM&SI7C?AV z<}P^q0Q>Uy(vEKU&Tc{l7(WR(fKxB1R!;~>fD-D#6P^uUvZ z&LNzS3NZqW7Z5r+a3DT#{>ayf_n430RRnCI7@i3-{y31`Vg&(NkR!$?zi}7;^Y@6i z7pW0(i_(mxavcpBBVf`bWsw$jK;D`Xi0zFYHL@Vm@sg+U7lUyiHn9`?*p*+obw^Y= zMXo}V(lFbyGxHNW3)GBz=R-pj&I}Ym2Z9DN)PFYgQ9)-i&(pVjxi%SeEpa(92Ng8c z2$ruIn^SgAK_Uxpl$$NpM=`Zb2{m8(^dmk(O8N{fs8o-$*`43{W3lx`_3)-L$8le+fp%!26Xc=;qobj9zRIFl!J|9pRr5Z&qF4G;N1CKJEeJxP9pZr(UKXQOnuYWKDs122+IRmKW>@#R!7mDEx$iNnq z0bZ~=SYP7roLWDo8ZUfWSOwLh&01c&*?uT%vuKM{fgl*DdmbokqyL9&VaFk6XL4%i zF@2_iyvKBMNQI_|Xl-ci9tX6Q){KZKjRu0UO#qDo;DS2)fsYz5uX<;oI;uDNq)XeS z_d4+G^f8S(sLO4*We^9S+ppyex`A7|mm9DnBX93^k+yqpFT%SC=C>{41nA8eP@oK& zfgH?AakuK3x(ahOx3B&xu;xjYKv%FHm#|=}lmZELjcIkq#lD-d5lRs&N#O+o_pS)n zbcKm1vvQ9b>#+pMa(Ak9W9qTcYOvUpE4G`u~tRr7s1o z`MSgN8eQ@9u8%sqH3N5P*LEj|cXfPwdR%uQ5bjEW2*5!V%Kref!gt5+Vi8O0#Z)UQ ztQUKsrK~%vx2!A2z*l@bgD0lEypY$(zHG=Kc}KAPe6VZ-*^F1HthSh}#6ZRAs0>NU zT1mC7UhQ1P{WZAZnx|D7B3GR7gb=H(G*GWwu0`Cpotg;|kEfgZ#2NI~1-|tHPU)=f9F6_oo!$Q+@WX{L z#YcR7@SLc>12UVv?3R%n{8cimo4vA9X0=o_dU9pZG&;#QykI7pSF|Psx-~wBMj5{F#g={4r zSw{$BjIV4W()NCK)w%oHU|42H*Ok-Y;^qN^kb8C@GXI|-EF$yUQSf;J)No-5^dS=F{nNE` zCUmlsKiMgv60JtLmUa;xb73mk5tV_Fv#b#tlPeXxQk^ie9-q-AkBaPVIg(SE6d!!C zVzb^h86`iGS7g(Ysj^_?@y-$OAG#i`?iCw-F#r2n-Mag%@Gu?s3~>A^;6g)%@A(wkp7j6G~pS z^OtTJ5FI8?AUWpGoiZUQDy1WNP8E;<0$&gzHx9@^6B_8ai#70K#*H06rnpm0A*T~B zdYJ3eFhCA-fl{vY*Yf3yp+&hJy;|v=P5%(EoV_S`FyTTIDZQEiD;B0mk|u?WB}kW` zz`JXKG~E+b_(ck^BMOYUb}zqJ&UZOv<|mLc zw>UR?fV4o6Q+`py+9vHMI1_{}9eMQWeXV1MnO^$o-R#j{t~*wsb=IX}L+&g2HGIST z^;TDI^HDb6e+70HnSZ1~w%=-s4M^Z^`r(HlfPsy6A%z@v=;4PThDafV9){2fI*kay z!4TRpxL<7R)wKf_WQ_7n5+Yg_WB-mk&Xyl&KlUi(ki2>JqK6w2spOJOHtFP(Ba+DB z3o&Fu&Oh;#f>dmd$+lwvC;TvtJ7|%vs_CYjcIxS+D)7OHp(vbj z$Xq3CVM(A|Ci&^Bup$=gth6dBrGEe2B|3qMnujRR5l<|vs_;iY zq}=D)n!LqI?X-BxmMxo;O?c_GK5kpW*qlt2_6tKW-{coYtS(5lru{bCVY^}KVbjkPg|RTnZ-LZK(mR=9o*53JoaF-Y5*Fz z4R-)@gIUEsb+AfVbVR5V$qRSow9{UzHCXb)tzot=fe0u5;;k1C?KEdyqLyvYwPD`h z;|)oEd0vbQy?D3BDy{m#qm!N)V<_O!4=Zs)bODWN#2mcwG#6!w;l}Jy0aw>E(jtK16I{1xy|FtGtLjG+S^h=5%$ zn87Z35t0u4VID~!9LPk`Ce7f$9CdiX67XTBl&s?;%40_oTo?eEjlwRw0^VHyl9KO< zuLz%LgZ-9P!U6t|bg9{&gE+UpB=Rp~sO!&eCij`m!B0Wy(piQ?*C776u6?CzA{E2P zz%mxCG@f(X(gM%}aEO9%yXqAW$l;GV))60R2+s(|XrC;|@C4oYPFlcsf-vMG4cO7n z3tSM4UtFvMi2qQFeZo`402+yDWNa7_?FT>46zP)&f}-Vo!!I;e5T>;h0enfbXXb}IRP$AkgF)wol_{f(*m~GQK%M2NND%YJ$9CHeI zn8y>S**y@hM+E_x2SK`5lkn*Z7Ui?gK_7TYP+l>Mm_%Se59m%T$_iZj4Cp``+Lf|j{8d$;sg;g99a#X@e8T2#3k69N>v(?!Ch)ECr3$-sH2@Al`fHqf$)I+C5vqmL<4gW&HyQ?MV}&wmL0|gQ`iTp_ zHw|TpeY&8H9?EseVUK=qBC+=748<$@<`AtR@(SfV8;H&1i#r@6kpNBMPzts8Ah)%Sk7tQEKJNnU(jg``BPM8 zQUwJ0$*$p1d& zx-~BYn^#!VYi{)=S<7NoO15!6eVvys!T!gv2@>sT3#!^-?n@j;jXh?kLSN2;mb6+; zZbIK0Nq6qh2+@n@pRIe>yasb&_9&VYXut_t=!94L+!t&LBnYsGnOO1qFIH&)5!6`0 z^2YLmkd5U8X28c6>O&ZN<6Ga@1=GFrz3&gr#S0 zIHP;GV7MDc7V(L*I3!0;`pCl0ZP|RhZv-I%RAyszNFd!;6dhS`hC`RCU;n)mS*Obj zXlApT`#nTs{Hc4PD%4jyMh1)+hBqW)j6hv2Yg-F|*TM$2?>+N-+3Z*V5U+T~+uLo6 z=f)*$V!+KOe)0BbJXx@&HLjV!1-780I9&4Zj4VDKvjD>6DgV8;y)ABYdlxkbcLF@H z(10niM?^&blHG^xdp;0GJH$bYW0a#J0wMctBU}mP`-Ayr!-gSJ0rjQ;2jqT%I4(-y zhf)xv;Upx883Hiz$G842+P6#o(E^8K+yfh&PsJNVFv5hetrWuf+K)`7lQOOO7-Fb0a? z4yDi#MBokAkO*!d2aQ03!&5waf<4+(J=cQ*h#*oYzyhOiU6dpkDS-}?5C@td9MMsO zV#p^VHwk{w51!x$coZHQ5P=BxPH{Mgbr=bhqyq{Eb#%c}5-1Qm&;%x61S}E&@JD=I z7XUEmgfj?Rgoqb|_;vz8R`I|J0gwsS!*BJb6n(G*Rxkuda7d;|JE|BhJ@^bO*n$Q} zhy)Qkvr{`3s91DI7n2wOmIxkkCxXmKYVsvs(1>GB)n@e*8UHl^4XywZl%zo%bOIg( zLLwA0;$tG;L{TPijoAn$1d$fXBtgC497aSC=qNL+!i^pDK_P?}JP+XU{gG=d^ ze$i`e@s+NmmUI!8(G-X>APL#f4%t8sVo?Lg&$*q9f^bWF&kOl2vKcX5^`V2Oye8bueb_!NC0nSpSTm6s6El-U z8N}%dc!7rC$b;#*mR&&*Z5dxX>ZNs&rN$B+Ccv2C(Ux8#L+vRLU5Xbc)d?U36Afny zu<(w56QyVYW@~|=0nnv-;fi+g6cc(}@Zfr(u^tOb5E|MLER`Lf2CK!lF~CQEP+yx<0DRX$^? ze}ee}XSy2Y3MSS1R&j+;Do_daFbVjnpBHfi_4U5%2 z&N`@Sv7qCV3)?COk8lF6co&Qs9);8orU0>{CaLUc0w~}FW58Wg5Csnx90mA9Kj238 z(RJ;5t#f4`VSuq33$rr&2)JVgrx*vf=r=g<4AJle+!h0)5D(vx1$ue2IlHrIS{N6b zu{KLqW`$N0G#BxD54`{zj#aE$J0P@G7qP{);FVAIRXLu;7gt&r1`%9mVILz=5-0Hj zBR~}Xz+0T~5_d|bZ~upAhn5gg$W+Ss;n;apS!U3^6o zHW50urLbRNsBhu6LNKucQLzF+1ED|;@!%02!?9vfmH|L{+aLrMp+Rnlvg8SI688dr zD_nR1x}xh5l!3dWD-=Xw6n^spRp1Z&PzbCzc91|1X5s_SaJ5(QyIQd&w%QlGtGGL$ zTk^nLOBMhfKnLo8i>=7u5)EC_09b%=w`_deIb{QD;9OMg?Z-^cQS79fZ8q+~tg;*FP zc^4Ec9efc3lrX!m5tZi=!T;mYA`12!Q5hN-Hgn~>wLb=U^W}4!m2+=`CNokaH?lB= z)-XRzU%hb^MTQh_X2Ts41d^Z*t6-awf@DheTQMBNID9mnGiUfkDPIyMV^Ss~7{p}D zAqX;NGNV-K)*(f33WtCxW_G+&T%A=6MRpR$h2q9+q9}XTIEdE5Z`{Xz{KtSC$bvk` zgj~pfe8!$a$A;W)rMAe9d{k>xF-RQ7kKA*&Hp7?}Yf*BY*5+{s@I&Hq5W%kaj_nKQ~lyvzOsU)vm7m&{q#e9WdZ&cD1fnIlg&$48YM znuAAE?ObB1mCd?bFqs6-^DIuQd|86WUYgWdKvFH|{3olVEdYJZQ&iAgl&J_rMy-6$ z>r~A{6|MPn!((e>QI*a2^F-S0O2%4(9$jATWy+CUwjv!*?wrq}l+ohMZlxT}+6l|T zTvVeo&98i4`vp|(WKJQ5RK_)ErL$#RnOS8!KaMj*A}B!eE^!9{L?p0&r-CV;JnY^w7^wq&{5Q( z@d#SYtX^e}RR2N8&gIoQRE)A3|kD7{&S-M$;$KQyh?Y)yhM9mJgcXP6Ax z=A2)IN7C=K&{JxaL>+ijz17or$9iqnoy5LmT|l***o4hrru~dAonLZoAXW9(S6$Qk ztlLYi(}g!7lU&&V)Z4%PP$`Yq@ch=;S=_~(XfhQs&o=!;2`*kM z9?a!j+dD6n7*p6+8i zChDtBBsaA<$slly1whYu4AQMD53@;s%FQayeYrl>&Px;?gc=(cITar z(*MZDEFcnY*W<%|UV6jq>fwawDh}&|N6Mf6&8q%dnhxq$3GRv()j2|0^u$cL?2HIQ zaLnR2_mVFtJRmX`3BXL}Wev@gZfQhb>=y0qI@RQ9Cho4jX61hDeNE1drtWuQf{o+u z5Do84#HM_4GAXk%xUqLNb2CD0Y@uoK%^mXD?bG8<^5bpamMrdvu1o5RIB|yBTy0e; zAXjg=jyP``H}8U5&48!pd8-F`IP!Y27XY(o5ny6_9Fcp@S9vm}%S&&z|Z_eZYx77o~fU!|>$_j}*fc^~2Ld{r_K3ioggjvyBdNcooUT_!O3 zm4Er`(^zyJdcny3NI zm5u->0tA?`t5?Ggf9555aj-=$V*yyCV)sOcj*JFzSQAC?;YyYbm#B;MBL88m7cE;R zT-dOohfd=dZZ!5HMWuH_OxQry>%|KZxqt;IIS_}rIxm*s<6>gyIv_ecjAEB$E06$R z!yX*!v*1CtZ4It9(Nd*Nx^*AUUFkOO+qiJyx;^-p;9$IL<@UA95^&psiSHs-Z1{0t z!73LAUK}|tV8?s??!7$NacI(w{W7kL8uMz%q(xVzJee@$%Ca-Z?wniqZr;6puUz_K zN118)(gYVy+&J>#(;h}`i?ilR9_0L8zm6}>2^o^IwaZ2hYK^2i7@tf@n{LFQOUwmw zEPdsP5IukD3Oe*1R1mP7Wpp8!U;a8Fi?B|p!J18kB8s6C!Z1!hvi}}jZmkXwd50|5 zKI8B=-*A&nv&;UnO*7C?&Twie2XJJeqD(5H5P}3s>hT1N zE_8Vasb8Sm3_ZN+>HrUVp7hC`jngYf=gLuO3y8k3JfxkXMJ6y3h7?oraMb%b?jj_^#tkK23VC}3l5^2Si zu37bBb=27olhIgYUp3aSQ%i+5S|(9o;}w6d@g#|dsI?Z`B>&>#_S$S`eF#jJW(bBF zh8l9HDQ6sF=>w23T5BaA6jHCQAFMG27NzDZ;*&O5GIu2u5V`1u7VtBap+qxMp(h}s zO7N00r@$kCCn$~hB1}8hb}bQL2?|ubZj?4hVDEgf)?IOHNF`_5?5#?G*q&j7*ueM7BTZ(Dpgp3~2nrpB73cKr) zTn?b%hDt!=o?`TfV~;j;P$7VNCUFza7fv{WoNE$sLykvkI$@)5^mqtfyHuzo8-Ds= z=_7x(`8sVtSvp~fdw6k!4P^3b@P$MRI-!ck-&!J{I{yZGfrx$z8A=~k;*g}BSOSQB z_S$cMj0kl$Np@#q?IJS884F7mXjOeR{`h=zO)`1sr6*Olp>L~RSQ~ZxIauvwgnCC_ z!*^L!Td6NPX{^_0U*;age_1lMxAj68a_-?~lY(dfr5bBcIcasnm1q8M$=M7{xk-Kqu@at!72C8S{!1KczVfe9&{^^Po5z^ii>V zMwDW&kax1|Ma^m4Bh~PT7rbI|kv7+((e#o>wf|?e&yA(JS(3iDo8Yu$DK=S3rMz}W zs7&Vs0-0cf7KFLtCFYMWSQi!hk~w$CB9XX}(AjWDl!@6y19RXup`%rL(LoftplwwIXSyF;6a6)n3bJ95Dxlo2S)S(ZBXhbC{ zmx=yHapXjfFm>}sQ+2VT9|dVhMLJTFmjBeGCq-#WRk~7^w$!CBg=tJ>I#Zg~)TTGZ zX-;*zQ*&lu3$rLy3CJ+8_3iTz(;=uOF8UCm5|yVRm8wu3qe593&SHAh zr9?0Xgm9oZbXb%1 z0{0>HZLecaK-SI}K^0F}$putV*b_iO6@d*v2LPc81_R;?H9^53N->0oK!FrPH~|wF zyx}hd00t7KZ~!cfVGqB7!IwCye);Q|<~|p)(Pf-undA@|gyLORV`N+vM}j|O5_18l z@r^s<0$qed5MxLLW=n-0J03wE->9UtUf^UXdv<*%(0~)P(1{EwmX1`8vSXNBs)cQ~ zGiA0iU5IRCBrDlTW6q5VD8Yi5rX9nIv4|d*19UOui zJ@eVmfSz-m?+l;CMS9SAM*o7L5v}MESiuTl=m#gTAO#@=TGE6*L8cFFi$r@FuY~a5 zHr8e8OFQNx%D{rB0ql`f;9A#)T!K6H+~+@+APoBOgcb;MpNSVf25a&VZr$PuqxU>7AWuz21C$r8=CNtbgx^)yyL(lE+K{{ zFu~r2w02!i(CJTydelM?XpVQXp&? zA{DYWNZ7AFkhE`bGyTDZqX>fc(I$rJJ_owcS1<}$pMV9Uphf`x?sTCqKJYr&_UWxAWHyH;Duqdqa3rSxLxYF5*aXJ7~c4wF-{;ThICc~ zcW4IokDvU~wYnvL^j%C?GX2EJ%fq?+ZVF82`V-06>((D@6#QYuJ>;AV34G2?Vsi z>bt(|qqZUZ2es%ZgYddYH~||FkR>RGLqM2sIfyCrtP{8aP0$Xi3jizp1uWFUi(m^c zEFu$NL#zu!?NFQ}c(fCF!%B(6?I;Mzp`4=8fx&}2!I;9e;KQ#m0TV8F10Ktx5{ zxH!4OEZo8rKm>Z|Fr28uNKC^dFhexlLKT}l$GCt?_yQPcf)|Lr6ZkwBfC5Wk2+unN zdV7SUD*wGh0EAeC0$1=O0qd}bsKr|pF@`n|-U!#^9qN=Ki1CSrD;$p4#?LZ>D;N_h zkb-#=6hdK`ay-X$jEVj7hQA4%Mreq6tjBwNfevtjDd4geVk$F;huO)5c(AK-Gz30V z$CNt(g=EMsOPny^hh!UrK_EGsC;^C+NQyMMN}z{}^vCJY01gzugPDgaIXo8fvW;BG zhJ+V>M8}Lg0fQtHgq+4|yvA%~5Oe&=m_PwM=m*`o!Ih&%Ys5xvY|5vkov4&YdK}7m zJO9aujDv}^tF(CrZv;nb3reoE$5Z^I?V^Su!U2WIf_oqZJ;5h~aLXZj2qOCjeb@w! zG65LafhK?f2?~I?bcMOBOM1u(yyUJS8VJE8Orv0ny!;xz^vk>A8*@;`$*|0Uz|6k{ zr0EhEws-*`lC{YA%t{HEHaG|ySi*Byg|C54?2}E;lFY(<2*gxOy9|KsDhFC<24c__ z-lR*rG|t6zGkH2jo8W-|inj|`1#QW6%pi=da@zzGN03Hu0uLV3?JOp2vo6h6|XN`OM%;Qy$O z%g6qN$N!Aa7m&D+7zrzLxs~idba94>;fb&q3jqC>&pInV!UJ@eo7})q4P7g-s06O? zP{KJm0n8kjkO^p@2}26eWh{yot%;7CPl~Wl2Mti9Fouy30fy<(`n(7sB~peFIEUcT zV93xH*ifI#Po4NICq)V4DFRsd1~0i6SIB@UI03sfh%N0>D@iaXAcIiI%!Zf)=embd zsEIEP(=lbtGUZY)sgyVUhd6}}Gp&-9P{=iHQI&IR90~G*vrY z&hu-|_2Yog0|Y>@1S6n{7hnS9dx7eFf!W(Ib@RnpRK-JRH``3t%LsE$bS2RZZ&`AD4lPz!QAA^)g}07);KxDXlp4LG9@iRsUM zeAoXVk2%S>ENzLHbifl36H@rd2nkmZ7+1$2ShV`1?TQCxScEW>r-Mz{G{6*wb%qO) zNzY;rt9Xyjc-ZL((TCYhfqjT_jTG&OF+_pa9smbXsEN*ES?tgbnWb5A4Z?sGh55kP zOfe1}_)^}mImhsUV~7Ty+Z#EdJ4AqokV)F5ttNH}0C`ah6Sx64aQ_1}c!H)yhF^Hv zs1=B*Wm<#S+OGZD%`n?eH98Z(+R;kJgKFEe(<8V|1)m^|No9$;6`rEmo45sz6A;`0 zNL#N3+wyv$sFjzF0J2dS2O*G?hge)r3|q>DTAu^BZmo-a6N5`I0zmlO7Fw`movjZe zw{{x>vY6HzxCB5Tq34KJ*&BpHfPqm2w_+WH(F*|1W4O&NtIn8P93_i~5fd^o6Eyja z`B_l^?AMxgioOddPtic%P*8n!UX**7f5o^8ebMOirg1?JQLtxXR4f*keAe)&=i^u5FVltE@2Ng zhzac@f{5WXGl&#km-bYg8xAHN#=6#&RKgoAAAStdbWLOVh}4Xnw_^wz<|7vt;it0! zXut)gvQHrT1_^pRDSkXq3|_i8RzM)e7wA<>00Ibj1Th$b2xx+KLp@)8h+fr}4=V%@ z1B5})#hTE8@szzs06ouB1wgHlo;7e8*e%0jvke9G%xT0-|3nANw!2eJlOi}s;;KXoc&9w>g7*Z01 zQd#Cu1cu4XH7tJdmv~ID%D<#d_kNrGf}#ZZzc6iGHZh=5XRhQ|yR;AZYv z^YRl6P7G#d&oJ1BMwo(Ji-Ns*Hb?uj2RY|-wt{umfIR>lImiTeXg2YYwrDeFbXI2~ zTDyOiXL`2hz{%&G8@q3X7kCEfd47R#fK2E zfq}(`&@f_sw|HA!(!*9TRE68I)<;mcH;#pEUH{}T{w#(j-T+7xX>6OfiJKBl*UOm? z&EXu+0i9DO9Mj?IhwvTVy;lv+oXzQ+&uJLZfmgoqn|mIdhA^CSe1uLdka$1^!C5(! z3`>=y<#;^o#2%azI0IHt11-SDi4eH2Ov#D_=8xwJZYkA57#8&K+-Q;&X z9KjK5w}BhE(E(bPMl%q$L;IZACTrS0$=qh^c*IxEZtSrXXMr1R*xtsWaIt@g+5~$o z_FBf~j@lk{puf65_`xrgpl&X73+8sQ>V~iXkp|>biQ%23?zV37&Th2JAiI-_X>hS^ zINe}^1K)3{kD^@a|g+cb@t=0@kdE^p5c(e+HzBv)n*utZ1X`d}b07%POYYDFa)^fr5(*H)AA>TK z;@xCJl7T6&av?y00=$lLK=8!4@@mL(uX*u@pn*`Jas{dh3bG*m`f@6dAl9yBCx0L- zUq1W6AN_f3;edl?kcWHF25*9KK%a5kDfB~6^lzFu4NmhluZiOpxCZKTApZ^aGo+0p zk~&hP?@1+0eVO^lI`&*t+5n{UTmRI%_@#QmI#hDo^CM6+zjZsZGEaYwTnAp?_$G`C zb{Lor0wwmuk@S@)fB-0fluP!|L(m6z^~&lDUYC|bQe=4wHb-KKOY$+S!S)FvoINUb zKf0*kI3!f}_1-}DTJQCZ+9TiKBbiVTWA636@tRZ@V{>1>bw_h*XTRyg_a!+bdzU_b z4=H$BsW?)2hmyS0E~ll)b|-mwo02AXpe7^aCPl|8iBcwJa;Ah7mKd7C#J`*vUt*b>(yYWxcrr3 z{S}X3%)fTd=gB2;{Mpa>qjikecO%DVxflU`n~=GzvZLZ(pX8_b`<45WpY(2?57UVR z{USI}6E#x9{Wn@UkYgz4H`>_$eyujC^0y1|M<$c&UydVx=KuZJ&kgmro*aC^f`|bK z{2vLt+Ef6De*y)L)BiCen80^BUIYlB;Z8Ge{L@?^@DHBopO z)~?*FBms!HBX#ja6@GmJP_>Fx>%|n16xM8*@z_0VoESC?AkQ8)OD6*O5Y|o{tz+d@ z1uc6utI&uOD+c(GY9K+70l@YvM7J*H%ty=xl6({nT)CGs*S(pOBTttF1IVQLP;pVx zn0@|Z+we6}#cTKyjCZ`_Jws#qEyb%@L3HBhZ=CZ$w!|(_B8|` zVs5Ra--1;grqyK8O;_D@kZFfdZ4kbv8&Vv3JmH@)s3N<&{?g80w&+MjqY(^TG*X@Z*UVV+``dWJX*8 zjx1D2(ZmZKI59;h?*OqEMwQT`ha92w6UhrDm=Vt{Zm@xjF*-Cg5(;?q!%7^INTr%Z zE$Bj%2{k-Zj2>#N(Z&g8qN(NyL8ag(oO04>C!Tup>F1w?3EGgMEHUXMlv2Lnf-cn5 zUe6x5xBW)cr6Cy-DV8JZAlLJ#Up;zvK8_<>IvtLEzJrffyGZGN@8{dKZO7Xs<6ZsD4U~@N=m5*qimzY35wE`ZKvGsR_U2(uIU!1 z+tNuMzsVBYP`r^&dIGy~3|nlVgBE(|OXKNFB@y0SlZYE~G}7glVUAg5z?e1^@x&)& zYe*cPwrK8T9e*71$Sk}2a?IRTd1aPc0(|MfWC==Ws&(Sp>XZRAJny@i{fwoSTw2RC zUA=O5wXPg{46?{1e|xf}0`FL`%%JAEXP@P%u!b6ADB?~jDVLZyGqlf(&lpwkAaL1l`AHKL3(*{0m3 zE`$j-Ak)hW2RVc9#!fZmdY?Lopi4s`WNbnUG&&89D18mPWS7fiyYUPwaNO%5|dEaDMw=nMv47D>a=@XA+dNnrqf8O&i8 zGhZ}1U0s+J0A|jJCaip=E2(t?A}sR+S;$8nOmI$g#xhg9tjP&-01mHUE)4lNN<;s~ zQ>A>Qfe8E4Um$qEnH;q8mDEy!FM&xHQkv%o`$&T$rM3ifOfaAO6qi5KFh&g$?=q5n z2}t-B(O@d+o&;lyHZ!`!fnt<=t`MX?|2CPBDzk3obRrO=Ns}79ZlnRA=xy@oQJvB> zb>jg+E}Wstx)8M&Bw8v+3F_2rMpcxvtmQ2)wlr0QETXAC zi@8&R>cFP5q3B#;nTE15#5q%~i$23dh1EvEkS9Pv7eL{SaI}FWx*Q1}0lWW@nD)RO zrejSoSb|IwWVR+P2mnhYD?AzoC7iic-V%B1+lGiBxJ)65Y#6e!)UKAbtotQ3WRMDK z_^BbP^)B+P+1yfm#h>>PqK1UFtCmNm-~9@V3}HWv-eRjW(;g$4R4IWFL2#2T8Px=(nZ4qt%H7nnL%@bxJ4#* zZ;G8E$``;^wzL&jgiB0J?NaZ)-sKDNAV}lb&bAE&{uX{+o8yM~ma9Khg6;R?zR_KB*9=yOI+t|n+-s*|b?A{l{b9gr;4Mm&W+T#BTHKR&82zkH4 zrjZb}PZzN%b{oUXnUOS&@GKcoe&wBZPzRlwOWu&MnpcEQf>Q$EjG*W+Gb-rB=K4pH`36GO z;@V4$>Ec<^>_7}dfS?3{BfFA3u{;>A3koXD#>s}-&H?DOp{v|&gjM+0n{#t!ru|I( z$qv;t$@b=6urcxCZ)VHJ47jBoJ!)rCP1%&_X&qDV+3b7WP^|ENH8?6k-&)_ZlXt3v zG3&_y=h~|5vq2=j>H=xt)^#y9#e+jcHj?Jvb;;f*})%Fu8RNX20fpgX}CSfK_jMCo~$3tl+j^oSEY zCkydFg&ZF7g6K*#1%I#@A*6y#h?&71ywC&N8nT3Y@Iq8tX6~Qd`Sq}meWhbRJE79f zs8RvP?QrK)07{^AraPTfHVB44#)0d-EM5;y2VHynVG6znAXpM}S3~rHw06XTS^ERV<1QO!uEKyt8nB2Is@r$_r6b9NqPsn*vUR~%xp^Z zg(5w}f@~hbvEA@&qte{#e*FlN<7S-qX4bwx5jGkzc2_znyImtNi7G1kjyO)JZAV_Im`2k?#wIA4s-RtE-_-$R+ z?OpQuo!OzC*1}IYOg4%G_%mp+6+y5|U#T7UK)-03TK(S&Skh zM&Fwm9}4iJKSszR4k2bdVkEK%ASPsmyaGLbV=6kKI8I?e(qZv!VHbWOdC&kTAV@c? zf+Ovt6ZWGLvQIB&<1datB_!iA0uD~D2>busV>-^D)Py4|UZg~R#x(XJ8Y0~;noxF) z22&E===XZEF%!~iAW!xwm)32CNDIp$=x z2xa0TVVWjqKIT$p#%?xcRPv>0a^_(QXIffjW4fhQDv4^=W@EkpNML4NNT<}$CUchN zyX+=$nkH{{CwZ(L8$?Q+IFNVJjH&;HrKz1jc&fp8K2Uk0=U%?&R=p>E)~9{m=Wfg= ze(q;?27x4~!zxH7e-0>t7U*fIr+9_}c^YVbRv7a+D1%05eU{2m_<=Xv1v4CiVooSm zf?=S!jD=#ThH|K2)~9|-D2k?Ncb=$fwCxz(x{J? z$9Ynmjz(yF9@~)qD3T_rk}fHeHmQ>)DRMrklujv?R;iU5`Cvk@YEQ zpuia%0$r#nj$}p&2r8jUX`}xRs+g)Nn|9}$2H1Ws5pVV=W&|l|h(x2|r)j|_S^fYt z#GHhVz#4D?X_%^vwdqv3s%fe!t8%HV9x1J!)~J$43|!DZ+9!0_LOa-lG#mm*+$xHq z03F0bEC7cJoL#ZJORp9Y1Sx^A605P+XRc-jveu}z4r{RrXnjuWuNvsK+NY*ICa2Qp zx>ixUf}xU#1g6F5o+`<_{t00ofjoReTtz5Sy@0;*YY(0l!0v0m@)wqAX~7CC&G?nM z%}drOEpBa!g4`~OSFMnS*bc1O-lx=3?S96s zgubnWdhMnLEzxFD{2@NPpCZ|3?gkw{2@JVk$WV0rxJ zd0fbPct?*!Ze#yE#z6Q(FiEdbipWREh-nSTPV7aIh)WghLmfnoC=mgyIc0OohU}s* zP&EgQkZ(qUuk~i!=uW5jf`{$G$8P*DOTjO3v@S!GE?@}8D$vYtO0fK5Z-Y>8Tzzli zT8H@#i4Jst>i$Lmz>2I)1|Hx8&638Z050YZ?upd!;i89nEG{%O?m?ObVi0h7tYD#l zEJM6a+{{hgtkc)vjJP2IG!TPUI1VrXj<$G;(O68Ib#djR5Q6L#0hLbCBz$MG*TaB5#X%D6?lUz z#1;3Xj~%T}>y!{G4fAe{#L+U(M#B#Fu*M>VE{>H^8Xpu(2SV~(61(A$CxMdgjZ)U8 zG+_T3k^!~b^mw#mX)4iCR2a$8R5j92Ulk+~+!i?z{s^2OZDwv^YG#0t>`{~&3D6FW zN)M5(6%o->YY`V6oD_i()+TIcx(}xE^u|om-Ygdf4dzwb(F@4Ll(f*Mtj1IY^-Z5y zAsrIEeRW-oSh{x76M4}g{fkYTwVS;WVdGE#Ja#CG^IJ>X9%1!pIJ8$oG{QdUCSeli zEW!S6P%KawLMt>w*9B%{Q7=g}MO$=bXLMh)&=6PHi3JB@DHdb3QBh^arr3i&&;(tC z) zv$u`aW*^&E!IifqH*N>h38Z&eJ(hZ7)n=uEqfilPm^XVb_e9;aaF5n=|CBiIwt3`K zR;7V{MO9$mbW(B0csG(RC&@IeQeKJH3AjKo`~rCln!hO}DV-EDQZ5kIG3Doncy#k{ET*S0B_@kk< zc^FVK8L6N7i~xgVszp$F_+CR(BV{3pM_Hj$S)jMNlDzn!v)O3v51-q(WG|$xn|W)I z043CeCx~E}ak-bn8IaSqdwm-o(8Ck_0sdIIKwZpLAka+-515>bCvb7t;`E+LNyPDg3w<-M5p)|vJ8g+xBOP5j4 z72MAadPA68#fO`)KZBO->TaN1(0M#nE!=LvTtg(>%D0We^@-0tk-z`X49dH_%qg39 z58Yl!{Bcjt%_}v(?c6%{w_<-=!)0^H(}>N&H)>R{D3u>0xaP7 zWE8cfjU?MNGx6e z1fWwryo~8!|MB2QW4^XGmV;H_>X&i7lAqWK+eHu?5yM~$%HRNkU+i6={$=3?2Hq(< zR{nio4Q}218KC{SO(lTf>r;sdR^4P&zy0lA?}^gpyI*N0zu5n|{+3T*1tK60>VWDW zp4PWj_jf-0)w`&hxb6QP2hv3FX&>ubocXh!9E}S=^a(JK;6NhkHWgI3kRc9hq6(f^ za*vlAHpq;_TvHK4hQ$kQiKp(lEn6F4x3xj-kDh7AB!D!~&a(A+DgC(fQW zI#g7uHwjA@T_Q=EM5(jo%a}7a>ia3FT`Yoev+>(HkVPtg+gJwJNKd9Fp)cajt$TN5 z$&)Ep76pRBk2*^_(5hwYR)=V$AtQ&?aHkr%d)PP;qyhh`nm>EpR=pUh6QTz zeIf*!aExC{1phUxDl*jd& ze6md`xl~mM0})j4sDWM}f}C-t9Y_N@+%ZgB+HAv3w=9ue6UyY4Ywo$|z+6-^j*g-; ztP@T!;e{!2jcwDKXc7)reDCx3yn^mqYNTJo@|R%z77mzRg7C`^V&M*k*etL_)AwS9 zRaE%leGfhu;fPMq!G>pW(U>8M>5a_ek&#q+WtsP#S)rC=^B3owo0KqSn_EJdVvwo* z7}$bz?l(4^k2bjH$mlg0Wqn)5w_}b6lF0uZS*#8~gE{z#r0KDPzW3^~6@D4%r#GgV z?4y~k5azel#QNua(RLYXjNw)Jg&M5cgz7uNG8=Eacm6kDx9M)&@yEkrxM7JEig@6N z({v5;%}-!M4>jZng`Yq+FWvOhQBPfUvpfe~bkb4Rh}&8uOu-L#>RVm+-Ffdlb5%}eAw%r?f>|y4RxvB5o`|rml%8%=_-+pxz&Nu|$ z61IpC{P*FHKXZ5^`e7P-mf=GF{rNv#@`3yJHhOU{fCV(5>ISGgitX)y4Rj#&2>3t= zPLP5XwBQ9Xm_ZG0kb@oc;0Hk%LJ|Ltkc1_~pbGeqoA1pgIo&G(8QS2u3~XT*-g{vT zQ|G*burOPw)1g9c_&flbP<20STJ(sRL?v2K1i46&_okP_Ard48onVKK==LGTt*(k! zl;RJygDK0U33CeRA{HknF%$d%8qS~><&IcJ`^XV>PO?Qi>>&;H%_|H4@FTY7D3N}M zjUkd7!5Z|JMgWd605b@NAB_U6Lng8ylZ4w62KhKa64ES6xMLpoNH$A8j*V_a*d{fJ zNlv1!ltvQ^Djf$)f{gNeEzrdiyB9?tPO&8ckU=Qim__<6uU@|lrvHo>Mip6(mkU8+ z)_e)fdO@s=FF1lcz5#?67|i1ees)B{IcH5C{xmq)T~e@6I#Rn#Pfr$|R#V zooPgV>Xdn#DghIoP%wQ8^_x1}=jQHXClgGQkMwwg4t_P&BM7LF&daAFDJMswK*Bwk zs%V{5iYbz4MUz`y%s@0{(xserY&${fo!r`1wu1DiXiU>u-zvborqn6R^8g|4*QcGH zpa_@v(H+2J4{TUMQZE0n1v&g7js?|(INmtUER;h(HY($Imc^`QGfJjr_y$5jmi^*S=vP(=imxk`JW9kAC!rQka>w zEYO1s|KkBVzCbwv6WG8T?(iR{)ZWc1cNoom?wH8x5rWW{!U1sci^rjZA9py&0pKxm z3v%KwKu*a78S?)>KFkssXLQBBDX@<@4CEMJR=nD5uy-lj$VdL8tVu>Olg~`W0b98a z1DWy*8vJ14iuu1wD{neLOOPf%ZqHbL@jr@u;S3K%28^h&Z=M@xe?T}*D8NHM27{uJ z?qCbTxJNXS5Esgj!7`*6hzi!AhA}9@9i?aq5xnskIW*#39YF##h+$t)i<;D?rphfW zf)UpitL%fdSyI82b?ekfkrAnCkO*SJfVd#3?ix4rOHo&GL*>}A`YP#BuffD z?rewU<5uzX35;+BaL9rRQfR`tTVe*Q>HvB&7({Yga`B9BJgG33In8a(l6cp8AlvTt zw497m=EDL?o~Vm|YlHv#KM-}+9hPHuwuyuytmyIHc1^d=011pQ{Z#WAjNj<+Q3 zUk||2=RKKrclzNGpZLhm{;!var)<=9;(HgJQ7>9fv$BRf?K-n1VLcLG|q90He{gHI?#a!MuA&}u=E9uxuWeO zvyuOGT)`W%$ms5!_yR{d1FN=d>79ZgiE6}NfBC(fQU`K`e5BzBgT#S3>M#OPnG6}d zQ2s7@G1U(IVIE2NTU2;1TSxVxCF^g0xsmw8*1taXv&VNwZZG%BE&zDX_pS=S@NM{t zFQX!c_m;2uR&W05kNw=Q^`wvbBt?$ANsF=u576N)%nd)5Pn$fzijqM1fWQP!&-?-a z{nU^BkYp)F@C8Z`x9TOI&h0*Ig2e2Dn)XX#PQU|WNC&~|qrZ#sol76wNUfXR#KO&l;~08kJ@Ta?uk9 zqT!m!Yo3r^f&docff*J-o2=^uasV7iVV=TZ`?LcDa={s@u^F8aAA0Fd7SjLIf^PeG z4hPF9VopHkuBv$oMj#2&AQ2L-9%6{z@fDSVEbxyYq^`{D@gk#f5Ea6pZW0^i>TxRS zCF8O6Qt}{UBPh!e7PUwm8SDvr2PF&ACp%r40pqN|E=1z=enDfHEfn-&D8Rxl@lr2e zpa>3W zVn(wxO;ZS-sUN4vEin)r;bp4mk}y3XFFAuRSpq-)GB6(oH+54a5L5p#O@J{Sb2VA> zG|vMvB{Kt^%816Q%xD4yx&RcsAspJ^+|tq^pCckeLQ+VxHNzq;zw$ZG10*qrVRmq$ z7^yn1Gds7FC5`jb?lLzGQwb$!Ir0EDQLQ7M0Ss4|}c9eRERm_wz01QzH zUyE9|h$q~E7xdsFDlsC2qXdco6mTmr2BR?UPA=#oK|NzHLIYk%!!+Q6UN@sE{{YhB zfDj}tEJVN-iUHiHF`Uw|AQIIcZ~+An!5?rTOCu>M&LaOU)}k#y&{cIXH8z$tpfsHN zg%>;(#y=BQxriGx9>!9--Anqcl##W&c$p0`?~e)-geLHQwT26P6};(fVS_ zCU8P0PT&k!K@GIv+R~3?Iqxbg6)ZFsXst#hr*Z2+%w<{9#W~t+c-do5Or&7NA_v!# zmuvzb42%a5;{QTZDTu{g^wW8^_d32d2ltC_epgzAmt2UqRL%u7nFD#z1AT+T1ejN` zOyGMH!f40VXr~BIs^mY2#7H_W#BwE0rWL)oqkh{3d>gf32AD~@wrE9l=YmQH|5r$W zuuC)qOVq^DtbpbGqfA3ln1DoS~Oq(+KF zNX!C-uLORlwS0p&Yx3k$5(b84xLLENf{!EsY#2qMW{8XTRK((iV|a!Ih=*TANeCER zPgGp5#${Z|JQgBj>ZXhn2Wh$n4ueK&QfB{|7_O@{wI4U7X@{Ns4H-z4U8#;SB?c$~AHJYRRIC*YLcRJ^DLT7ZA=xClvJ5o7ys@Zk0d6$4ne#Tjo z+NlkUfTobAocAaC)L|8N&4H%5Y1|o}L#UqPIh$!#dBlf&v|v7fu>qTXemuvNm95vlK45R zK`Ec zyCrrY3XtFon?@oWy2IQ$bqZT7=xSxINu*lBvHwnaAd0cY7AP_MZ464Vo94462(}w1 zv_*)vO$WCJn;=?rDF92a0xSQnvW7yll5D_wu&8BNTN_vesS&Wi3alUsh8a_OyCIf) zW%{amoYFv53ZMn|x+&@mbvsR}yMe}gY-~G(&iirJTc+R4TM^416iYG^3^(+p&l;!C zo-E36tU}+r%Ix9FTw}B<48OA`y-!LpTuIREo6ro+#vU+V&BF3}yw-#;AjO@gFOe`c^ zz6+MYX&kFx!L#t}C_uc;#6rj!46@|RtGgJ-8I8U%%)WzA_mGgc!otR>e9%Pv!4d5Q zO0dYLTG6H)NqHQ}0^t9>OU1}R%eR0_$Cu*EBrD9;oXgyc$lV+?1AxHqT*FiAJK}4; z(tH3*2=7cy10F5XG9q>>&(O&R^Xks#XmYy|o$!Wk?mk4{%5Lxq!UqCJu1{*O2*S}n z&)dKaNf&)(C9Uej?dmd5?UWAjXt4u~;ISiRhny1bGTpBj;?$we)eVl^VtwlZK-g7| zC4fEEeS6Zm&e5CQ>ynPyoDJF{A>4+2<(9(H1XdDSy(d8KDo#N1> z>4D@(&uysv*`h6Wi|yEwEpX<=*>^(SJ5MZ@UCc*e8z3+v^p5dLkLAK{n$Z% zE#9r2fb`DYICB5t$=%^=kJAs2*5)nX(3Rj`UF`Ik_fBu%QZE97!5K)<1SKV6!liuKtyZ@C=9E2Ia=-@9+imu=W%nlhb4*{QM)Lvf- zP)_Ndc3p(;ZK@LVehmjO^M5ZZZ+-z)#sO1);Lnf#q;TtrNGM?u63cq3y|N?2(s#}Z zD`P|U@nZiVqcXKBVI5f!SU`OfMBV56!MUwK3XbRY#ZfG0S1f3M66?_-ebVWDi7F$5 z_jSFjpi(8bNG6jE`~3kQoxUf}KN`s&Sh|uS&olQkMZD?fM%5qv8KU|F!1m!E_W`2D zvtBO(7&Ley2&{O_76B=EFk*lcIlz%Z7$#qu6qViq2>>FOGl>#8dJG^!WPpknCuU6Z z!(5<0{(c?7$AzRs9M(h~q-YVNMjbj0)_~>;;=~Z0#u06y%h(H(I$nT)YYk+*h(@$v;(vuM?_b$gJbJ%3V_?me6K1rKng zPMrT}kxMGquVBT}y^J}t=FOT_P~zvwN*7&OE0!tQ+J*N*^i5noq=j{c{PwJ||v4T!YqVn?@b=MMYUu%2SCV z#NhLTx23zW)8vy z5?11YLmq+y>hxUzsa;qKYc?SBi&ppnCmeB?VTYG?6kg@b33R<^A&VK+VFNUV_#z2A zV6X!OV6gQRk&W8*$6!PrH1!c=QrQKgiA7=_A>CYL?9Bbx}p81rC;kfoRw^{J2~?Rt=*!XgB%gW3*zDz=&# z8_}%+kEpSo9hRuKq}=y)zU?8Cy>&(tWE#}J6EnOq>{`!LHHt! zu+ZwHu0j1misZ2ep@7IdNyr->los;qF1IL&Bx98J;`6c^!YXOeM(0*ZJ(FzNTM1^k!;2nlAY;r4PRGMa9GS$kTxK{t z6U`I(Zss6H{g@)hBQL0s^wM0Oa0V-CXpx6Ey%^^a3Vx_#iM2X8N2x&v`?Jm1(M>no z)md-dwYYRy{dCk*S6u=c_Y|W?9DB4u)ML4Xqet2&u&p%H%bt)$JgB4%5ngAr4!7KN z+l`sHR$Gp_=A5e+`0bZxzBy5j#|jzfqARX7*AJ8Ax$%$CrgDsx|E{~=2ssV4)Ko)` zb@HYjLC!UaxB+|IbJu-$QQR-C{J>XkJS-XwKv4~U*rOY(u+9$P&i$1R?KHz1rK1eDMY z7bTLaRUJeFbZ|!qF(WHlN#u!9oMIJ+SCpwBQHe~1K>$$T1!0gwiktWe64>YtWeDK~ zK>(re6G!3ah_^nJ}(X&o4 z3rS4W;iEM?P)9Cfb<9$pQ<}we+zUnkMImZ~6F?xr0%U}!Nzl_;v)et<0p$qO>1J4jBuG_9b043g9ac5lJLeol0n0Q4D_9R^kWZS3MlL7 z!~_Pp3$#q&7@2A&29&_Z7m8^#o$54^`Fg?rnCGB7{%xqto2H+v0}-1>1ge8<9kiG_ zRfJZNBv{2Nbf~%+u>MJ>#Nw7$bwXC9W)-Lb0IO7g;?)0|79^)#Wvf~3+9$fgwXK{< z>ull*sGJ^EGh&r1SLZrSyw>a?hixofO=Q%p{_O-hu)#BMp_IZ-1g4zrtkHrX33aSu zu%7Mg33d<#Bm}OstZl7pUklsV%67K2t?f%G6x$Q9K@Vz>Ln!zW2-^xbwnaF_A%+Xn zkjUd7@sJI2p9|gSN_V={t?qDf`&!!g!5i|h2Qv<#R_ls)yf1Z(jLu5l^s0Bg>}{`m z-}~3_!gs#(t*?FWi{Jd}cfb7YuYdmw-~bDFzyvO^f#Ex-LA^A=G(E6`qnm*(%;Lcm zuCRqUn_%@axWPQ!Fo%ar1VNHF#I<_v51KnOO9W10k!u3bO9zklWno){MGW`%=#^V-jqz1#l;_1gOQ~w_8*=!yO*n6CmLh zQnSTA>Y!9vNu1)Nqr}1+{&2cj9OW6`c*j3}f)QB33Sj65C$Jy|A*>wZ6LERNVSbqy z5F&`%aLdk99smyuE)~ka!pvQHf-6`9b8i>|L>dk8k!#UvVbF&sv_ObSeT<0pcFIw3&fBD>| zPHtrQ#80~|Db?Hc;P&pJ30|P!A9$lX9vHA z{I(`o_^zNS^#=v~L2-Xj<~2d|!*7Bk$Yl96RSNf?Z+>@3e^4!rMS@X>4MU`&=nY2y z`;%UO3Pvcfpg$RQ0zV)Q;B^8DW)MneeaCkK$%iN_FbYL?0xU2J)DQp?NAQ05GirU zXHJ2LW=Ir*NQj1r8FbhccPNBJ*j;$GgqK)ofngsIA%;IuO-7gwv+xm;XosY>i2vXe zpZJGs^ks`Cg-u9NZ0LrqF(bBtZ3U5vb6AIz7yx;=heG6sbCg=Apbe_9NsOjx>9~#Es4*#k2=wp;x`Y%vn2jIljUKs<8pC!Z2uN{vf&ox# z7cc=asB0`J5&tM_5D|BoF@l+qg9Xuec=Cfy(lM1l51|tZ{UDAUv6Q~$lrPW$PA~=U zb}CTGlv8;%w}3!YqlH;O4^Mys=wSn@k^x3QaPT$}MsNjGA_YyLKWpa|{|BD(Ypn4<=3&<3ZX0mT>qB>)EMfC_3*H$51b zZ%H0;c^88DnR5SWnV6}W-q3@Wp#p}9n2Ko}OGyTrz%{R_2eCPu(Nc``W0hEm5Jcb& z7bFML762r`nu!@AojI6Pxtj%7nP$*E0R)wXFmY3f7kufLoX0$KfR_)0oTFI~t9h8l zX%vmgTaYQ4lv$dYxtXEqnN$Fp-RYjBd70{on)D_S33h0kgab&C1@}M-ct8(1fuFEI z4I3E%E}#qlpbwkC93}t*JJ19$Fd+Ns3jO(?0m@PCt@0`x_5#*-kPHT*Ne82CAb>UyjOS290~ij1fCYB|LJ+~B5=v7P zYM}s{6AJ%P4z$n=#1JV;>Yq$Xr59>r>T`pcVS)!yYZ73B7;tNR=W7YjlRtS7eHQ`6 z6qJ#ejV}-be&CcF$Rr-}D<+X1BSH{(8UT8VPKLLoIxC?hfKYySx0M|R5x>_?(suMwx zsj3-@!V&8bB*phHm0+tnfUA+Bd!qVJOUDjRpaZKH5l&J?Wdl8=$0!exE4BI&e!7L3 zN?My51DVhc(e@FNih5r7E1w#w2r;S>^QxyRs|CWX-sY;N8mlxzjV4e8u<#8}uz<#r z0a5?3g%~<0@j9<4T9uLKq7yL(%XJT@p&&?culdRa3C6GVN`3?T4+X0f@G7tL+K&I4 z91Kc+v$91W*085y0vNlX{&yREN0I?E#u&$7> zOd0?`kPW=R2*-d9Tf`Ht&;j_$r1pxm0s4}hfu>Wo05N!z7BHr92Y7*}lWE$MHn@02 z(Rg0*59h!ML~)cJl9cliB_6XZlSz7Ns~C*o7@qn!F)WmBFfyyIQ^L50l#` zK5z`t@GN)XCG_Vk#d5sKt6IgCxSdm{PCLTOA2zz1S-@2VuO( zTSeq2BhyP1gK`A=XujZkb<&#(PN8Ykcd`O_zR63E-}iebz`iYTC^S_9s7f>Ao4v|Q zBGKY3_R=#lFbd-!1YW|xm_osw>ntL;rI?|n5J3VDFl&96lYF-TjwDk&Icr(VwJ_Dlg9#U+(vr)xH zGRD!9Qz)_@Kv^9$pbOjb#<{`9X_C7ZBdV0S#R7uF5uwFYq9K~`eKa7i8`EIcC&4cu zfy?K}-vG%vVJ}4&0L5AmIwi@QRLLfA$&j32p8R`CQOXmWVVgWG>esPf^vGJ#%KOKD zIqG5gOH-vB5#%>C)R#lHY<(0X$#R6s)GO}iOS z`+|rE0~0U-Roj9Ru>*)#nKTHL8_Ya8I7g!}Zy1xRAL6!di)tAhsA&JYGtba7DIpbf zaWDz9Fb#ug%tFuRn!bPzKYG6|(;V4r+&XSG{&CtQj!C z0ajZ91+fD?nQIiVciKD=FhF>QN6w}lD?=FoJYWsSxdaCEl~01hU_9KdlRB%DLB;(- zF%$@Iim%66kH*sslSFgh6XF-dw`o&QnFGL*VJ{ zob0n40~0&vLpw`HI1r%$e2|&5GZ)}3IxQr{h~zqGliu>Z-XQ)w@7>JXjyG1u%Y%OypISm z@%N`w;0Jt6CFk+h@JyvyA+K0%8Y>SYR58ySfQ8$S!$ne36^3J8%%oK|bw`d-S(nA8 z=Y>{5?}A45R9fLzF4!kSrCCoO@*ofMJ4sYv;t^T_7eFQTN`JzM1@)5E)N*B0^nvr4 z!BpX8WK`eqYM)tk1@csX^_ziHi4XvokO(peU@ITze(zcfKT{+B_Zi>T&?WQMmH6wW zTfF64NOxi4^?BbG`4%Pz5g`Zm*(iC{2 z%Dy^12Rel+)kF|j)O5VyiSR?Je_{W_;`{P~Vuv!v?qRbeb17Q2Y$cYIidCywj3fvJ z@Fz|c5VL3ltZgfECd;lTl}5bjvt!JOk1sAoY`8LH%ZGbf&8)d1O3nuf5@ib7Gsnzy zPk$!8y0qz)s!O|`o4B&g)~;(_o{Uo`=8HguqBJVn_fFuQjSKY9X&eyF&7TX5L8zL) zZ8~flEa60r7ByXheM>^|#1ePB;)LQ?=mg{d=hLrme}ePI>esU?=N?9%CoKqR$Cdcd zQ;)quXhV&^$tgOC?JCzM~5cG8nS~snvusI zw(KiN13KJ^MYMu&fW{TK1QL@>6Hdqi8$(jr#v?6F&;SpzYBMZ^QPxS~1Q&8~Mu+!m z3jj|&`ScS|($r+rO&t#6(o2I@2q3!vSQv#G6G8|;&j9-T^F|y)6E#8$Hx%_g%!qt# zB+2NaY&JXLORmh9WD6Bl$C4{@)>L=x?Lrm}dlkdTD5LPy86V7b*;-gZq%TG3oa-HMCkd#gc`;?!Q_ua9vRdOt^KuGUx_|d!fJ72686XGRWWCym3HgERO?z?UqRqL@{PI#!nSJ7(AvTl)mlVp%2n|87x z#~H4UGkzlBh7!Tg(F{&1gEN6}wsL|nWG>|hIB zCXzymXlMV(_=P|;fCzZ(300H)#g;Is$wLOfg<}-RSB$Y08H{2aLfBg;18~Vq0&k1F zx*ieJ*ehhs2zlO9-}grJAzH5QR#dc8FOya)GS>2a6uK7E!UvjRC5x2MDr5ARxHUAk z?{mXs<46AI41w^mjwS#h^9<6z0Ep8pz7OkHR2Kn!^bUo zWANj=Njn{J+mu=q{R)wbw z90i8B4lJe31L#&db>*!)=Wb*-#J}KVsdM7ej-S-xYafClUl=%liFt}VT>YZ>k1Jm{ z{*aTm1gzS3YDGK0@;q~FSO(XVnJtt=vYFy?e!Zztk~H>SU@1H#3Om>nC z2Owo{++i?*8rs8W9P$lAqMjyjT?G?4R49cM?+x;$xF5Z5-D7AHcmC3CU*X71QQY_F zFEe6%(ouS2wQO;w)2tq`?Zc;qV}`NbY!Y_)@GaPAI(^qC32;^OQ5U{RDZLYExPXQD z7>%R%F@wf_LR=}oMFe!9J)=w_)(R)>91;*P2B&*Hj(2&L)A1$4F}JNVM)(zPXOG^e zRb*};?YpprwCFOj-{$i%KhLtY#}0ys-%Xk{&^P##=IhYfFCEb7QJt2-EjYB79(vt< z7=Lv1E-mIwhx3napLl=Aj{~Xw`*$D5$6qs>$2LHCRVF)ZF8n?C-^aX4`q5T;a`^g| z!@I=@nJ16p0|UGr-Vgoz-rKStXs1uKzVlYLe>OfS{DH%V&t8#(AN~eKy>!_4))YB( z5`QP|lf%dTFLF<7Uy&FeJ8VAt@{8bY9*pN#SrBs$OsUB9(DI)_a@&p`T5E=gwVmCc z5NPYE$~+BtsHZ=ltI&4MSQM2~*FnV*KK0RPF-4wrSJ|p+sHY5i5_>80;Eo)`-16ogS)nI^> zgukQ}H4sHK<`)cwWr5T{)_OG#VjLc{4xcg@C`3^*ktxTxqLi0_<|#OAOtTqW}_K&g-kz8%RPNRn=cG=wSSXnMRPYbQH*`9gXpi#w-#XD9V;rmuM&tu!C9 zyRlY7TQmRBDh-GCJMEje)h5&}5;|z@AO^OIBJCkC)(t@?IhOKC=iJ&IJ&L*%v5HZQ z*Ca|Mv9-ljlSP4tMR#Br=BOmMd9bW4Ue2c`P=+z%KXzzxZytneOgsgJRCt4vL?rMF zK>u$J>SkJq$jdN8UB-DLQEBY{{LM2-#mVS~F9~oyS5q()fQ|41 z?4nZDlU)tbu1|OY2I&7e0B{9kK$BQl2lU_jgnMOz?K8~w;c}a;k`Ce3M;3fuxK0}3 z)&`t5jmWqq%z%^DAb}-?m=#)^k0Bld)JbDmYzH70{fwqM*TfLR1RSex0iRz+1|bz| z=tJV=13AEOf=E*xB~mIv5_LMMrXW1TYvmSifYbtmj|Zq|G?LqQQk@PGWezc!=dIHP z0$3DQjgTzk!tZGN3?GG^UZNag?io?67_(yyVa=ZrNnwH#djm4|Z7UwTBp<&g#g<^5 zE)t2|W^)B+<&e2D!{Ehr{e7k}JQT}|ctwlB@Dj9Wqx4B9v0oM`&v;C}g##cRSfL!i z&u19+zE=Q@8p+ISE=BBAcza`>r0lk2#b<3B2mxfJ4LcN62bTA(%bbK591Kuww{rDY z2!?dCthAaMJtPqMq)^*pC{%RFkOKhJ(EHJ$>jpBj0UkqyzNFK3=eh!xr6Vg?fJ|8< z3!B4n*;)ON+1O@P>Th(eC#@CnR4)8KodW&&+8P zbKfK?07V;{!bsD=Zai?M6xvB3$UjYs`Q`Ji48bRFgc@gQectd_4l5@T@h6(|RzfRg zRPh)r*cLQsfH4rk(3oPrXySy>fI4!_Z{;O}f+a`}0J;i9Oq$T90ILG%t}9a7*7a*f z#tj!sZhMqZzwe<|;qv>f{}yM!hm$Pl5)#H~|BIT2QgqEK%K#||T&@#WoLCZo(!2a2 zK=U+j_d<1dg?EDK1HEbUwiMzR;5D-;dYTg{kKeQ zI{=xUCIwH<;gF0%B`X4OPy`n|Wm1X>P4pl;b zG4wcM`<00Jm5V4~>Rp@B1gg`7X8;TUssPYAnWDHDBp1M3@T7o{mQYj7xdFc>Pf8uF*Oy>fYdAR3|cLa%4IJ#L4ob}tO*t#eiml>6Le4LuP8Wtdx+7DjIu>Mhl z(?4aqxY6@5FqE06O3K5=fva;=ONqeQuoW;dl~!Rm@}fPGCgqM0G~~@q!c|JIXU@)5 zK4X;Xm0u6G8R((Vc#WGUO%1J)tV|P`BvCd^Mt9WY3#?aFGTo8_Mqe@o%ccx^W*NK8 zS=zE*DH#uEcBP-bx4=PPPyoI{mP97?vgQGoctC>+pj!dU4$xlwKy#^y$s*Fn!Cw=j z01yRCwv`Ff!K_)~bORAL#Hq)!z_dDE#w(`fWn$k%RF6ojOj);;1rx!iocFefB!dMd zrKb#4BLcArpnn8tUj-5}Zjqt}=`M32mc(*%IhXsTN-_!oZ{n-uWd-)`SIAo_#C#|peuSj8_sn{nGh z+((dn7&f|24R)k$gYzSuyb0bOewWf5Vur#MGa0XY@@a9+Oq_Y5& z>#fVOVPU2$6xs&_9}I%w&@(MHs060?#8#kaTSpLZA&p^C0HI|+l+T(#qG|#32HzD~ z3f)@vj(AIzxb+5?$(`ISR0eBut(-jPw_rv2Z!#)l0o7n7HzEhR)pvFEx6%cMD*bPF z5UGpXX{)@w9ncSU-K4%1j{GhqPF60)+S86hTkNcQd<8wH!+?eRNdl3Hb?K?JD4=}X zo6yyc)$cfi=OjU^5G-Fw1 z*Q2f@4s(07^BCKo$#xsMwj1q$gZ*+tC-qy1pDHGh`G;Va*Sg~vX=e*kfc}cCQ~KUu zS>^Wicau4R4RWV(PEsNHf8I8{(;i(tM6(|)oqBo-mU}BEC{Oh3t*pipHk`;TWW(5= zVWhaf)ey%}{aJnA&D{hJ&rP2=!1qg$$K}fX>6RWvy@-~k9wv}F@E4aK7CA6e9dT?v zO!3euR!pZ-EI({b?@q4q&G6aA9RLsl7zA4;B?fTwl@=Dp*6pN#P#C%>bglp;odJcX z`i4{|!_i&SqSH=23)9D{J~j#8#l7;+*SDpY z1Gx#MP`U81K{*K==jmaw9U+T7b+Y`~zt8@2nkY|jfTKUfuyx;=cJJ)--WfUAh^oA7 zE(s^6;zL>(e3IymaesJuz$@e4xd_>rh`?7(f!5t8E5uLxCCTdcpGs#I!JsK62>XH-XNNG$jNF^(4P@^=-FXEh);js`&#DJ znoUs1*GK_P)4vMYfS;w}xe)F8$UoijeE3*z5=C z11;|l>#TCVVba7u1IL%i+uJ+1bCU0rs9$)cB#_@UO5(MUT7Ngb?oWB~&*O~Yqy=+l zY>{UfePRXzMt)6P|BSu*dgV&^)+`VkMNEafqJ!&UO;*4X}IJ z{U#UlmPg~Q<-wu#^jkEIw_6=DUuT^uT}W|BB%?acCV#fxRNdmgf1}+x0w&){26y&o zt3Ni_*Sa0PuN=-{9sb>zkUbgdX8s(+lPB|$OXNJrcI}HWW7w&e?^@8eL~a>5n;-95 z4R9C~OeO?pszbhnq7(sAv1XE3u((xwR3pBtBA^~$3)l{Gg-lY|;_|Nl{Y#F-%-OW& zu~^dNDerj^)Ppt?7THoB3#4?MCMzlJ$&ZoA(v9lMV;RGLG74x457QV#QAMkb#;Eb@ zzfAXi{F5^8t73tdvPT4>V3-3)#=n5Qmux%|GWrBF>VJp$4ivx(tVU~Tpfb{YB8R=? zqnODxh1a7F=d*ZZp>g7e*bVo@pw6=1!u?^%w0rK>75;_4oTr;4ZA7eg_SjY4{=R*y zvz)`zwP%_{LdW9@H5@?dN}u8_i}ktM$w?w#lTB7#1qvf@+@>{*r#eArMK}qND7yQu zO7VYedD~DVv><-1)$|~Fym8AR7);9U)WQ+_GA7(<2`yX0egsOXsIVsqCSHEpS$K6TL;&@F`& zPk)*KT*N+R)D10|3@68Q-Ul8c(!#Wh?=fAK-JVC_trGdZg4viq-u9`zRh6n^z$9v4 za{r((+2`@v-~H@=TTfvQ^4P(d>7nE4M(|9-?U=5;oiuAcR*7UKD84`$^hX0aG){(w zVrrokT$QBYR~*kIv7<5AL85~9#Xc7#tQe>ScZoDoTqWGCcvp+Ur#-wx!F5Cnlg1!! zT(Qbk+{q{?^axxxIx3LJp-GHvkszefP8mn-29+*91l#d{vKjkzia?dpOV5(c} zYxD86XSI`pX?LsT+G`0Zh|Raz3o?IEUKQ7wVB}EaW0gPM8ct=D*D>rr-5XEi*DPxL zE$;fJN>tOhvS0jguHNJY@d@+KgUKqJ`U%H1DVOC`NfCgO8y13~<+1evj6zOH=M*g= zl;0(ho2A05;(Rf?@-6Gw5Ai4E=dkgai0C(uwyry~l?GUz&eY7PB|y%Ba;PMJAN z(Stl|ZOQnOB*Uz`Uhe`EL-u*SS`(+(N^5{cPNn6E#7%YsQxRT)uAxm$rDu{&`z5{O zP5hBTX%Q?z`G-@y)k}8r)G#Wo8t`3&f&}$l3CJlZGX+QF80mZN)m+k1xrq~xOIRG2 z4NqtQ@m@u_X)_Ky`&1qm3|!8O7kBIX7<;QP%o4x_aVIZF(A!eu8rN-Krc&}wRJOJa zkUrFv1yWPv@9ti>vWfJ|xLNnUlesptdhrag(*5Z9^40f!A60 ztq7cB`c%H=JL5f`m=*BY_kAtjr!fI4=7Zv@8>E+xvI=sTY{IC9HEcL@qWd*pfs1;B zEB#sO3);wxZ&Mwqn+8&I9dLcA_XkJ~u{p~I;n7U>1AT8cBp%{j}a23N|J(2*B7_U z)^JA8O|^|qCJ01Mefmr8G`8rKRZti% zGvPnkZOoJ_FEOgd4Zql>xAkwH%6be7-$hRcx`pFtN;ugCX0%k$oVia_zDf$+pc5DA zG1&*;#5^Be5kr6$Crt>S>p`-h7NUqM>6DsY5v16j3zbg^PNuG?>EXhuQ|O;5nf5@* zJrN|#?<9zTQMykKeq6VtRAA}-<~7)#Eu?qEh2Z(;-KZLQmAhScas87n+GGC_3`Zye zP)UV2L^3BsCl`RuWwyfm*`yDiS=e(6|^ zm=~PYegxCxV2%!4%yc!?=ZiGihAWWw>6$+-@YMm8`s!0Y)Kds8dkDt_4?|R=CfL9z zu&L!J$IpWaAtBq`&;O^imk`Xxvulg|m}OsUl6L$3Nv+(n|B2i;7 zB&k)YRBaNuC}OeW)DlZE9g)1s%+g*+0cGjWc~OH*o|oV#in2T^UqRv#p6HwfBds;^Nd*;$nw30STO;|ft~!y zG8(It&RRksb(X-Nly9EKh`iu(2*3*mY>~O!j=Z0CO;SGIqQcvH{V~FFJmaK_mDqvL z58VOE|K21?R+y0bNs+O|>Wre?lVf^Yjp} zrsAtxRgViE2J8vDD^A;QNEHtr?}_6TXWZDOOQ!?&rRx=E$%D=lvFk2!(`h5RMcud$ z&%Bf#D!z`{kgkpn_#{6XJ&a7KBw}V&=#kY{|d{*80*#~_;GQapH z|F=T1LP~>0sV?$ePy8LnS9=l6ucb!A9Ic!V4`QsAmw%jPx3qQIc(2U%wIdwV4AmSq zy3DwvFrl`fmIG73D$IwVX7GOqkgE9L8j6gR*i%Zs=ZaY3ikd{@4#}r{QNz@`XB7CQ zUoO3(zxuc8Plz5m6nUk|C+yt>)4gtZ|B>?P#DP(uN%dv-z(gePom*WfaK+g4W?jU#A2V$PJ zZ$Bj_U~%^^3+9Qq?#2%9(BGk&u&|fopS;>!B7jr!;+dQmHWVr2dSOu4@Ld_4%0X5AEF6`QN#R-ce!P#Kkdp%YOZGk_gATa!0g>!d^eo`_ZV-?I;Y6Nu`bq1Q_ZJVVta_i8>a z5bIrO5MKdh4aaro@$5NRzNH+$ADdKfO?76_THzR4Ltr0JeXjyQ9fhZNHRCpgz}m3P z8Cb49a90I}+7YNQ38F`<(>7GoNVx_TE|4$7jpp&3W9kCmHF?NQU45VreTEU_Ak%10 zM4Bt@dIG7VD?<<`>XxSD#R_u|*7n#Hk+o%kPK6z3!7q{UJdnG3E!`_1ebW}x@;uph zjFKUa<2#=J0;t+BOi97X%RE8NMS*Ar3pJ~=0l@|$ZXCqdC|Dnt$g6GE0jr89_9T2R z5_2`-Fh}PfT&uo$#IH8N8ZH8}_Fx8tuVVd6q`RHn8%Np2h8&mIhNQ&MSqg#jt7aq_ zD0|T*k2oTaWE-QnG)yJ}RP{)-JV?~TB%qrix7Qs9U7*GXV>6{t^BQduHa+tm3TK%_ z^;09urFxj446^Q82xw82 zXmT|37?f!ucVDV(*Z(mZBR?Ktt3;7psed6r2_+b#pCE$+9+Q#O<+XC6I@ov$q9n?u z`;@KK>CMN+;zd^oSPAKA+i3U0$t8q&>;nr0&2@eBmW_}$uQdJ6j7&Z$rEUnTZm3A| zA4@gi(Z+gR;^)sRS#6@e#t!2CR?j5_KhKagtIpv9ZfIy|ZAZDcMp zPy-~ISB^Z(G(9WMC^Cg82MWtAAX~V_H>>~=XV)7+xfN!>ixRMA}Re8QGwfE|%sgp>n7a??(V&pEDVJ`aC&6|qEj`mNIypdzU zq@NN^r#;=K@B0~;U+`g*$TYdY{mAP?f~l#8-}sy1>HBnVp3IQl-`w*g*M5F%@Zu_6 zz!8FvFhg-_XP~Ne#SD8L$=ou@M#s{h$e<1O3Htkdh77xy{dyP@jW8e1(!s(N4zzX; zvd){c?`;x~Fu(a=#V=X&yXN&Ur)Ol0x02iIe1<*#(hpK@H2Ky$OYw$xg?#PbG{O+G zuMJV+vyCnphoVA8gAgiN1}a~{dN=z~-s1zh&0!T4?Dj=g6&L0062eY1g?WPUo@16q zL(!&b!fF&{H)f(Hi&EWEOT)T(z_7+%g68wm+~YWkt#IREsLp4@v6J1oFG~fmi>y0z zN6`|A8zlgZW+Q`tS{ZMejV`XUB-M>|$RV=xj#{kN=HuG!BYgFenTqV2V@#e+POLLM zGfptaA1X8XPQKNS#g{z(h4zdo@t+i(H;aNQdxZR_L|o!Jvi}gWuTmWUYU!5fX#Hz& zyr!ciXRep5BGByOyG$X}-!(Z|7r1#Bo@?aYyw$Xdxg^*eXeEH&b({McfFmS#0BTNr zNS%qwg$8mlFF;rh(VuK5U}Foc%L{DL3&=xt&K49Pzzm<}Gq_7lGV2fEdBb%#;RWA9 zFXu76Spa!UEyWWLoypdjBPW7f?nn`Uc^%I=G0YyPJ*go=LJ_!dG`zrfLe``K=(G-t z`4Co=?JO!xJRzJHas9BsuSK$|Y$dctVxUl3j&@~sIl_-I%oHU`Z(-mZUCbJ zlvZ-hVq@IAkYRJD4Pr1hD=uTHAzI+)O0UH`p=GtC-M^$0SgngKK<8Sj-?cDJ&9~?r z+Zib^{IO_iC?uYgXq;*RxLBg?A+u1lGV@$CBLXyos!Riypa=PC5P+4P6)(@1tzPTh zdzR=_EyI(=Pchs2=WRhp?OVp$Iub!Dwsg;KiavZ4l8q& zyx(opue6u_x_A_Sxn0!tC_W-krs&REkg%}p&L5qMw>W?2cNJP!WA1E>?yfIQ5!SJZ z{MIzVZHoSWHP>&oc{+kQlRQp}e5TkQe|al!mE$W}7HCHbVWa`LpTz1}-xg#f;x+-^ z9t%A4BavGHX%+<-of7k}R7jMOK28F26catRC3~$koyso+TY>%wCQ|Z`%;CB zL`55HPWjp@$Of4U)M+cYx;LgN zXqiK;g@v)C$ozVoNmQlMHu{y$oWVz0p6tUDV>Wu>&?mM36Z|V6&3{(PLIP-67PsU5 zrcDUbZpOgY=9QAy9XR5x*o(lTl~a%yglIX%q__Rx%xTT2@G%`yf%|jK#K~wfhQzhy^b^#|duVM11(T|X$GNvh=gTK{>lLBksy&V~G=U4CA_h98G zehmPw3%n}NsLc9kY?OS2|1Yvq?A_5{5ijJUK7VUHYSmVuyyXI9iyp1R-bq}*h}B%< z1>1le!5dIpEImeDEf~ar#=`qh5(0RJ9CeH~j`K{DL&5~h5rqq57bWZU_c!#XMK2b1 zkT;CTRj;izR>Inx8Xz*-2Qz_0fDgl%4?|9XT_^4rd3Px$Ug0!FIPFY>J-ecf4|#rwX#wDs;bmv9A!C%jfe)! zSGhI-SY_~XKWGE=Co60eFMdarEjnw7>KuCFPlz$-g7?~2cuZlrN#q4F8Wdqd??Qrt z`rwq|;2QB1l?;3~C;;Iz0EZBCt~8aez#(^TbE_n(kZAGTz)LH74UNw2FCExgNnkX;nTb=gj~P!PnNh#&m2taJ1s(9b{(P-5JrL+elF|E z=~Oqne|NmS)}C&glaC8#sX+y@WlarQ@he|^8v75;VG+hD04=Y3e*)5II)01 z?-bR;a@C{v4#06#$pLQo)cv(mGSdxzEkdLOHv4*olRdhaN9Vyniv zzlLB?LTj_BDKkDRbz#GLVF1{56N%9ztkm+ayRyq=ux)c5#UzDLWhJlz)#x;We*7`A zyjY*{PX?zAbH{~@lKu*Qz5Oz$&gev3Q~^Ks!sP_CL^pGisX zN(T=)!lhk#X>vUpBXs_*N`ac&qSx*^p}g!uGVnIrL*|b!P5$9hRAS=ZKd7m%CVOPq z3}m`Tx@OqTy}QPj5yi)JL$s9InS~HBg~K&q(dATXe=oQWpM8Jpev;;UHI>8~(Wfvx zLd=oTp-t}UI6qF#-(Z^Hh>70Pp*yAlB??!S1`)g+CGRvk#jN=}ibjusE9IDl4R)(RI3uktVm8o4 zp)d-6KmYX5c~Y_#ju86t@GaLj|N4x}b@JZVtpjw6h7aGMTiSJOQ6L0g4gptVXxfUi zRNM@2Da&#*GBmQ&4|?0)w@o2-Z6l*Z#_>D_pj$xOHL0tbCv(H306>qmliQ6afvn~z za&8W#Fwdzcj3~@_Da)tn7UH~rATu)qPMQ<5!2VmJ9|8wwk5(h|W+NQ(q5#Ql`R2^U z=019Bh3{l^geemoD}MJr-81ihK*!c~@t`)7o!#59DIsxHnU=u)P)gRW6)~nL!Eh>) z;meDw=&?TEq`H5aYyOaSoIibL|?r!+IZaC{*JcdqXdI(dA!i% zunr=6uCrmJBJzu3PR(OJCI5%XVgvHClULf(9M@-`IAV*Ux5dZ9$J0ncuS#Qs;tWYa zMNuVo2@>B<-(2L4w)g$`K-1K#hwbx?3U zZ|j?Pu8DNI8f+w63pwL&A^B2L3B|dRAO0YYuYWj70ByGSYl#C`l-21ayoy8|z4aLe zk>jBwtVu&P?zsRa17iR#u^%MNt5UmQnv@O59RnAf>MA@ZaHj_fvtQRa8;7DOrw(d} zhC56`1NJmI0cI)Z*MHJ<>?esDpNx?#q77jByCdwxnsrx-*>{n@?jY zE3iJC`D{U@1D?paniGG=9foc;eJKDPRuMP=2|S~wY%S2vj{9DJMW8`|&4mEE5;-N4 z4H<$Lwd~kct8#%)g8VMEG2Yg^6SMtu@}oN6~`EUariRCa8E@6XaTw}h$Ui0@wPbXMZzuJKB2uPUo_Xy zoT71K=dL7oQ##~(7n@2-!Vq`XbhwB0G*~~#t45BC7l>l#RPs)7VIRb$ctRKlH?XCV zZz-vF5@xS2tj+P>2=2RoG;H+(z?$lkr`7NRB+*x?;rl+)0EyFd?I*sVOnp=J1~LqZ zRBqOfwg!@2+mCVP`XUTX$B857B)6V-jP+TOnefQAXvWny(Tb?EC+m6Zt%T86gYK#- z-8$6I*&o{wC^SRI;<|FEs|n3|B&b`4F5f#-tBGZAHdn3IPZfvSn_bLq4OGH)4tn{C z0dcu8T-XfYtEPF`OcoqO^(cqhK3*?`KUf`bF9ZkA*k*~(UoRgRsi&P3q(GAroc}Pt zwzm`K#|ow@QTtXTu=?Hp*5>_LhHFlJZ`a;Ik4?mK#9&2-mY5Y~MI_aYx2Xhx=_EE< z7_K`?t1(9)1e8zjd^6B<-O=nl&PeXnO7L%CHV4^-^c*%01=C{&;{{RvmbgcM&3hB*o-BF8lPi}UuIA_c&DQFxwi+Xp)O63J0B?Kio7SiVVWD$kA|kpE~E zO3%oV2S}pG=80QL^zVZ47EzZy78fHJ4;SSkP@*v87Ee?`|GTNE74aVzu~#MK{m z0b}&4Oqg0`*!s>zg~U~}U0&)xa3qs_?yYuCURnXCIV0~ZwSk3T=Z;Js&iMr8l;LK; z?j(C7fp&GAk|Fo+4##q=>5zBAQxi^z^kHRV+0Fgk>)-9@_Hjw-cczh+S z>lfgH8-V%sd2`4@>gnSl+l;6B)XpInp^lY;v0wju3~Y+N@1uFfcdBv#elf~8Yxh(0>~648{fY)){t?(>Fki2K%}>av`k&`DJX2sv?H^q` zia$;N_@y549XkBs`ts7sae^qTE;(WbzjT8HS9*9o)5=SHni|Dp5;Z2GN(q|P1U(u7 z{eH~L^YG9439ra}dz6?kIULso%HosYR@i}r6NsyTj5YkO3KEe40w%9UI#uT+Q65CY z+#_%}pZx)uelibc-b>ZX5#1rbi6-{ndt|85^{RQKB1bsLi#Gxvc`j<5XYd5%0$W#S zkn({&Fcof{CuK1O#|PI+aD%Dm0pq){$8MmlU{ZN^5U(k?9|UTIg9C#p1IIxoC`h=w zDhe*llLOAhgJoSPFF0wgUQ=^WThPI2ICD*(@Ga0t??F~kR5wlu4NeWQ++sOCyqlY- zrHCDXR_oG1bDN9kiok{j68o4TqJ2LmnH5JY zAZ$8iuLvx=!|Wwn@Gc1^Z#VZZIlPW2#?S-iZZrF)1r}dwqPb1D&Kb+J8DD@|WN7^p zd$(PeI!G^JXU2^E(*kvBGwkq;ngY(@x(#{=q5ubTfX|;Cn@ zx%tSU|67Lr|9)%#&xu~c{(qY&;O2M_@&W;%*MP+T_w&)OKzG)Srz52O)+ambUuGjU z(^&Po8m96EELt6>x*Dg8CEQ;wJKuW*oWaKXo4=h2rSI&wxeIg`J4B6#=gHP)5*1njgQ0QBgb+oOtWK8+~piWi7< z$jq=+YbF;6y{;bePgWH>lQ))|;Q*rTw6MH{=DW|;iECnM`c83!vC0)d8=;MZDHbu^ zN9B*UxEe~tm~FX|#w;ysGu)-|73m(^XI9iI?YIV-%V3tQ}TrNfAx^w0wM zDfJ4S)k(RQK5G1?_|9P{WJ&yn{0TKupG%ASq zQ074}?sUt0MGN$82rhcHc#&|AJn$ns+hTH8L3paz2^^r}?5D*h0b1@@rHnuq8Usvv zibx#a8S*xw5KN1PPFm0;$= z!e`e?rVYVneuB*%v0*}+0$l@8Rmv^Y5RS=8$32nj#=j~SqC zn`n<{WNhHeJs}xCz%N#XoSr2qYc@%{wBM`J9+@_BgqOjC@T55AKh|A72cx7<1?I0g z3)z8&_09PilF1`<_UOV2Djdu9L{ik}Sy5_W)b>-$A4^z`jaYC*S!tFLkdeV!tJ9P` zsuA!*M1sCH9JT=zWdC8S1&6-Z8Hb@Wh; zf55V#JPSvkIxPf0eQv0Fce9o(F+TC5%k4LN=RQQ$eo7vQ%}~CV0^<+=q%ZOM)IX z3A0Jw_Zn}&Gc1#B*2DbD3l9O^Qjivpj0HJwiaOc$08d9($!uFSs!wn_GwB{5%5@C@9uuhE0k#!nnhfaMQ53D&tCP~6rIRyv z^c(W#6=cLbJGZj9dy4tJ{jQLW7fLtVSCg8jU)r}z-%+Y@yt|<^`%vV8EpRhAxxp

F4mitp0$mwdu`1a>yg`Lz1 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:$?bfrr^~woXb0tf&)RPO@5B!9jB}t&2 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 t?SobrQQg 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 diff --git a/docs/source/about/changelog.rst b/docs/source/about/changelog.rst deleted file mode 100644 index f739ce980..000000000 --- a/docs/source/about/changelog.rst +++ /dev/null @@ -1,1124 +0,0 @@ -Changelog -========= - -.. note:: - - The ReactPy team manages their short and long term plans with `GitHub Projects - `__. If you have questions about what - the team are working on, or have feedback on how issues should be prioritized, feel - free to :discussion-type:`open up a discussion `. - -All notable changes to this project will be recorded in this document. The style of -which is based on `Keep a Changelog `__. The versioning -scheme for the project adheres to `Semantic Versioning `__. For -more info, see the :ref:`Contributor Guide `. - - -.. INSTRUCTIONS FOR CHANGELOG CONTRIBUTORS -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. If you're adding a changelog entry, be sure to read the "Creating a Changelog Entry" -.. section of the documentation before doing so for instructions on how to adhere to the -.. "Keep a Changelog" style guide (https://keepachangelog.com). - -Unreleased ----------- - -No changes. - - -v1.0.0 ------- -:octicon:`milestone` *released on 2023-03-14* - -No changes. - - -v1.0.0-a6 ---------- -:octicon:`milestone` *released on 2023-02-23* - -**Fixed** - -- :pull:`936` - remaining issues from :pull:`934` - - -v1.0.0-a5 ---------- -:octicon:`milestone` *released on 2023-02-21* - -**Fixed** - -- :pull:`934` - minor issues with camelCase rewrite CLI utility - - -v1.0.0-a4 ---------- -:octicon:`milestone` *released on 2023-02-21* - -**Changed** - -- :pull:`919` - Reverts :pull:`841` as per the conclusion in :discussion:`916`. but - preserves the ability to declare attributes with snake_case. - -**Deprecated** - -- :pull:`919` - Declaration of keys via keyword arguments in standard elements. A script - has been added to automatically convert old usages where possible. - - -v1.0.0-a3 ---------- -:octicon:`milestone` *released on 2023-02-02* - -**Fixed** - -- :pull:`908` - minor type hint issue with ``VdomDictConstructor`` - -**Removed** - -- :pull:`907` - accidental import of reactpy.testing - - -v1.0.0-a2 ---------- -:octicon:`milestone` *released on 2023-01-31* - -**Reverted** - -- :pull:`901` - reverts :pull:`886` due to :issue:`896` - -**Fixed** - -- :issue:`896` - Stale event handlers after disconnect/reconnect cycle -- :issue:`898` - Fixed CLI not registered as entry point - - -v1.0.0-a1 ---------- -:octicon:`milestone` *released on 2023-01-28* - -**Changed** - -- :pull:`841` - Revamped element constructor interface. Now instead of passing a - dictionary of attributes to element constructors, attributes are declared using - keyword arguments. For example, instead of writing: - - .. code-block:: - - html.div({"className": "some-class"}, "some", "text") - - You now should write: - - .. code-block:: - - html.div("some", "text", class_name="some-class") - - .. note:: - - All attributes are written using ``snake_case``. - - In conjunction, with these changes, ReactPy now supplies a command line utility that - makes a "best effort" attempt to automatically convert code to the new API. Usage of - this utility is as follows: - - .. code-block:: bash - - reactpy update-html-usages [PATHS] - - Where ``[PATHS]`` is any number of directories or files that should be rewritten. - - .. warning:: - - After running this utility, code comments and formatting may have been altered. It's - recommended that you run a code formatting tool like `Black - `__ and manually review and replace any comments that - may have been moved. - -**Fixed** - -- :issue:`755` - unification of component and VDOM constructor interfaces. See above. - - -v0.44.0 -------- -:octicon:`milestone` *released on 2023-01-27* - -**Deprecated** - -- :pull:`876` - ``reactpy.widgets.hotswap``. The function has no clear uses outside of some - internal applications. For this reason it has been deprecated. - -**Removed** - -- :pull:`886` - Ability to access element value from events via `event['value']` key. - Instead element value should be accessed via `event['target']['value']`. Originally - deprecated in :ref:`v0.34.0`. -- :pull:`886` - old misspelled option ``reactpy.config.REACTPY_WED_MODULES_DIR``. Originally - deprecated in :ref:`v0.36.1`. - - -v0.43.0 -------- -:octicon:`milestone` *released on 2023-01-09* - -**Deprecated** - -- :pull:`870` - ``ComponentType.should_render()``. This method was implemented based on - reading the React/Preact source code. As it turns out though it seems like it's mostly - a vestige from the fact that both these libraries still support class-based - components. The ability for components to not render also caused several bugs. - -**Fixed** - -- :issue:`846` - Nested context does no update value if outer context should not render. -- :issue:`847` - Detached model state on render of context consumer if unmounted and - context value does not change. - - -v0.42.0 -------- -:octicon:`milestone` *released on 2022-12-02* - -**Added** - -- :pull:`835` - Ability to customize the ```` element of ReactPy's built-in client. -- :pull:`835` - ``vdom_to_html`` utility function. -- :pull:`843` - Ability to subscribe to changes that are made to mutable options. -- :pull:`832` - ``del_html_head_body_transform`` to remove ````, ````, and ```` while preserving children. -- :pull:`699` - Support for form element serialization - -**Fixed** - -- :issue:`582` - ``REACTPY_DEBUG_MODE`` is now mutable and can be changed at runtime -- :pull:`832` - Fix ``html_to_vdom`` improperly removing ````, ````, and ```` nodes. - -**Removed** - -- :pull:`832` - Removed ``reactpy.html.body`` as it is currently unusable due to technological limitations, and thus not needed. -- :pull:`840` - remove ``REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY`` option -- :pull:`835` - ``serve_static_files`` option from backend configuration - -**Deprecated** - -- :commit:`8f3785b` - Deprecated ``module_from_template`` - -v0.41.0 -------- -:octicon:`milestone` *released on 2022-11-01* - -**Changed** - -- :pull:`823` - The hooks ``use_location`` and ``use_scope`` are no longer - implementation specific and are now available as top-level imports. Instead of each - backend defining these hooks, backends establish a ``ConnectionContext`` with this - information. -- :pull:`824` - ReactPy's built-in backend server now expose the following routes: - - - ``/_reactpy/assets/`` - - ``/_reactpy/stream/`` - - ``/_reactpy/modules/`` - - ``//`` - - This should allow the browser to cache static resources. Even if your ``url_prefix`` - is ``/_reactpy``, your app should still work as expected. Though if you're using - ``reactpy-router``, ReactPy's server routes will always take priority. -- :pull:`824` - Backend implementations now strip any URL prefix in the pathname for - ``use_location``. -- :pull:`827` - ``use_state`` now returns a named tuple with ``value`` and ``set_value`` - fields. This is convenient for adding type annotations if the initial state value is - not the same as the values you might pass to the state setter. Where previously you - might have to do something like: - - .. code-block:: - - value: int | None = None - value, set_value = use_state(value) - - Now you can annotate your state using the ``State`` class: - - .. code-block:: - - state: State[int | None] = use_state(None) - - # access value and setter - state.value - state.set_value - - # can still destructure if you need to - value, set_value = state - -**Added** - -- :pull:`823` - There is a new ``use_connection`` hook which returns a ``Connection`` - object. This ``Connection`` object contains a ``location`` and ``scope``, along with - a ``carrier`` which is unique to each backend implementation. - - -v0.40.2 -------- -:octicon:`milestone` *released on 2022-09-13* - -**Changed** - -- :pull:`809` - Avoid the use of JSON patch for diffing models. - - -v0.40.1 -------- -:octicon:`milestone` *released on 2022-09-11* - -**Fixed** - -- :issue:`806` - Child models after a component fail to render - - -v0.40.0 (yanked) ----------------- -:octicon:`milestone` *released on 2022-08-13* - -**Fixed** - -- :issue:`777` - Fix edge cases where ``html_to_vdom`` can fail to convert HTML -- :issue:`789` - Conditionally rendered components cannot use contexts -- :issue:`773` - Use strict equality check for text, numeric, and binary types in hooks -- :issue:`801` - Accidental mutation of old model causes invalid JSON Patch - -**Changed** - -- :pull:`123` - set default timeout on playwright page for testing -- :pull:`787` - Track contexts in hooks as state -- :pull:`787` - remove non-standard ``name`` argument from ``create_context`` - -**Added** - -- :pull:`123` - ``asgiref`` as a dependency -- :pull:`795` - ``lxml`` as a dependency - - -v0.39.0 -------- -:octicon:`milestone` *released on 2022-06-20* - -**Fixed** - -- :pull:`763` - ``No module named 'reactpy.server'`` from ``reactpy.run`` -- :pull:`749` - Setting appropriate MIME type for web modules in `sanic` server implementation - -**Changed** - -- :pull:`763` - renamed various: - - - ``reactpy.testing.server -> reactpy.testing.backend`` - - ``ServerFixture -> BackendFixture`` - - ``DisplayFixture.server -> DisplayFixture.backend`` - -- :pull:`765` - ``exports_default`` parameter is removed from ``module_from_template``. - -**Added** - -- :pull:`765` - ability to specify versions with module templates (e.g. - ``module_from_template("react@^17.0.0", ...)``). - - -v0.38.1 -------- -:octicon:`milestone` *released on 2022-04-15* - -**Fixed** - -- `reactive-python/reactpy-jupyter#22 `__ - - a missing file extension was causing a problem with WebPack. - - -v0.38.0 -------- -:octicon:`milestone` *released on 2022-04-15* - -No changes. - - -v0.38.0-a4 ----------- -:octicon:`milestone` *released on 2022-04-15* - -**Added** - -- :pull:`733` - ``use_debug_value`` hook - -**Changed** - -- :pull:`733` - renamed ``assert_reactpy_logged`` testing util to ``assert_reactpy_did_log`` - - -v0.38.0-a3 ----------- -:octicon:`milestone` *released on 2022-04-15* - -**Changed** - -- :pull:`730` - Layout context management is not async - - -v0.38.0-a2 ----------- -:octicon:`milestone` *released on 2022-04-14* - -**Added** - -- :pull:`721` - Implement ``use_location()`` hook. Navigating to any route below the - root of the application will be reflected in the ``location.pathname``. This operates - in concert with how ReactPy's configured routes have changed. This will ultimately work - towards resolving :issue:`569`. - -**Changed** - -- :pull:`721` - The routes ReactPy configures on apps have changed - - .. code-block:: text - - prefix/_api/modules/* web modules - prefix/_api/stream websocket endpoint - prefix/* client react app - - This means that ReactPy's client app is available at any route below the configured - ``url_prefix`` besides ``prefix/_api``. The ``_api`` route will likely remain a route - which is reserved by ReactPy. The route navigated to below the ``prefix`` will be shown - in ``use_location``. - -- :pull:`721` - ReactPy's client now uses Preact instead of React - -- :pull:`726` - Renamed ``reactpy.server`` to ``reactpy.backend``. Other references to "server - implementations" have been renamed to "backend implementations" throughout the - documentation and code. - -**Removed** - -- :pull:`721` - ``redirect_root`` server option - - -v0.38.0-a1 ----------- -:octicon:`milestone` *released on 2022-03-27* - -**Changed** - -- :pull:`703` - How ReactPy integrates with servers. ``reactpy.run`` no longer accepts an app - instance to discourage use outside of testing. ReactPy's server implementations now - provide ``configure()`` functions instead. ``reactpy.testing`` has been completely - reworked in order to support async web drivers -- :pull:`703` - ``PerClientStateServer`` has been functionally replaced by ``configure`` - -**Added** - -- :issue:`669` - Access to underlying server requests via contexts - -**Removed** - -- :issue:`669` - Removed ``reactpy.widgets.multiview`` since basic routing view ``use_scope`` is - now possible as well as all ``SharedClientStateServer`` implementations. - -**Fixed** - -- :issue:`591` - ReactPy's test suite no longer uses sync web drivers -- :issue:`678` - Updated Sanic requirement to ``>=21`` -- :issue:`657` - How we advertise ``reactpy.run`` - - -v0.37.2 -------- -:octicon:`milestone` *released on 2022-03-27* - -**Changed** - -- :pull:`701` - The name of ``proto`` modules to ``types`` and added a top level - ``reactpy.types`` module - -**Fixed** - -- :pull:`716` - A typo caused ReactPy to use the insecure ``ws`` web-socket protocol on - pages loaded with ``https`` instead of the secure ``wss`` protocol - - -v0.37.1 -------- -:octicon:`milestone` *released on 2022-03-05* - -No changes. - - -v0.37.1-a2 ----------- -:octicon:`milestone` *released on 2022-03-02* - -**Fixed:** - -- :issue:`684` - Revert :pull:`694` and by making ``value`` uncontrolled client-side - - -v0.37.1-a1 ----------- -:octicon:`milestone` *released on 2022-02-28* - -**Fixed:** - -- :issue:`684` - ``onChange`` event for inputs missing key strokes - - -v0.37.0 -------- -:octicon:`milestone` *released on 2022-02-27* - -**Added:** - -- :issue:`682` - Support for keys in HTML fragments -- :pull:`585` - Use Context Hook - -**Fixed:** - -- :issue:`690` - React warning about set state in unmounted component -- :pull:`688` - Missing reset of schedule_render_later flag - ----- - -Releases below do not use the "Keep a Changelog" style guidelines. - ----- - -v0.36.3 -------- -:octicon:`milestone` *released on 2022-02-18* - -Misc bug fixes along with a minor improvement that allows components to return ``None`` -to render nothing. - -**Closed Issues** - -- All child states wiped upon any child key change - :issue:`652` -- Allow NoneType returns within components - :issue:`538` - -**Merged Pull Requests** - -- fix #652 - :pull:`672` -- Fix 663 - :pull:`667` - - -v0.36.2 -------- -:octicon:`milestone` *released on 2022-02-02* - -Hot fix for newly introduced ``DeprecatedOption``: - -- :commit:`c146dfb264cbc3d2256a62efdfe9ccf62c795b01` - - -v0.36.1 -------- -:octicon:`milestone` *released on 2022-02-02* - -Includes bug fixes and renames the configuration option ``REACTPY_WED_MODULES_DIR`` to -``REACTPY_WEB_MODULES_DIR`` with a corresponding deprecation warning. - -**Closed Issues** - -- Fix Key Error When Cleaning Up Event Handlers - :issue:`640` -- Update Script Tag Behavior - :issue:`628` - -**Merged Pull Requests** - -- mark old state as None if unmounting - :pull:`641` -- rename REACTPY_WED_MODULES_DIR to REACTPY_WEB_MODULES_DIR - :pull:`638` - - -v0.36.0 -------- -:octicon:`milestone` *released on 2022-01-30* - -This release includes an important fix for errors produced after :pull:`623` was merged. -In addition there is not a new ``http.script`` element which can behave similarly to a -standard HTML `` diff --git a/docs/source/guides/getting-started/_static/embed-reactpy-view/index.html b/docs/source/guides/getting-started/_static/embed-reactpy-view/index.html deleted file mode 100644 index 146d715e4..000000000 --- a/docs/source/guides/getting-started/_static/embed-reactpy-view/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Example App - - -

This is an Example App

-

Just below is an embedded ReactPy view...

-
- - - diff --git a/docs/source/guides/getting-started/_static/embed-reactpy-view/main.py b/docs/source/guides/getting-started/_static/embed-reactpy-view/main.py deleted file mode 100644 index 6e3687f27..000000000 --- a/docs/source/guides/getting-started/_static/embed-reactpy-view/main.py +++ /dev/null @@ -1,22 +0,0 @@ -from sanic import Sanic -from sanic.response import file - -from reactpy import component, html -from reactpy.backend.sanic import Options, configure - -app = Sanic("MyApp") - - -@app.route("/") -async def index(request): - return await file("index.html") - - -@component -def ReactPyView(): - return html.code("This text came from an ReactPy App") - - -configure(app, ReactPyView, Options(url_prefix="/_reactpy")) - -app.run(host="127.0.0.1", port=5000) diff --git a/docs/source/guides/getting-started/_static/embed-reactpy-view/screenshot.png b/docs/source/guides/getting-started/_static/embed-reactpy-view/screenshot.png deleted file mode 100644 index 7439c83cff03c27cbaf3727d190a5e67a4bb90b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13161 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 - - - -]> - - - - - - - - - - - - diff --git a/docs/source/guides/getting-started/_static/logo-jupyter.svg b/docs/source/guides/getting-started/_static/logo-jupyter.svg deleted file mode 100644 index fb2921a41..000000000 --- a/docs/source/guides/getting-started/_static/logo-jupyter.svg +++ /dev/null @@ -1,88 +0,0 @@ - -logo.svg -Created using Figma 0.90 - - - - - - - - - - - - - - - - - - diff --git a/docs/source/guides/getting-started/_static/logo-plotly.svg b/docs/source/guides/getting-started/_static/logo-plotly.svg deleted file mode 100644 index 3dd95459a..000000000 --- a/docs/source/guides/getting-started/_static/logo-plotly.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/docs/source/guides/getting-started/_static/reactpy-in-jupyterlab.gif b/docs/source/guides/getting-started/_static/reactpy-in-jupyterlab.gif deleted file mode 100644 index b420ecd8c5a082a39f5904ba157735fa4327a02e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 551674 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¼wFhiKJO#PGn}eCOA3o(hq*E`fq{h|7m=VVhsn# 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

s|(tlL*{F!C`sOFg~r|`KR26*snK^<|E+diUt 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

3!**NgB6n3ODBalz(e)1+vRy8xz z=0`U-Ni(VXH#_a@k8VaRwTFxS87xQbhARkxMYo911F9S#C1rqxz=Ko}tLF9E8;nPS z@W~pq&i2J6&L*C*EVpN3JTWHzkrtW0$4BUI{biG`t({0Nwrb<;r}wMjt8l;Ce!X5d zZ}XIP%{b!sh}(@upN%8znl?ytgs!kFko|c`pnr*$oI~M`*407Gj zwU+um`<7GW8_6NfN)KYiN)$RvOwy%sfSl6k$a0ns$X-&=*PitLGfZQ7PBt9g!-8t}R4~aK#D0feJviXhBXiaIP&x zlj-SAxtdq!CR5~n#1>;ogFgX?p6U)9%b|M@WcMcFH6*uK36#GSwi)j*L_p^!qjM#$ zRu^I#*`#nLhRTMGkdVHVxL6u8oP-=_BMeEQ2!vWT0|SuDBdEwxBFc}65DL3c{tI!p zA2Ch?Lc~|n@Q$rO2rL<$MsnnoK0eAKl@uZ=q1b*BQo|TlMetUZjioX%e)W;D$_cR~ zSz3pwV!)Nj@P{OL zH5HxBg6p#&fy%&WdT=y|(@4-e0>-IJ3u+KR_7U9kJu~vvP($Rz{6h4QvO^XLc}*EL ztxP(e;uAx{%9}_48GG%{z6M-6GbGe(qTuK~U)Fm45XkKIV9EmL^$_B6l4y_d6 zBnU58M%o;K=6YdDCgCYo$YKI!hJXkqImXaXzIf;qF|3Am<-~0OjSdH51-dlOV`T8X zDYzRGGEPGpFcC};XjC)HNg0{SfL~-F$uWUUNd3Cw207QMrOVZW`tPQiVJD&M@&)Bts96{3X*d@nZcju!3>GrW2?SVcbH-q z-uoEZ6oxF9z$QyTwW$~hgs;Tw3+@hqx&#+z036MLe!B^YVR0&j$VAp-C0=8W`drNq zMY~D(y==?LPevR@hKJ&z-{5h{MBrCAV0$##BIX1rL6LzNPWp56icj3X)E%iwUh zHw`xH1$84KQq=3LnczTn53}u2Hf1c!w!aLX!lnnS$ZYgpYwx`Nxj%_QU@S zSA1K#>ZB~O+YuX12-Kq9so1IoJ`n8LVt}H77at&168?KxY_3R$WE8s%f|$NQc#cP!bNF^SA3gKt&CgJviWX zr*wRBfxPP;0eGk*CdicqeL(HOrSrG$#e$%JZUdO~2dl{sw&ad`%Kw#@Qz9Ym{HB)B zv+p|~o$zi35{)@NT`?rM8!@4Hg3C)Hw$Tx-%mg>mU`@rTfFl`$gZdL8iE#IHUk_P) z#M=ij-!@OnVoY`&t`6u|r(q1uAp0yTV`-=|?Sy0y2xgW8lBd;gKcP!tp)c?6W>&V8 zrcWrNu9484I({!FFV>Jc^I50~b_{L??u&DRd?%)4Ao|#M79D&WK&aZ^SY8Lj>}z;4 zk-Cfk-Tzql)J#cjM=76#l(5J2OB}iN$vNHI&6`nyD?-|yd2-krJ0X#Ke50>;j{@BM z@7wM4#!6s0b`ZWGs4=Dg&j(Z}CgEX5zt<&{Fi;cg-uLiMzqli^fz(+Mg|+=68c_lO z@>9$D;066C)I1i%x{P@jwPgy`1Q4BRdU%rW_Z?nGZ1tN8d@CUK|r>W9uARHE6+MiG-CT{Pwm7C zUkr@CB0roWXy2}bXc6FsLpQ|5GLn@Cr!OFYM(a|9!wmbm0^&Le!zf68RzrY~(5kj} zo_>g~yisy*l!|FR6Zzo+;sFT@qFj+cj5Nhyy$8qdH{M0fLok1GF-ipF7!^0&hkHf9 zs(x{4wn5ZMJZdd;t1@!Z{zcUJMYNrC(8@}=H{ty4r!(i4v(;DECE??*pkXb^S0@G? z-cDT95_|Y+RyV{s+<{q?>7;c?}70rr-IF=N{Lf_1T6WhgW%+ag8p<&74|iI1m`-LlkLzzeR@y5 zaBbj1$YYeR(EZk*7u*<5v?eFLw)B4DOqBRcrtQ-y0`_L?cmBa`Nq~i*F~fuU28R$3O7=C%*7M9igWG*%3k*WSrQ%_jh66 z(6PnAV(ANikFyH8ema=`&v90>S<@?2=3nEiY7VU{<9`+Q`I}k)

2@zAb&wFm-o- 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�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

hvbLLd{H*H zb)(m2AYge5a>dVFOs8G%DMsa!*=4VDT+hQFv`IsnmKx&#XLO(GY$!FWClkfhf>0B& zLr_7YbBpas!!Wi^(+*Umh!76Csg2cS!%G290ePE@5SiXUs%|gt5|FO+M?E6DK&oe7 zL4xEmemW`c4V%2$e!GB?z;AyWlE6Qh*#%dOQ!8?&WXD}b2`4`YCWCeO|0#exvsfd>(KbviDi zich;jK$ec3-shd(;b$&5kgL=ZUdNIk$WL%$tlo5!)BEP96d-_a&Q=g^$GZl01CEeE zC34d_EYui%8=z`n+2tw}1*F^E-045PRQAdx51=$~ti)@k+i*{hge<@{fIq>j?ly~c zBP;+tFE`T{cLcZwIcdWHG|)L=V7Grf3V`#&gF1NEfN%5XnPo^o!W|eSqF`~m0tn3W zT>X5J7hX-w#tZKn)9pZDQp0bTXW?_V}evfm;`v-0!%KiWvOOI8vf!Vl@; z{xyXVj2Gdj3fm|~2H{?RA+1UjKlZTiVk8tb$m(*XyUtG5>T^GjMq2|;K!7G6zkH_2 zS7`42`odWFGhXr2@zB}uc>LaC<6g%DxmP&-*$*#0alhtX*;%9h(W@r}_^gJYzkR0t zy#=&yIT0H0N8kUwW;LP-RidLM7W-+$^QUd`K2u+4^RX`rD*PiJ979TJ*RAzGu*+>? z#Bja>F(~H+)@awes*5{D)$37lMPhSxXfp3ztG{`<7)AJ+aVz;c;0Kn+RQP+aCo(Gf zgM+%Z-duTMr0|GYw~=!+7nRBG_WpRCMs012G`MMkZ=Uv^H2aUxu87!|t3;H;ClhWl zZGF_V{hxVW$MNN0HGsXNFP`<`XI?GT3gZgDLw_{yWeWkS{0s{|uOv70Im73!lqC)R z#A`(Gq{a(=?m3D}PGt1%_pj`Q6HPN~5u}sPDI|`*Iz73s`6#P^Pp|Z{Ak(K z*gRQEq=ezL$i~8h0M{wtJ<7D&F4aj%9WL+^Oy z|C8#K{2ysRmrIz@!tX$aA`=yQ+D8s5|4I6h{#T%fM9wn;Bj}}Q$D6z+34~WP0uW1B z5QG?^6$Rcd`zw@ccNDlN!TmYCh!gpYI)LqaWVa_2zkRb~ups>Q4f=cg7NWw(0zq3C zn}CawdrIbpt2Y+y@Gz3*NJQ&jzcP%3@rUNc`7uvhrg#Ymvw;?VglEeC9Aaw)2$CHw2s z(hx?XMRhQf@#4cXQ=s(*Um|5W+?NxdN0mwNv6CMQDL;O=lB6d?vJgl^MWqH`LGunf zQEP$deR}Woe*|2cWxQxfVtohV%v*?3rap-9N(LgPNK2Gctbkyh?u^X9lg;Z^lk7*Y zDBLKVVS)!cGnOiZLlfHYtA5%jc#h@gbzo$UX;Ntqk8LYlFx8Omg{|s46Si~lvGfGd z(X;rR*DS-|m%aR_NZvz{dtpTB-~!^uvYEdxg8W_yeknu|2m`ww= znW8iDi2KTaOyu_ik0E3w@1+(){uJ|+|GJH9HvA;k8AS70zCt@PynUjNPorS8UqsIk z8=z-)2L56ZTYYYdC|eREW`1zT#!Y-ccjN(eM^N;%`ouL3mEygnT0{yWW zDCYIs@0pa}SEKZzdQ!hJD$oPRJ-L5*(Q+e(r$q*3|4zK6J^WXl{>zSEKX}iodd-bd z0{|d=u%I#4FPBtvG84h1xQK5FHs{au%L_>Z)AYe$EUpHOj{2cdS0?j;aI@q` z5ocb-rI2@-kt{dSz@gi;s2a5jO?%|XWA4kz{}y}3A_gS-EqFxffi+eSHQhh})U16gMp znRpiQs(KI2eX4`JWDnlO@RWP#+8Dv4v%oWce@7?4G1e~x#xc*^P8QvJ@<3k(i+%2S z4tIM{;gk9lwRCk6X5N9k1goR^6hDrsec23y&i9s6fegyvQ)Pf_u!Zl76bhMUtoRhc zH9#}Iyotb%W7e9EDn7&0$5{QvPtcL8T)U+qS7OPX@xEbGL>itvGMOA#I&vkAxy-;_ z?!_=V?Dkw`i6UP>f%B43I~xNu%N;7XJjrl&n2YW?ac^MnP&glfSOf zeb#=n{_7snQ{z8x{_C_i#)riq$=dZ7{^1qR4Sik9PF8%YxTCI!%K3fTv(E#?JlPL< zg)^_%{>&@pp8x)-EkQmN^{I(Q`u8TE^`-LXq0Z?opUK33F24)F^1JYl4*=;^y(UIChQcb4D&7BnmO3vbvS-wz)RMlR*;!C4i715GZ!lxPq|1mLJ0v#|avz zNE8FG{HAMGC>#3pGByl9gzlhUK^P@Pbb;))!KSl6!(=tZv9IC+sGYEJi~_1*epH0R z=dV<*X5|ETdWOW#G=Kj^>>`3wnFtbA^F9m}P(-*!>yte}<*GUU7vqiJ?dZ zc+-J(sl*8J5y=9ob?-Y`RKYMTRbDg--;uAulkONRr~~5y>f4aeaDfb;CF^&tatKG7 zC58|V4(iR1>GqNGi7|+pzWLZb?!~Y;$tWVATJJNn3;NREFbyfU@0${EJORWpEedwOI4rs?+bm!4G`reEzvi|5rLv3aHYxKz|>fU;l5abjMer`912$ z{ncnj!yO^u_)K!_w7Dlo!Crf)O{?4tPs`T6?br#}zi_QYM>JWkLB}MEjxU-~Ts>+( zj!KPvq$pai{k)Eu^LUWDcTwQHFSnZ5cl5Exh1pNm_Z_S00(P&_IM!-J7%wY7ZS z%rC&5ZMyI5&NiHl$&y29$@q~P{qv^w3lJ?rK!26zf7D2t^QN#E6D~`#zox2)nT;$B z?K{DwkH#JFC6sn_d4|6bwsr-~i!c}fzJbDcLIN-dWN6QaCZxJa%4#1&LQblcz^kF7 za>JxV|A}f&OZ}F~b6U$!JDbU8v_OwU;SHB|JS-}ZcE z`wu^dbcNf;rXtGszICmRJhs?N4#nD3+`T$7+}`o!M{~rZdtXGr`X^`LJYkb8nyOr# z!_NJywCh6H^RF^!j{E`_;NO+_lUXrH=Q@>OobtwERP8kx5_!)Th-^Fbi_&+oLS0Q3 z_8>+%grI_0!9%W_p8@jAvF76+V)qhffOC?b$YxQJp$zh4vayT+20Gl_3sPYr?HN|G zk35s5ff4Z|mg5nW{b`!ZW3LW@r;mMKUs=!gdp+aAr?9bl6#}_zNT~7&er=5$O@YbAf88bc34}oA1b=w2f6U$aBk2Ce z1&&M8w2^4~Q0q^TZPTB`k!#&Ugmu&YGk0fd>4t61IZI@mbRiCFf@TW-Mg$m6Sr$d{ zb9Z$B6Zqn}Md6jL9H$It^usD(Sn0#JC-zDq2VX0IOfHHhQ;qV|VnNRomj_wydl}=`=sxE3m|XID zIC0&T%f29GLMEv&cA<%Lr*9z-?=*OmHQsY(uL`Y>)?L5((@y3+Co~DmOxFWKD#7mI z7B93bnXeqh*>rc%9it}JR<3L0p@Mu44__{qhHzb+AjNS5uQS`;n4AYNUj9VyJ(%3$ z9isCmelEN=GBUJ9vfZcWpau9sSQNGFwRBiUFJ9( z`%k_i=4RPVi@ov*0pN_n{q-i3QCTKvu~!Y`9-Y0({63`eTefSha!Bz16{u~T`4we@ zF58nipPFv-DR%4J7HF7FBQvYIo8B=>(MAE75iwY!51Pu6Ix1j zFDwBO3mOeIg6(+e@N2Q7K~PgY&?~B*{7>Dijw`yfdSV4?eO$46hyA=yZ4L+c>&kuJ z@NJKRyXZ{ZB?(9-#$WD)z*8&Z58z{mS+_MZ>!+Yxv1p{`KL24e15<@9zLkerTn<@m7OW zm0^4;G@a|#kIklq%KM*6j!vb)Ud}7u_shFa6LyS#(S$);W%qs@4cjTz98tlm6%MAW zKE#Uk*%d>UBEm#Zzpkcxo_-s?D2830X&mtreOi7y@QZ_iKd> zM9hx`pN^mjl(o^ zR{)dG07HCtU5K;@q^l1o#Afz^I9DKf$zKib^HCQ9DntY($o8;PA4FZ!bEHa-QfEFd zOw_=If-59aFn`~V2~Z&-&WjX_=v0Z7uIHq$kxSLb5>VufI?~;ido6R*C{}XSfjOwJ z`x}xf>UI%Z#wW{Or7iEMpkt>mCyZJu6x0|6sa3XwCN+(7wLorM(GL3!O>N`&q%;;M z%2?`us3x5tQV=hJ=AT=CImbW5>wj%X`tBxS`LF+T>nBvqaytV583ev^_^Qp_MPdB6 zNW*dYr(~!J-zYxZDCbJI3%F2T`KDB3J+L7uKkfj?y#5!Z?9Uv5BTGoD!lsozHOr`c z1_(Dg@2-##VoPK;73?brs3x_ld{T%x6sc_W10W(c&Tu=t7t} z4A-B{bhs42|GLycQ&cZKFHWxTt`iV$^gCaA^(-RxEan{eM`;uPAaI7i1FDIo3qB%N zMq4%A-dKI;0^}I6mP!3N1PC{3cs53T3;%>i#;UTO?KS^wJ=dK=5=$T0@U0uv3ujl$ zcZfl6WX9C8W8gyW=n$Bld7}zRs)#}|EGB6*1eQmExp9X(P`<3hBCV{f6yl4%Syon= zsDLJZdi7)H^PpEu&uuD~Nz*HzPi<|h?(9Z!Ro7CG@zl1SZxyO5R@mmIz-Oeq7?%my zZ2%-D?#9J?ZiY3GZ?IcU%M_?j>g3!#Sns>91gmT}9i>K;U<4L6UJ&T|-plV(%-kye-6QLeASY1fCV&mRy z%dH1VlIxB8c33F6;8i${x8rN;SQN#UKakDE;I>%W*&w_&k}qn-ijV^;t}J=V(ZOWj z&53HK*3{PUE=QQ)0N6eqLWx$H8a~{7hH?lt;wK zTVI*tKnzVXB1}-ALC<8m0QxCp`GJbroYr3D6po0Ic+1igO3ZU1gY<46eAGUUzL^W5 zOqdD+-{A&Jq8`dQLRWl+Xtk-%uao%3qxdfIb%QuzJ)v+$_feHp9nxb+G_l9+`16pT zVkqKEnNuog>s$RU>4b^)s%YzX8*h->+atT%WHfjRbn z8)D~~AtciI&wO#*{-a7igU;Lhv-JL7E3W?^f0zaYC<^f30+f~^dpv<0^>S%VbtKS- zLX=^sRa1C|!q4^LMc;q^L;Cw;qazzo(n>B-dkDGN#<&kI`K@>TK@*Hqu2j3IhRfcW z5(_bW_MhS{LXSkjc6xs)Hix16?{NY>dgzO=d&qgG`q%aQGepJdxC+# zXm+T?y2{{Ba+L-BmsUpd@#TG1?dF79teiJzu?`)dhI*N>57PZgU1VO;S-;Xkju9DK zute1MRNwRZ_+Q>0G+_pwc|3_f*!S@t+XLOU*1H(}6hYjc#QY04zI*bKJfei{9z&0r zS?2Nokj^h0Z8y5A>l{ukYj@^SrS{X>(+!fl(BH}Vu)Y|EINz`k{*7+TiUDWKm`@8W z>!tO9m=+ki$a-8-4f9MqG6)xH@Mntd}OLCtm{~>DPu?hOxyC`q$$jksj)yk&dvf6X zNco$lm}E$)Kz>4@&KH-)TejWguB~t^pNck&mQwB;OntjUYT~o#T~2bvYE@UJ0Ix1^ z=27(xwG~4tZQ>Yh?{p?`-!raH{@iuOKDi_jAr+)0zQurWWHcH@CdnQ(2k zNm6vcp+p+?w9G8mG}x4>>z0iaNG8+H;Vd=oF?tAjWPvy7xEo98p%f-FQGyH{@>I^f zi0*nd!%D&r1CvrjiMs)o#E>bf=AN_*LIt{!^H<&*410{HT6CE<>Xcu$+AJPVThDzB z5%}^f>jGFnBHLbE+CW94I?RHVu#>A7^d(qC+3EVjk5ro_Y;pJEV3bgS-V2n(t*KYW zEj2{x*@#Qfy}~SC_uL$rZ}#oC3)mgpq5Da{8ceZx+1i8=#W#DPR5I;w4;HRF3vG~@ zY9@zwQAdeR@luO^d#JqDx%g9t6G8U9Qrd)xuJO}T=1i6<_HC_6TYYvyOH(y=CdNKG zb902H<*)>ay8Y*#BuS!A@IctM#x|}^vlBzx=_!?!_GA9tlhD9&$=UPpJ6f)+Gy~ks zd}@Q@QnF}DIQunu_!B33EtOw)_Ts=-9I0d-q3{5buoj5Tr82`O2jMW~P^u;0(xx=C zXQU!B=uS|z3dSqrIXk^&$O_D{Dx@40hjG`PwQ1zOP+G~}XZ3_il(8l%TKiE*tL0B3 zRVwA&h=kX--*Ga~WYEEq%)fC)5fY&zq9*f+uf`UM1Q8~wRES%;(rWQaBdNJ2hXX4) zlH);Apo{Su2_}QHD1CkkuN{QAa%rJnIHu}!_wcbR*P2$0Sad^XH-j-XpSCI2v$y

A6ah66BiT4|yRm=w+V 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 V6@AeeeGSqe4I9nTz7Y@r06QZgj+g)d diff --git a/docs/source/guides/getting-started/index.rst b/docs/source/guides/getting-started/index.rst deleted file mode 100644 index dd210be60..000000000 --- a/docs/source/guides/getting-started/index.rst +++ /dev/null @@ -1,123 +0,0 @@ -Getting Started -=============== - -.. toctree:: - :hidden: - - installing-reactpy - running-reactpy - -.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn - :color: info - :animate: fade-in - :open: - - .. grid:: 1 2 2 2 - - .. grid-item-card:: :octicon:`tools` Installing ReactPy - :link: installing-reactpy - :link-type: doc - - Learn how ReactPy can be installed in a variety of different ways - with - different web servers and even in different frameworks. - - .. grid-item-card:: :octicon:`play` Running ReactPy - :link: running-reactpy - :link-type: doc - - See how ReactPy can be run with a variety of different production servers or be - added to existing applications. - -The fastest way to get started with ReactPy is to try it out in a `Juptyer Notebook -`__. -If you want to use a Notebook to work through the examples shown in this documentation, -you'll need to replace calls to ``reactpy.run(App)`` with a line at the end of each cell -that constructs the ``App()`` in question. If that doesn't make sense, the introductory -notebook linked below will demonstrate how to do this: - -.. card:: - :link: https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb - - .. image:: _static/reactpy-in-jupyterlab.gif - :scale: 72% - :align: center - - -Section 1: Installing ReactPy ------------------------------ - -The next fastest option is to install ReactPy along with a supported server (like -``starlette``) with ``pip``: - -.. code-block:: bash - - pip install "reactpy[starlette]" - -To check that everything is working you can run the sample application: - -.. code-block:: bash - - python -c "import reactpy; reactpy.run(reactpy.sample.SampleApp)" - -.. note:: - - This launches a simple development server which is good enough for testing, but - probably not what you want to use in production. When deploying in production, - there's a number of different ways of :ref:`running ReactPy

`. - -You should then see a few log messages: - -.. code-block:: text - - 2022-03-27T11:58:59-0700 | WARNING | You are running a development server. Change this before deploying in production! - 2022-03-27T11:58:59-0700 | INFO | Running with 'Starlette' at http://127.0.0.1:8000 - -The second log message includes a URL indicating where you should go to view the app. -That will usually be http://127.0.0.1:8000. Once you go to that URL you should see -something like this: - -.. card:: - - .. reactpy-view:: _examples/sample_app - -If you get a ``RuntimeError`` similar to the following: - -.. code-block:: text - - Found none of the following builtin server implementations... - -Then be sure you run ``pip install "reactpy[starlette]"`` instead of just ``reactpy``. For -anything else, report your issue in ReactPy's :discussion-type:`discussion forum -`. - -.. card:: - :link: installing-reactpy - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Learn how ReactPy can be installed in a variety of different ways - with different web - servers and even in different frameworks. - - -Section 2: Running ReactPy --------------------------- - -Once you've :ref:`installed ReactPy `, you'll want to learn how to run an -application. Throughout most of the examples in this documentation, you'll see the -:func:`~reactpy.backend.utils.run` function used. While it's convenient tool for -development it shouldn't be used in production settings - it's slow, and could leak -secrets through debug log messages. - -.. reactpy:: _examples/hello_world - -.. card:: - :link: running-reactpy - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - See how ReactPy can be run with a variety of different production servers or be - added to existing applications. diff --git a/docs/source/guides/getting-started/installing-reactpy.rst b/docs/source/guides/getting-started/installing-reactpy.rst deleted file mode 100644 index 0b2ffc28a..000000000 --- a/docs/source/guides/getting-started/installing-reactpy.rst +++ /dev/null @@ -1,121 +0,0 @@ -Installing ReactPy -================== - -You will typically ``pip`` install ReactPy to alongside one of it's natively supported -backends. For example, if we want to run ReactPy using the `Starlette -`__ backend you would run - -.. code-block:: bash - - pip install "reactpy[starlette]" - -If you want to install a "pure" version of ReactPy **without a backend implementation** -you can do so without any installation extras. You might do this if you wanted to -:ref:`use a custom backend ` or if you wanted to manually pin -the dependencies for your chosen backend: - -.. code-block:: bash - - pip install reactpy - - -Native Backends ---------------- - -ReactPy includes built-in support for a variety backend implementations. To install the -required dependencies for each you should substitute ``starlette`` from the ``pip -install`` command above with one of the options below: - -- ``fastapi`` - https://fastapi.tiangolo.com -- ``flask`` - https://palletsprojects.com/p/flask/ -- ``sanic`` - https://sanicframework.org -- ``starlette`` - https://www.starlette.io/ -- ``tornado`` - https://www.tornadoweb.org/en/stable/ - -If you need to, you can install more than one option by separating them with commas: - -.. code-block:: bash - - pip install "reactpy[fastapi,flask,sanic,starlette,tornado]" - -Once this is complete you should be able to :ref:`run ReactPy ` with your -chosen implementation. - - -Other Backends --------------- - -While ReactPy can run in a variety of contexts, sometimes frameworks require extra work in -order to integrate with them. In these cases, the ReactPy team distributes bindings for -those frameworks as separate Python packages. For documentation on how to install and -run ReactPy in these supported frameworks, follow the links below: - -.. raw:: html - - - -.. role:: transparent-text-color - -.. We add transparent-text-color to the text so it's not visible, but it's still -.. searchable. - -.. grid:: 3 - - .. grid-item-card:: - :link: https://github.com/reactive-python/reactpy-django - :img-background: _static/logo-django.svg - :class-card: card-logo-image - - :transparent-text-color:`Django` - - .. grid-item-card:: - :link: https://github.com/reactive-python/reactpy-jupyter - :img-background: _static/logo-jupyter.svg - :class-card: card-logo-image - - :transparent-text-color:`Jupyter` - - .. grid-item-card:: - :link: https://github.com/reactive-python/reactpy-dash - :img-background: _static/logo-plotly.svg - :class-card: card-logo-image - - :transparent-text-color:`Plotly Dash` - - -For Development ---------------- - -If you want to contribute to the development of ReactPy or modify it, you'll want to -install a development version of ReactPy. This involves cloning the repository where ReactPy's -source is maintained, and setting up a :ref:`development environment`. From there you'll -be able to modifying ReactPy's source code and :ref:`run its tests ` to -ensure the modifications you've made are backwards compatible. If you want to add a new -feature to ReactPy you should write your own test that validates its behavior. - -If you have questions about how to modify ReactPy or help with its development, be sure to -:discussion:`start a discussion `. The ReactPy team are always -excited to :ref:`welcome ` new contributions and contributors -of all kinds - -.. card:: - :link: /about/contributor-guide - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Learn more about how to contribute to the development of ReactPy. diff --git a/docs/source/guides/getting-started/running-reactpy.rst b/docs/source/guides/getting-started/running-reactpy.rst deleted file mode 100644 index 8abbd574f..000000000 --- a/docs/source/guides/getting-started/running-reactpy.rst +++ /dev/null @@ -1,221 +0,0 @@ -Running ReactPy -=============== - -The simplest way to run ReactPy is with the :func:`~reactpy.backend.utils.run` function. This -is the method you'll see used throughout this documentation. However, this executes your -application using a development server which is great for testing, but probably not what -if you're :ref:`deploying in production `. Below are some -more robust and performant ways of running ReactPy with various supported servers. - - -Running ReactPy in Production ------------------------------ - -The first thing you'll need to do if you want to run ReactPy in production is choose a -backend implementation and follow its documentation on how to create and run an -application. This is the backend :ref:`you probably chose ` when -installing ReactPy. Then you'll need to configure that application with an ReactPy view. We -show the basics of how to set up, and then run, each supported backend below, but all -implementations will follow a pattern similar to the following: - -.. code-block:: - - from my_chosen_backend import Application - - from reactpy import component, html - from reactpy.backend.my_chosen_backend import configure - - - @component - def HelloWorld(): - return html.h1("Hello, world!") - - - app = Application() - configure(app, HelloWorld) - -You'll then run this ``app`` using an `ASGI `__ -or `WSGI `__ server from the command line. - - -Running with `FastAPI `__ -....................................................... - -.. reactpy:: _examples/run_fastapi - -Then assuming you put this in ``main.py``, you can run the ``app`` using the `Uvicorn -`__ ASGI server: - -.. code-block:: bash - - uvicorn main:app - - -Running with `Flask `__ -............................................................. - -.. reactpy:: _examples/run_flask - -Then assuming you put this in ``main.py``, you can run the ``app`` using the `Gunicorn -`__ WSGI server: - -.. code-block:: bash - - gunicorn main:app - - -Running with `Sanic `__ -................................................... - -.. reactpy:: _examples/run_sanic - -Then assuming you put this in ``main.py``, you can run the ``app`` using Sanic's builtin -server: - -.. code-block:: bash - - sanic main.app - - -Running with `Starlette `__ -...................................................... - -.. reactpy:: _examples/run_starlette - -Then assuming you put this in ``main.py``, you can run the application using the -`Uvicorn `__ ASGI server: - -.. code-block:: bash - - uvicorn main:app - - -Running with `Tornado `__ -................................................................ - -.. reactpy:: _examples/run_tornado - -Tornado is run using it's own builtin server rather than an external WSGI or ASGI -server. - - -Running ReactPy in Debug Mode ------------------------------ - -ReactPy provides a debug mode that is turned off by default. This can be enabled when you -run your application by setting the ``REACTPY_DEBUG_MODE`` environment variable. - -.. tab-set:: - - .. tab-item:: Unix Shell - - .. code-block:: - - export REACTPY_DEBUG_MODE=1 - python my_reactpy_app.py - - .. tab-item:: Command Prompt - - .. code-block:: text - - set REACTPY_DEBUG_MODE=1 - python my_reactpy_app.py - - .. tab-item:: PowerShell - - .. code-block:: powershell - - $env:REACTPY_DEBUG_MODE = "1" - python my_reactpy_app.py - -.. danger:: - - Leave debug mode off in production! - -Among other things, running in this mode: - -- Turns on debug log messages -- Adds checks to ensure the :ref:`VDOM` spec is adhered to -- Displays error messages that occur within your app - -Errors will be displayed where the uppermost component is located in the view: - -.. reactpy:: _examples/debug_error_example - - -Backend Configuration Options ------------------------------ - -ReactPy's various backend implementations come with ``Options`` that can be passed to their -respective ``configure()`` functions in the following way: - -.. code-block:: - - from reactpy.backend. import configure, Options - - configure(app, MyComponent, Options(...)) - -To learn more read about the options for your chosen backend ````: - -- :class:`reactpy.backend.fastapi.Options` -- :class:`reactpy.backend.flask.Options` -- :class:`reactpy.backend.sanic.Options` -- :class:`reactpy.backend.starlette.Options` -- :class:`reactpy.backend.tornado.Options` - - -Embed in an Existing Webpage ----------------------------- - -ReactPy provides a Javascript client called ``@reactpy/client`` that can be used to embed -ReactPy views within an existing applications. This is actually how the interactive -examples throughout this documentation have been created. You can try this out by -embedding one the examples from this documentation into your own webpage: - -.. tab-set:: - - .. tab-item:: HTML - - .. literalinclude:: _static/embed-doc-ex.html - :language: html - - .. tab-item:: â–ļī¸ Result - - .. raw:: html - :file: _static/embed-doc-ex.html - -.. note:: - - For more information on how to use the client see the :ref:`Javascript API` - reference. Or if you need to, your can :ref:`write your own backend implementation - `. - -As mentioned though, this is connecting to the server that is hosting this -documentation. If you want to connect to a view from your own server, you'll need to -change the URL above to one you provide. One way to do this might be to add to an -existing application. Another would be to run ReactPy in an adjacent web server instance -that you coordinate with something like `NGINX `__. For the sake -of simplicity, we'll assume you do something similar to the following in an existing -Python app: - -.. tab-set:: - - .. tab-item:: main.py - - .. literalinclude:: _static/embed-reactpy-view/main.py - :language: python - - .. tab-item:: index.html - - .. literalinclude:: _static/embed-reactpy-view/index.html - :language: html - -After running ``python main.py``, you should be able to navigate to -``http://127.0.0.1:8000/index.html`` and see: - -.. card:: - :text-align: center - - .. image:: _static/embed-reactpy-view/screenshot.png - :width: 500px - diff --git a/docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst b/docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst deleted file mode 100644 index b9f274f0a..000000000 --- a/docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst +++ /dev/null @@ -1,6 +0,0 @@ -Combining Contexts and Reducers 🚧 -================================== - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst b/docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst deleted file mode 100644 index 4a00caa48..000000000 --- a/docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst +++ /dev/null @@ -1,6 +0,0 @@ -Deeply Sharing State with Contexts 🚧 -===================================== - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/managing-state/how-to-structure-state/index.rst b/docs/source/guides/managing-state/how-to-structure-state/index.rst deleted file mode 100644 index 5092370a5..000000000 --- a/docs/source/guides/managing-state/how-to-structure-state/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _Structuring Your State: - -How to Structure State 🚧 -========================= - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/managing-state/index.rst b/docs/source/guides/managing-state/index.rst deleted file mode 100644 index 0578bafdd..000000000 --- a/docs/source/guides/managing-state/index.rst +++ /dev/null @@ -1,127 +0,0 @@ -Managing State -============== - -.. toctree:: - :hidden: - - how-to-structure-state/index - sharing-component-state/index - when-and-how-to-reset-state/index - simplifying-updates-with-reducers/index - deeply-sharing-state-with-contexts/index - combining-contexts-and-reducers/index - -.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn - :color: info - :animate: fade-in - :open: - - .. grid:: 1 2 2 2 - - .. grid-item-card:: :octicon:`organization` How to Structure State - :link: how-to-structure-state/index - :link-type: doc - - Make it easy to reason about your application with strategies for organizing - state. - - .. grid-item-card:: :octicon:`link` Sharing Component State - :link: sharing-component-state/index - :link-type: doc - - Allow components to vary vary together, by lifting state into common - parents. - - .. grid-item-card:: :octicon:`light-bulb` When and How to Reset State - :link: when-and-how-to-reset-state/index - :link-type: doc - - Control if and how state is preserved by understanding it's relationship to - the "UI tree". - - .. grid-item-card:: :octicon:`plug` Simplifying Updates with Reducers - :link: simplifying-updates-with-reducers/index - :link-type: doc - - Consolidate state update logic outside your component in a single function, - called a “reducer". - - .. grid-item-card:: :octicon:`broadcast` Deeply Sharing State with Contexts - :link: deeply-sharing-state-with-contexts/index - :link-type: doc - - Instead of passing shared state down deep component trees, bring state into - "contexts" instead. - - .. grid-item-card:: :octicon:`rocket` Combining Contexts and Reducers - :link: combining-contexts-and-reducers/index - :link-type: doc - - You can combine reducers and context together to manage state of a complex - screen. - - -Section 1: How to Structure State ---------------------------------- - -.. note:: - - Under construction 🚧 - - -Section 2: Shared Component State ---------------------------------- - -Sometimes, you want the state of two components to always change together. To do it, -remove state from both of them, move it to their closest common parent, and then pass it -down to them via props. This is known as “lifting state up”, and it’s one of the most -common things you will do writing code with ReactPy. - -In the example below the search input and the list of elements below share the same -state, the state represents the food name. Note how the component ``Table`` gets called -at each change of state. The component is observing the state and reacting to state -changes automatically, just like it would do in React. - -.. reactpy:: sharing-component-state/_examples/synced_inputs - -.. card:: - :link: sharing-component-state/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Allow components to vary vary together, by lifting state into common parents. - - -Section 3: When and How to Reset State --------------------------------------- - -.. note:: - - Under construction 🚧 - - -Section 4: Simplifying Updates with Reducers --------------------------------------------- - -.. note:: - - Under construction 🚧 - - -Section 5: Deeply Sharing State with Contexts ---------------------------------------------- - -.. note:: - - Under construction 🚧 - - - -Section 6: Combining Contexts and Reducers ------------------------------------------- - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json b/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json deleted file mode 100644 index f977fe9a7..000000000 --- a/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "Sushi", - "description": "Sushi is a traditional Japanese dish of prepared vinegared rice" - }, - { - "name": "Dal", - "description": "The most common way of preparing dal is in the form of a soup to which onions, tomatoes and various spices may be added" - }, - { - "name": "Pierogi", - "description": "Pierogi are filled dumplings made by wrapping unleavened dough around a savoury or sweet filling and cooking in boiling water" - }, - { - "name": "Shish Kebab", - "description": "Shish kebab is a popular meal of skewered and grilled cubes of meat" - }, - { - "name": "Dim sum", - "description": "Dim sum is a large range of small dishes that Cantonese people traditionally enjoy in restaurants for breakfast and lunch" - } -] diff --git a/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py b/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py deleted file mode 100644 index ca68aedcb..000000000 --- a/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py +++ /dev/null @@ -1,44 +0,0 @@ -import json -from pathlib import Path - -from reactpy import component, hooks, html, run - -HERE = Path(__file__) -DATA_PATH = HERE.parent / "data.json" -food_data = json.loads(DATA_PATH.read_text()) - - -@component -def FilterableList(): - value, set_value = hooks.use_state("") - return html.p(Search(value, set_value), html.hr(), Table(value, set_value)) - - -@component -def Search(value, set_value): - def handle_change(event): - set_value(event["target"]["value"]) - - return html.label( - "Search by Food Name: ", - html.input({"value": value, "on_change": handle_change}), - ) - - -@component -def Table(value, set_value): - rows = [] - for row in food_data: - name = html.td(row["name"]) - descr = html.td(row["description"]) - tr = html.tr(name, descr, value) - if not value: - rows.append(tr) - elif value.lower() in row["name"].lower(): - rows.append(tr) - headers = html.tr(html.td(html.b("name")), html.td(html.b("description"))) - table = html.table(html.thead(headers), html.tbody(rows)) - return table - - -run(FilterableList) diff --git a/docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py b/docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py deleted file mode 100644 index e8bcdf333..000000000 --- a/docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py +++ /dev/null @@ -1,23 +0,0 @@ -from reactpy import component, hooks, html, run - - -@component -def SyncedInputs(): - value, set_value = hooks.use_state("") - return html.p( - Input("First input", value, set_value), - Input("Second input", value, set_value), - ) - - -@component -def Input(label, value, set_value): - def handle_change(event): - set_value(event["target"]["value"]) - - return html.label( - label + " ", html.input({"value": value, "on_change": handle_change}) - ) - - -run(SyncedInputs) diff --git a/docs/source/guides/managing-state/sharing-component-state/index.rst b/docs/source/guides/managing-state/sharing-component-state/index.rst deleted file mode 100644 index 54b61335a..000000000 --- a/docs/source/guides/managing-state/sharing-component-state/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -Sharing Component State -======================= - -.. note:: - - Parts of this document are still under construction 🚧 - -Sometimes, you want the state of two components to always change together. To do it, -remove state from both of them, move it to their closest common parent, and then pass it -down to them via props. This is known as “lifting state up”, and it’s one of the most -common things you will do writing code with ReactPy. - - -Synced Inputs -------------- - -In the code below the two input boxes are synchronized, this happens because they share -state. The state is shared via the parent component ``SyncedInputs``. Check the ``value`` -and ``set_value`` variables. - -.. reactpy:: _examples/synced_inputs - - -Filterable List ----------------- - -In the example below the search input and the list of elements below share the -same state, the state represents the food name. - -Note how the component ``Table`` gets called at each change of state. The -component is observing the state and reacting to state changes automatically, -just like it would do in React. - -.. reactpy:: _examples/filterable_list - -.. note:: - - Try typing a food name in the search bar. diff --git a/docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst b/docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst deleted file mode 100644 index 08fce5a69..000000000 --- a/docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst +++ /dev/null @@ -1,6 +0,0 @@ -Simplifying Updates with Reducers 🚧 -==================================== - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst b/docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst deleted file mode 100644 index 6a96f4b30..000000000 --- a/docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _When to Reset State: - -When and How to Reset State 🚧 -============================== - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg b/docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg deleted file mode 100644 index 9077913ca..000000000 --- a/docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - layout - - - - - component - - server - - - - view - - client - - - - event - VDOM diff - VDOM - diff --git a/docs/source/guides/understanding-reactpy/_static/live-examples-in-docs.gif b/docs/source/guides/understanding-reactpy/_static/live-examples-in-docs.gif deleted file mode 100644 index 96a04d68ba0480c5614acece7994c744b21382c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182779 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

6pQ(;c*mU#DxDSHa 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

w*dFg`of(3eCNEk-MoQ`9mjKM5~R+$r@#3;6I8# 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

=qV!p*y*Vl$?cn$I?bK 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 - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - model - - - - - controller - - server - - - - - model - - - - - view - - client - - - - - event - sync - change - render - diff --git a/docs/source/guides/understanding-reactpy/_static/npm-download-trends.png b/docs/source/guides/understanding-reactpy/_static/npm-download-trends.png deleted file mode 100644 index cf5140b0dbd3f46735f02a2fad8aa6338b1f40b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64372 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%0Dk(^(f=Hv3bW2M}jaCF<N(2!bF25~GH| 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, but we skimmed over the question of the data structure we use to represent -it. Let's reconsider the examples from before - on the top is some HTML and on the -bottom is the corresponding code to create it in ReactPy: - -.. code-block:: html - -

-

My Todo List

-
    -
  • Build a cool new app
  • -
  • Share it with the world!
  • -
-
- -.. testcode:: - - from reactpy import html - - layout = html.div( - html.h1("My Todo List"), - html.ul( - html.li("Build a cool new app"), - html.li("Share it with the world!"), - ) - ) - -Since we've captured our HTML into out the ``layout`` variable, we can inspect what it -contains. And, as it turns out, it holds a dictionary. Printing it produces the -following output: - -.. testsetup:: - - from pprint import pprint - print = lambda *args, **kwargs: pprint(*args, **kwargs, sort_dicts=False) - -.. testcode:: - - assert layout == { - 'tagName': 'div', - 'children': [ - { - 'tagName': 'h1', - 'children': ['My Todo List'] - }, - { - 'tagName': 'ul', - 'children': [ - {'tagName': 'li', 'children': ['Build a cool new app']}, - {'tagName': 'li', 'children': ['Share it with the world!']} - ] - } - ] - } - -This may look complicated, but let's take a moment to consider what's going on here. We -have a series of nested dictionaries that, in some way, represents the HTML structure -given above. If we look at their contents we should see a common form. Each has a -``tagName`` key which contains, as the name would suggest, the tag name of an HTML -element. Then within the ``children`` key is a list that either contains strings or -other dictionaries that represent HTML elements. - -What we're seeing here is called a "virtual document object model" or :ref:`VDOM`. This -is just a fancy way of saying we have a representation of the document object model or -`DOM -`__ -that is not the actual DOM. diff --git a/docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst b/docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst deleted file mode 100644 index cdde27f08..000000000 --- a/docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _The Rendering Pipeline: - -The Rendering Pipeline 🚧 -========================= - -.. talk about layouts and dispatchers - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/understanding-reactpy/the-rendering-process.rst b/docs/source/guides/understanding-reactpy/the-rendering-process.rst deleted file mode 100644 index 00215a887..000000000 --- a/docs/source/guides/understanding-reactpy/the-rendering-process.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _The Rendering Process: - -The Rendering Process 🚧 -======================== - -.. refer to https://beta.reactjs.org/learn/render-and-commit - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/understanding-reactpy/what-are-components.rst b/docs/source/guides/understanding-reactpy/what-are-components.rst deleted file mode 100644 index 4c22dda13..000000000 --- a/docs/source/guides/understanding-reactpy/what-are-components.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _What Are Components: - -What Are Components? 🚧 -======================= - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst b/docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst deleted file mode 100644 index e570b8f41..000000000 --- a/docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _Why ReactPy Needs Keys: - -Why ReactPy Needs Keys 🚧 -========================= - -.. note:: - - Under construction 🚧 diff --git a/docs/source/guides/understanding-reactpy/writing-tests.rst b/docs/source/guides/understanding-reactpy/writing-tests.rst deleted file mode 100644 index ffac27df6..000000000 --- a/docs/source/guides/understanding-reactpy/writing-tests.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _Writing Tests: - -Writing Tests 🚧 -================ - -.. note:: - - Under construction 🚧 diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 8b21160f6..000000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,206 +0,0 @@ -.. card:: - - This documentation is still under construction 🚧. We welcome your `feedback - `__! - - -ReactPy -======= - -.. toctree:: - :hidden: - :caption: Guides - - guides/getting-started/index - guides/creating-interfaces/index - guides/adding-interactivity/index - guides/managing-state/index - guides/escape-hatches/index - guides/understanding-reactpy/index - -.. toctree:: - :hidden: - :caption: Reference - - reference/browser-events - reference/html-attributes - reference/hooks-api - _auto/apis - reference/javascript-api - reference/specifications - -.. toctree:: - :hidden: - :caption: About - - about/changelog - about/contributor-guide - about/credits-and-licenses - Source Code - Community - -ReactPy is a library for building user interfaces in Python without Javascript. ReactPy -interfaces are made from :ref:`components ` which look and behave -similarly to those found in `ReactJS `__. Designed with simplicity -in mind, ReactPy can be used by those without web development experience while also -being powerful enough to grow with your ambitions. - - -At a Glance ------------ - -To get a rough idea of how to write apps in ReactPy, take a look at the tiny `"hello world" -`__ application below: - -.. reactpy:: guides/getting-started/_examples/hello_world - -.. hint:: - - Try clicking the **🚀 result** tab to see what this displays! - -So what exactly does this code do? First, it imports a few tools from ``reactpy`` that will -get used to describe and execute an application. Then, we create an ``App`` function -which will define the content the application displays. Specifically, it displays a kind -of HTML element called an ``h1`` `section heading -`__. -Importantly though, a ``@component`` decorator has been applied to the ``App`` function -to turn it into a :ref:`component `. Finally, we :ref:`run -` a development web server by passing the ``App`` component to the -``run()`` function. - -.. note:: - - See :ref:`Running ReactPy in Production` to learn how to use a production-grade server - to run ReactPy. - - -Learning ReactPy ----------------- - -This documentation is broken up into chapters and sections that introduce you to -concepts step by step with detailed explanations and lots of examples. You should feel -free to dive into any content that seems interesting. While each chapter assumes -knowledge from those that came before, when you encounter a concept you're unfamiliar -with you should look for links that will help direct you to the place where it was -originally taught. - - -Chapter 1 - :ref:`Getting Started` ------------------------------------ - -If you want to follow along with examples in the sections that follow, you'll want to -start here so you can :ref:`install ReactPy `. This section also contains -more detailed information about how to :ref:`run ReactPy ` in different -contexts. For example, if you want to embed ReactPy into an existing application, or run -ReactPy within a Jupyter Notebook, this is where you can learn how to do those things. - -.. grid:: 1 2 2 2 - - .. grid-item:: - - .. image:: _static/install-and-run-reactpy.gif - - .. grid-item:: - - .. image:: guides/getting-started/_static/reactpy-in-jupyterlab.gif - -.. card:: - :link: guides/getting-started/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Install ReactPy and run it in a variety of different ways - with different web servers - and frameworks. You'll even embed ReactPy into an existing app. - - -Chapter 2 - :ref:`Creating Interfaces` --------------------------------------- - -ReactPy is a Python package for making user interfaces (UI). These interfaces are built -from small elements of functionality like buttons text and images. ReactPy allows you to -combine these elements into reusable :ref:`"components" `. In the -sections that follow you'll learn how these UI elements are created and organized into -components. Then, you'll use this knowledge to create interfaces from raw data: - -.. reactpy:: guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys - -.. card:: - :link: guides/creating-interfaces/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Learn to construct user interfaces from basic HTML elements and reusable components. - - -Chapter 3 - :ref:`Adding Interactivity` ---------------------------------------- - -Components often need to change what’s on the screen as a result of an interaction. For -example, typing into the form should update the input field, clicking a “Comment” button -should bring up a text input field, clicking “Buy” should put a product in the shopping -cart. Components need to “remember” things like the current input value, the current -image, the shopping cart. In ReactPy, this kind of component-specific memory is created and -updated with a "hook" called ``use_state()`` that creates a **state variable** and -**state setter** respectively: - -.. reactpy:: guides/adding-interactivity/components-with-state/_examples/adding_state_variable - -In ReactPy, ``use_state``, as well as any other function whose name starts with ``use``, is -called a "hook". These are special functions that should only be called while ReactPy is -:ref:`rendering `. They let you "hook into" the different -capabilities of ReactPy's components of which ``use_state`` is just one (well get into the -other :ref:`later `). - -.. card:: - :link: guides/adding-interactivity/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Learn how user interfaces can be made to respond to user interaction in real-time. - - -Chapter 4 - :ref:`Managing State` ---------------------------------- - -.. card:: - :link: guides/managing-state/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Under construction 🚧 - - - -Chapter 5 - :ref:`Escape Hatches` ---------------------------------- - -.. card:: - :link: guides/escape-hatches/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Under construction 🚧 - - -Chapter 6 - :ref:`Understanding ReactPy` ----------------------------------------- - -.. card:: - :link: guides/escape-hatches/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Under construction 🚧 - diff --git a/docs/source/reference/_examples/character_movement/main.py b/docs/source/reference/_examples/character_movement/main.py deleted file mode 100644 index 9545b0c0a..000000000 --- a/docs/source/reference/_examples/character_movement/main.py +++ /dev/null @@ -1,73 +0,0 @@ -from pathlib import Path -from typing import NamedTuple - -from reactpy import component, html, run, use_state -from reactpy.widgets import image - -HERE = Path(__file__) -CHARACTER_IMAGE = (HERE.parent / "static" / "bunny.png").read_bytes() - - -class Position(NamedTuple): - x: int - y: int - angle: int - - -def rotate(degrees): - return lambda old_position: Position( - old_position.x, - old_position.y, - old_position.angle + degrees, - ) - - -def translate(x=0, y=0): - return lambda old_position: Position( - old_position.x + x, - old_position.y + y, - old_position.angle, - ) - - -@component -def Scene(): - position, set_position = use_state(Position(100, 100, 0)) - - return html.div( - {"style": {"width": "225px"}}, - html.div( - { - "style": { - "width": "200px", - "height": "200px", - "background_color": "slategray", - } - }, - image( - "png", - CHARACTER_IMAGE, - { - "style": { - "position": "relative", - "left": f"{position.x}px", - "top": f"{position.y}.px", - "transform": f"rotate({position.angle}deg) scale(2, 2)", - } - }, - ), - ), - html.button( - {"on_click": lambda e: set_position(translate(x=-10))}, "Move Left" - ), - html.button( - {"on_click": lambda e: set_position(translate(x=10))}, "Move Right" - ), - html.button({"on_click": lambda e: set_position(translate(y=-10))}, "Move Up"), - html.button({"on_click": lambda e: set_position(translate(y=10))}, "Move Down"), - html.button({"on_click": lambda e: set_position(rotate(-30))}, "Rotate Left"), - html.button({"on_click": lambda e: set_position(rotate(30))}, "Rotate Right"), - ) - - -run(Scene) diff --git a/docs/source/reference/_examples/character_movement/static/bunny.png b/docs/source/reference/_examples/character_movement/static/bunny.png deleted file mode 100644 index ce1f989c5202ffe9ddabe03fabdc933c4aca1a71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 474 zcmV<00VV#4P)Px$lu1NER9J=WmOo3wKp2Og6Dfg~LaAV(ATF*>;vkL=3gX}>h?|?Zh#x{+T)RmZ zL2xLdjt=$%w6lYj20C0D2pO`)p-D;8w7p!ai}^v|^5^dN+`aF;7s!<9ze}immf0PG$dkBv329z^TFvKr^m{POIE zyZdVZ9v?pG*qtauth%k{s9;6kB?>`c*r?Y=?LgCJ-fcC#T;3p;HvlGflky5o(?CjK z6dn<31F*8Tj_iB|025NqfoLsg)N247ot`U!`VeAuV=EoN^VJ)NxBC;{*!>>Ga%JW? zcE2|PkR${G@N~?B9TO*I06)ouYnj;n9)uD~*E|tG7{n-5nR5J!Fj&MqHr(2~AQ*ge zdq)(@mAJ!d3}%{A{g4c#IRFIsgCw diff --git a/docs/source/reference/_examples/click_count.py b/docs/source/reference/_examples/click_count.py deleted file mode 100644 index 3ee2c89c5..000000000 --- a/docs/source/reference/_examples/click_count.py +++ /dev/null @@ -1,13 +0,0 @@ -import reactpy - - -@reactpy.component -def ClickCount(): - count, set_count = reactpy.hooks.use_state(0) - - return reactpy.html.button( - {"on_click": lambda event: set_count(count + 1)}, [f"Click count: {count}"] - ) - - -reactpy.run(ClickCount) diff --git a/docs/source/reference/_examples/material_ui_switch.py b/docs/source/reference/_examples/material_ui_switch.py deleted file mode 100644 index 704ae3145..000000000 --- a/docs/source/reference/_examples/material_ui_switch.py +++ /dev/null @@ -1,22 +0,0 @@ -import reactpy - -mui = reactpy.web.module_from_template("react", "@material-ui/core@^5.0", fallback="⌛") -Switch = reactpy.web.export(mui, "Switch") - - -@reactpy.component -def DayNightSwitch(): - checked, set_checked = reactpy.hooks.use_state(False) - - return reactpy.html.div( - Switch( - { - "checked": checked, - "onChange": lambda event, checked: set_checked(checked), - } - ), - "🌞" if checked else "🌚", - ) - - -reactpy.run(DayNightSwitch) diff --git a/docs/source/reference/_examples/matplotlib_plot.py b/docs/source/reference/_examples/matplotlib_plot.py deleted file mode 100644 index 5c4d616fe..000000000 --- a/docs/source/reference/_examples/matplotlib_plot.py +++ /dev/null @@ -1,85 +0,0 @@ -from io import BytesIO - -import matplotlib.pyplot as plt - -import reactpy -from reactpy.widgets import image - - -@reactpy.component -def PolynomialPlot(): - coefficients, set_coefficients = reactpy.hooks.use_state([0]) - - x = list(linspace(-1, 1, 50)) - y = [polynomial(value, coefficients) for value in x] - - return reactpy.html.div( - plot(f"{len(coefficients)} Term Polynomial", x, y), - ExpandableNumberInputs(coefficients, set_coefficients), - ) - - -@reactpy.component -def ExpandableNumberInputs(values, set_values): - inputs = [] - for i in range(len(values)): - - def set_value_at_index(event, index=i): - new_value = float(event["target"]["value"] or 0) - set_values(values[:index] + [new_value] + values[index + 1 :]) - - inputs.append(poly_coef_input(i + 1, set_value_at_index)) - - def add_input(): - set_values([*values, 0]) - - def del_input(): - set_values(values[:-1]) - - return reactpy.html.div( - reactpy.html.div( - "add/remove term:", - reactpy.html.button({"on_click": lambda event: add_input()}, "+"), - reactpy.html.button({"on_click": lambda event: del_input()}, "-"), - ), - inputs, - ) - - -def plot(title, x, y): - fig, axes = plt.subplots() - axes.plot(x, y) - axes.set_title(title) - buffer = BytesIO() - fig.savefig(buffer, format="png") - plt.close(fig) - return image("png", buffer.getvalue()) - - -def poly_coef_input(index, callback): - return reactpy.html.div( - {"style": {"margin-top": "5px"}, "key": index}, - reactpy.html.label( - "C", - reactpy.html.sub(index), - " x X", - reactpy.html.sup(index), - ), - reactpy.html.input({"type": "number", "on_change": callback}), - ) - - -def polynomial(x, coefficients): - return sum(c * (x ** (i + 1)) for i, c in enumerate(coefficients)) - - -def linspace(start, stop, n): - if n == 1: - yield stop - return - h = (stop - start) / (n - 1) - for i in range(n): - yield start + h * i - - -reactpy.run(PolynomialPlot) diff --git a/docs/source/reference/_examples/network_graph.py b/docs/source/reference/_examples/network_graph.py deleted file mode 100644 index 79b1092f3..000000000 --- a/docs/source/reference/_examples/network_graph.py +++ /dev/null @@ -1,40 +0,0 @@ -import random - -import reactpy - -react_cytoscapejs = reactpy.web.module_from_template( - "react", - "react-cytoscapejs", - fallback="⌛", -) -Cytoscape = reactpy.web.export(react_cytoscapejs, "default") - - -@reactpy.component -def RandomNetworkGraph(): - return Cytoscape( - { - "style": {"width": "100%", "height": "200px"}, - "elements": random_network(20), - "layout": {"name": "cose"}, - } - ) - - -def random_network(number_of_nodes): - conns = [] - nodes = [{"data": {"id": 0, "label": 0}}] - - for src_node_id in range(1, number_of_nodes + 1): - tgt_node = random.choice(nodes) - src_node = {"data": {"id": src_node_id, "label": src_node_id}} - - new_conn = {"data": {"source": src_node_id, "target": tgt_node["data"]["id"]}} - - nodes.append(src_node) - conns.append(new_conn) - - return nodes + conns - - -reactpy.run(RandomNetworkGraph) diff --git a/docs/source/reference/_examples/pigeon_maps.py b/docs/source/reference/_examples/pigeon_maps.py deleted file mode 100644 index 1ddf04fdc..000000000 --- a/docs/source/reference/_examples/pigeon_maps.py +++ /dev/null @@ -1,46 +0,0 @@ -import reactpy - -pigeon_maps = reactpy.web.module_from_template("react", "pigeon-maps", fallback="⌛") -Map, Marker = reactpy.web.export(pigeon_maps, ["Map", "Marker"]) - - -@reactpy.component -def MapWithMarkers(): - marker_anchor, add_marker_anchor, remove_marker_anchor = use_set() - - markers = [ - Marker( - { - "anchor": anchor, - "onClick": lambda event, a=anchor: remove_marker_anchor(a), - }, - key=str(anchor), - ) - for anchor in marker_anchor - ] - - return Map( - { - "defaultCenter": (37.774, -122.419), - "defaultZoom": 12, - "height": "300px", - "metaWheelZoom": True, - "onClick": lambda event: add_marker_anchor(tuple(event["latLng"])), - }, - markers, - ) - - -def use_set(initial_value=None): - values, set_values = reactpy.hooks.use_state(initial_value or set()) - - def add_value(lat_lon): - set_values(values.union({lat_lon})) - - def remove_value(lat_lon): - set_values(values.difference({lat_lon})) - - return values, add_value, remove_value - - -reactpy.run(MapWithMarkers) diff --git a/docs/source/reference/_examples/simple_dashboard.py b/docs/source/reference/_examples/simple_dashboard.py deleted file mode 100644 index 66913fc84..000000000 --- a/docs/source/reference/_examples/simple_dashboard.py +++ /dev/null @@ -1,102 +0,0 @@ -import asyncio -import random -import time - -import reactpy -from reactpy.widgets import Input - -victory = reactpy.web.module_from_template( - "react", - "victory-line", - fallback="⌛", - # not usually required (see issue #461 for more info) - unmount_before_update=True, -) -VictoryLine = reactpy.web.export(victory, "VictoryLine") - - -@reactpy.component -def RandomWalk(): - mu = reactpy.hooks.use_ref(0) - sigma = reactpy.hooks.use_ref(1) - - return reactpy.html.div( - RandomWalkGraph(mu, sigma), - reactpy.html.style( - """ - .number-input-container {margin-bottom: 20px} - .number-input-container input {width: 48%;float: left} - .number-input-container input + input {margin-left: 4%} - """ - ), - NumberInput( - "Mean", - mu.current, - mu.set_current, - (-1, 1, 0.01), - ), - NumberInput( - "Standard Deviation", - sigma.current, - sigma.set_current, - (0, 1, 0.01), - ), - ) - - -@reactpy.component -def RandomWalkGraph(mu, sigma): - interval = use_interval(0.5) - data, set_data = reactpy.hooks.use_state([{"x": 0, "y": 0}] * 50) - - @reactpy.hooks.use_effect - async def animate(): - await interval - last_data_point = data[-1] - next_data_point = { - "x": last_data_point["x"] + 1, - "y": last_data_point["y"] + random.gauss(mu.current, sigma.current), - } - set_data(data[1:] + [next_data_point]) - - return VictoryLine( - { - "data": data, - "style": { - "parent": {"width": "100%"}, - "data": {"stroke": "royalblue"}, - }, - } - ) - - -@reactpy.component -def NumberInput(label, value, set_value_callback, domain): - minimum, maximum, step = domain - attrs = {"min": minimum, "max": maximum, "step": step} - - value, set_value = reactpy.hooks.use_state(value) - - def update_value(value): - set_value(value) - set_value_callback(value) - - return reactpy.html.fieldset( - {"class_name": "number-input-container"}, - reactpy.html.legend({"style": {"font-size": "medium"}}, label), - Input(update_value, "number", value, attributes=attrs, cast=float), - Input(update_value, "range", value, attributes=attrs, cast=float), - ) - - -def use_interval(rate): - usage_time = reactpy.hooks.use_ref(time.time()) - - async def interval() -> None: - await asyncio.sleep(rate - (time.time() - usage_time.current)) - usage_time.current = time.time() - - return asyncio.ensure_future(interval()) - - -reactpy.run(RandomWalk) diff --git a/docs/source/reference/_examples/slideshow.py b/docs/source/reference/_examples/slideshow.py deleted file mode 100644 index b490b3feb..000000000 --- a/docs/source/reference/_examples/slideshow.py +++ /dev/null @@ -1,20 +0,0 @@ -import reactpy - - -@reactpy.component -def Slideshow(): - index, set_index = reactpy.hooks.use_state(0) - - def next_image(event): - set_index(index + 1) - - return reactpy.html.img( - { - "src": f"https://picsum.photos/id/{index}/800/300", - "style": {"cursor": "pointer"}, - "on_click": next_image, - } - ) - - -reactpy.run(Slideshow) diff --git a/docs/source/reference/_examples/snake_game.py b/docs/source/reference/_examples/snake_game.py deleted file mode 100644 index 36916410e..000000000 --- a/docs/source/reference/_examples/snake_game.py +++ /dev/null @@ -1,188 +0,0 @@ -import asyncio -import enum -import random -import time - -import reactpy - - -class GameState(enum.Enum): - init = 0 - lost = 1 - won = 2 - play = 3 - - -@reactpy.component -def GameView(): - game_state, set_game_state = reactpy.hooks.use_state(GameState.init) - - if game_state == GameState.play: - return GameLoop(grid_size=6, block_scale=50, set_game_state=set_game_state) - - start_button = reactpy.html.button( - {"on_click": lambda event: set_game_state(GameState.play)}, "Start" - ) - - if game_state == GameState.won: - menu = reactpy.html.div(reactpy.html.h3("You won!"), start_button) - elif game_state == GameState.lost: - menu = reactpy.html.div(reactpy.html.h3("You lost"), start_button) - else: - menu = reactpy.html.div(reactpy.html.h3("Click to play"), start_button) - - menu_style = reactpy.html.style( - """ - .snake-game-menu h3 { - margin-top: 0px !important; - } - """ - ) - - return reactpy.html.div({"class_name": "snake-game-menu"}, menu_style, menu) - - -class Direction(enum.Enum): - ArrowUp = (0, -1) - ArrowLeft = (-1, 0) - ArrowDown = (0, 1) - ArrowRight = (1, 0) - - -@reactpy.component -def GameLoop(grid_size, block_scale, set_game_state): - # we `use_ref` here to capture the latest direction press without any delay - direction = reactpy.hooks.use_ref(Direction.ArrowRight.value) - # capture the last direction of travel that was rendered - last_direction = direction.current - - snake, set_snake = reactpy.hooks.use_state( - [(grid_size // 2 - 1, grid_size // 2 - 1)] - ) - food, set_food = use_snake_food(grid_size, snake) - - grid = create_grid(grid_size, block_scale) - - @reactpy.event(prevent_default=True) - def on_direction_change(event): - if hasattr(Direction, event["key"]): - maybe_new_direction = Direction[event["key"]].value - direction_vector_sum = tuple( - map(sum, zip(last_direction, maybe_new_direction)) - ) - if direction_vector_sum != (0, 0): - direction.current = maybe_new_direction - - grid_wrapper = reactpy.html.div({"on_key_down": on_direction_change}, grid) - - assign_grid_block_color(grid, food, "blue") - - for location in snake: - assign_grid_block_color(grid, location, "white") - - new_game_state = None - if snake[-1] in snake[:-1]: - assign_grid_block_color(grid, snake[-1], "red") - new_game_state = GameState.lost - elif len(snake) == grid_size**2: - assign_grid_block_color(grid, snake[-1], "yellow") - new_game_state = GameState.won - - interval = use_interval(0.5) - - @reactpy.hooks.use_effect - async def animate(): - if new_game_state is not None: - await asyncio.sleep(1) - set_game_state(new_game_state) - return - - await interval - - new_snake_head = ( - # grid wraps due to mod op here - (snake[-1][0] + direction.current[0]) % grid_size, - (snake[-1][1] + direction.current[1]) % grid_size, - ) - - if snake[-1] == food: - set_food() - new_snake = [*snake, new_snake_head] - else: - new_snake = snake[1:] + [new_snake_head] - - set_snake(new_snake) - - return grid_wrapper - - -def use_snake_food(grid_size, current_snake): - grid_points = {(x, y) for x in range(grid_size) for y in range(grid_size)} - points_not_in_snake = grid_points.difference(current_snake) - - food, _set_food = reactpy.hooks.use_state(current_snake[-1]) - - def set_food(): - _set_food(random.choice(list(points_not_in_snake))) - - return food, set_food - - -def use_interval(rate): - usage_time = reactpy.hooks.use_ref(time.time()) - - async def interval() -> None: - await asyncio.sleep(rate - (time.time() - usage_time.current)) - usage_time.current = time.time() - - return asyncio.ensure_future(interval()) - - -def create_grid(grid_size, block_scale): - return reactpy.html.div( - { - "style": { - "height": f"{block_scale * grid_size}px", - "width": f"{block_scale * grid_size}px", - "cursor": "pointer", - "display": "grid", - "grid-gap": 0, - "grid-template-columns": f"repeat({grid_size}, {block_scale}px)", - "grid-template-rows": f"repeat({grid_size}, {block_scale}px)", - }, - "tab_index": -1, - }, - [ - reactpy.html.div( - {"style": {"height": f"{block_scale}px"}, "key": i}, - [ - create_grid_block("black", block_scale, key=i) - for i in range(grid_size) - ], - ) - for i in range(grid_size) - ], - ) - - -def create_grid_block(color, block_scale, key): - return reactpy.html.div( - { - "style": { - "height": f"{block_scale}px", - "width": f"{block_scale}px", - "background_color": color, - "outline": "1px solid grey", - }, - "key": key, - } - ) - - -def assign_grid_block_color(grid, point, color): - x, y = point - block = grid["children"][x]["children"][y] - block["attributes"]["style"]["backgroundColor"] = color - - -reactpy.run(GameView) diff --git a/docs/source/reference/_examples/todo.py b/docs/source/reference/_examples/todo.py deleted file mode 100644 index 104ea59a9..000000000 --- a/docs/source/reference/_examples/todo.py +++ /dev/null @@ -1,35 +0,0 @@ -import reactpy - - -@reactpy.component -def Todo(): - items, set_items = reactpy.hooks.use_state([]) - - async def add_new_task(event): - if event["key"] == "Enter": - set_items([*items, event["target"]["value"]]) - - tasks = [] - - for index, text in enumerate(items): - - async def remove_task(event, index=index): - set_items(items[:index] + items[index + 1 :]) - - task_text = reactpy.html.td(reactpy.html.p(text)) - delete_button = reactpy.html.td( - {"on_click": remove_task}, reactpy.html.button(["x"]) - ) - tasks.append(reactpy.html.tr(task_text, delete_button)) - - task_input = reactpy.html.input({"on_key_down": add_new_task}) - task_table = reactpy.html.table(tasks) - - return reactpy.html.div( - reactpy.html.p("press enter to add a task:"), - task_input, - task_table, - ) - - -reactpy.run(Todo) diff --git a/docs/source/reference/_examples/use_reducer_counter.py b/docs/source/reference/_examples/use_reducer_counter.py deleted file mode 100644 index 6f9581dfd..000000000 --- a/docs/source/reference/_examples/use_reducer_counter.py +++ /dev/null @@ -1,27 +0,0 @@ -import reactpy - - -def reducer(count, action): - if action == "increment": - return count + 1 - elif action == "decrement": - return count - 1 - elif action == "reset": - return 0 - else: - msg = f"Unknown action '{action}'" - raise ValueError(msg) - - -@reactpy.component -def Counter(): - count, dispatch = reactpy.hooks.use_reducer(reducer, 0) - return reactpy.html.div( - f"Count: {count}", - reactpy.html.button({"on_click": lambda event: dispatch("reset")}, "Reset"), - reactpy.html.button({"on_click": lambda event: dispatch("increment")}, "+"), - reactpy.html.button({"on_click": lambda event: dispatch("decrement")}, "-"), - ) - - -reactpy.run(Counter) diff --git a/docs/source/reference/_examples/use_state_counter.py b/docs/source/reference/_examples/use_state_counter.py deleted file mode 100644 index b2d8c84a9..000000000 --- a/docs/source/reference/_examples/use_state_counter.py +++ /dev/null @@ -1,26 +0,0 @@ -import reactpy - - -def increment(last_count): - return last_count + 1 - - -def decrement(last_count): - return last_count - 1 - - -@reactpy.component -def Counter(): - initial_count = 0 - count, set_count = reactpy.hooks.use_state(initial_count) - return reactpy.html.div( - f"Count: {count}", - reactpy.html.button( - {"on_click": lambda event: set_count(initial_count)}, "Reset" - ), - reactpy.html.button({"on_click": lambda event: set_count(increment)}, "+"), - reactpy.html.button({"on_click": lambda event: set_count(decrement)}, "-"), - ) - - -reactpy.run(Counter) diff --git a/docs/source/reference/_examples/victory_chart.py b/docs/source/reference/_examples/victory_chart.py deleted file mode 100644 index ce37c522f..000000000 --- a/docs/source/reference/_examples/victory_chart.py +++ /dev/null @@ -1,7 +0,0 @@ -import reactpy - -victory = reactpy.web.module_from_template("react", "victory-bar", fallback="⌛") -VictoryBar = reactpy.web.export(victory, "VictoryBar") - -bar_style = {"parent": {"width": "500px"}, "data": {"fill": "royalblue"}} -reactpy.run(reactpy.component(lambda: VictoryBar({"style": bar_style}))) diff --git a/docs/source/reference/_static/vdom-json-schema.json b/docs/source/reference/_static/vdom-json-schema.json deleted file mode 100644 index b1005d2ed..000000000 --- a/docs/source/reference/_static/vdom-json-schema.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "$ref": "#/definitions/element", - "$schema": "http://json-schema.org/draft-07/schema", - "definitions": { - "element": { - "dependentSchemas": { - "error": { - "properties": { - "tagName": { - "maxLength": 0 - } - } - } - }, - "properties": { - "attributes": { - "type": "object" - }, - "children": { - "$ref": "#/definitions/elementChildren" - }, - "error": { - "type": "string" - }, - "eventHandlers": { - "$ref": "#/definitions/elementEventHandlers" - }, - "importSource": { - "$ref": "#/definitions/importSource" - }, - "key": { - "type": "string" - }, - "tagName": { - "type": "string" - } - }, - "required": ["tagName"], - "type": "object" - }, - "elementChildren": { - "items": { - "$ref": "#/definitions/elementOrString" - }, - "type": "array" - }, - "elementEventHandlers": { - "patternProperties": { - ".*": { - "$ref": "#/definitions/eventHander" - } - }, - "type": "object" - }, - "elementOrString": { - "if": { - "type": "object" - }, - "then": { - "$ref": "#/definitions/element" - }, - "type": ["object", "string"] - }, - "eventHandler": { - "properties": { - "preventDefault": { - "type": "boolean" - }, - "stopPropagation": { - "type": "boolean" - }, - "target": { - "type": "string" - } - }, - "required": ["target"], - "type": "object" - }, - "importSource": { - "properties": { - "fallback": { - "if": { - "not": { - "type": "null" - } - }, - "then": { - "$ref": "#/definitions/elementOrString" - }, - "type": ["object", "string", "null"] - }, - "source": { - "type": "string" - }, - "sourceType": { - "enum": ["URL", "NAME"] - }, - "unmountBeforeUpdate": { - "type": "boolean" - } - }, - "required": ["source"], - "type": "object" - } - } -} diff --git a/docs/source/reference/browser-events.rst b/docs/source/reference/browser-events.rst deleted file mode 100644 index 632be410a..000000000 --- a/docs/source/reference/browser-events.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. _Browser Events: - -Browser Events 🚧 -================= - -The event types below are triggered by an event in the bubbling phase. To register an -event handler for the capture phase, append Capture to the event name; for example, -instead of using ``onClick``, you would use ``onClickCapture`` to handle the click event -in the capture phase. - -.. note:: - - Under construction 🚧 - - -Clipboard Events ----------------- - -Composition Events ------------------- - -Keyboard Events ---------------- - -Focus Events ------------- - -Form Events ------------ - -Generic Events --------------- - -Mouse Events ------------- - -Pointer Events --------------- - -Selection Events ----------------- - -Touch Events ------------- - -UI Events ---------- - -Wheel Events ------------- - -Media Events ------------- - -Image Events ------------- - -Animation Events ----------------- - -Transition Events ------------------ - -Other Events ------------- diff --git a/docs/source/reference/hooks-api.rst b/docs/source/reference/hooks-api.rst deleted file mode 100644 index ca8123e85..000000000 --- a/docs/source/reference/hooks-api.rst +++ /dev/null @@ -1,379 +0,0 @@ -========= -Hooks API -========= - -Hooks are functions that allow you to "hook into" the life cycle events and state of -Components. Their usage should always follow the :ref:`Rules of Hooks`. For most use -cases the :ref:`Basic Hooks` should be enough, however the remaining -:ref:`Supplementary Hooks` should fulfill less common scenarios. - - -Basic Hooks -=========== - - -Use State ---------- - -.. code-block:: - - state, set_state = use_state(initial_state) - -Returns a stateful value and a function to update it. - -During the first render the ``state`` will be identical to the ``initial_state`` passed -as the first argument. However in subsequent renders ``state`` will take on the value -passed to ``set_state``. - -.. code-block:: - - set_state(new_state) - -The ``set_state`` function accepts a ``new_state`` as its only argument and schedules a -re-render of the component where ``use_state`` was initially called. During these -subsequent re-renders the ``state`` returned by ``use_state`` will take on the value -of ``new_state``. - -.. note:: - - The identity of ``set_state`` is guaranteed to be preserved across renders. This - means it can safely be omitted from dependency lists in :ref:`Use Effect` or - :ref:`Use Callback`. - - -Functional Updates -.................. - -If the new state is computed from the previous state, you can pass a function which -accepts a single argument (the previous state) and returns the next state. Consider this -simply use case of a counter where we've pulled out logic for increment and -decremented the count: - -.. reactpy:: _examples/use_state_counter - -We use the functional form for the "+" and "-" buttons since the next ``count`` depends -on the previous value, while for the "Reset" button we simple assign the -``initial_count`` since it is independent of the prior ``count``. This is a trivial -example, but it demonstrates how complex state logic can be factored out into well -defined and potentially reusable functions. - - -Lazy Initial State -.................. - -In cases where it is costly to create the initial value for ``use_state``, you can pass -a constructor function that accepts no arguments instead - it will be called on the -first render of a component, but will be disregarded in all following renders: - -.. code-block:: - - state, set_state = use_state(lambda: some_expensive_computation(a, b, c)) - - -Skipping Updates -................ - -If you update a State Hook to the same value as the current state then the component which -owns that state will not be rendered again. We check ``if new_state is current_state`` -in order to determine whether there has been a change. Thus the following would not -result in a re-render: - -.. code-block:: - - state, set_state = use_state([]) - set_state(state) - - -Use Effect ----------- - -.. code-block:: - - use_effect(did_render) - -The ``use_effect`` hook accepts a function which may be imperative, or mutate state. The -function will be called immediately after the layout has fully updated. - -Asynchronous actions, mutations, subscriptions, and other `side effects`_ can cause -unexpected bugs if placed in the main body of a component's render function. Thus the -``use_effect`` hook provides a way to safely escape the purely functional world of -component render functions. - -.. note:: - - Normally in React the ``did_render`` function is called once an update has been - committed to the screen. Since no such action is performed by ReactPy, and the time - at which the update is displayed cannot be known we are unable to achieve parity - with this behavior. - - -Cleaning Up Effects -................... - -If the effect you wish to enact creates resources, you'll probably need to clean them -up. In such cases you may simply return a function that addresses this from the -``did_render`` function which created the resource. Consider the case of opening and -then closing a connection: - -.. code-block:: - - def establish_connection(): - connection = open_connection() - return lambda: close_connection(connection) - - use_effect(establish_connection) - -The clean-up function will be run before the component is unmounted or, before the next -effect is triggered when the component re-renders. You can -:ref:`conditionally fire events ` to avoid triggering them each -time a component renders. - - -Conditional Effects -................... - -By default, effects are triggered after every successful render to ensure that all state -referenced by the effect is up to date. However, when an effect function references -non-global variables, the effect will only if the value of that variable changes. For -example, imagine that we had an effect that connected to a ``url`` state variable: - -.. code-block:: - - url, set_url = use_state("https://example.com") - - def establish_connection(): - connection = open_connection(url) - return lambda: close_connection(connection) - - use_effect(establish_connection) - -Here, a new connection will be established whenever a new ``url`` is set. - - -Async Effects -............. - -A behavior unique to ReactPy's implementation of ``use_effect`` is that it natively -supports ``async`` functions: - -.. code-block:: - - async def non_blocking_effect(): - resource = await do_something_asynchronously() - return lambda: blocking_close(resource) - - use_effect(non_blocking_effect) - - -There are **three important subtleties** to note about using asynchronous effects: - -1. The cleanup function must be a normal synchronous function. - -2. Asynchronous effects which do not complete before the next effect is created - following a re-render will be cancelled. This means an - :class:`~asyncio.CancelledError` will be raised somewhere in the body of the effect. - -3. An asynchronous effect may occur any time after the update which added this effect - and before the next effect following a subsequent update. - - -Manual Effect Conditions -........................ - -In some cases, you may want to explicitly declare when an effect should be triggered. -You can do this by passing ``dependencies`` to ``use_effect``. Each of the following -values produce different effect behaviors: - -- ``use_effect(..., dependencies=None)`` - triggers and cleans up on every render. -- ``use_effect(..., dependencies=[])`` - only triggers on the first and cleans up after - the last render. -- ``use_effect(..., dependencies=[x, y])`` - triggers on the first render and on subsequent renders if - ``x`` or ``y`` have changed. - - -Use Context ------------ - -.. code-block:: - - value = use_context(MyContext) - -Accepts a context object (the value returned from -:func:`reactpy.core.hooks.create_context`) and returns the current context value for that -context. The current context value is determined by the ``value`` argument passed to the -nearest ``MyContext`` in the tree. - -When the nearest above the component updates, this Hook will -trigger a rerender with the latest context value passed to that MyContext provider. Even -if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen -starting at the component itself using useContext. - - -Supplementary Hooks -=================== - - -Use Reducer ------------ - -.. code-block:: - - state, dispatch_action = use_reducer(reducer, initial_state) - -An alternative and derivative of :ref:`Use State` the ``use_reducer`` hook, instead of -directly assigning a new state, allows you to specify an action which will transition -the previous state into the next state. This transition is defined by a reducer function -of the form ``(current_state, action) -> new_state``. The ``use_reducer`` hook then -returns the current state and a ``dispatch_action`` function that accepts an ``action`` -and causes a transition to the next state via the ``reducer``. - -``use_reducer`` is generally preferred to ``use_state`` if logic for transitioning from -one state to the next is especially complex or involves nested data structures. -``use_reducer`` can also be used to collect several ``use_state`` calls together - this -may be slightly more performant as well as being preferable since there is only one -``dispatch_action`` callback versus the many ``set_state`` callbacks. - -We can rework the :ref:`Functional Updates` counter example to use ``use_reducer``: - -.. reactpy:: _examples/use_reducer_counter - -.. note:: - - The identity of the ``dispatch_action`` function is guaranteed to be preserved - across re-renders throughout the lifetime of the component. This means it can safely - be omitted from dependency lists in :ref:`Use Effect` or :ref:`Use Callback`. - - -Use Callback ------------- - -.. code-block:: - - memoized_callback = use_callback(lambda: do_something(a, b)) - -A derivative of :ref:`Use Memo`, the ``use_callback`` hook returns a -`memoized `_ callback. This is useful when passing callbacks to child -components which check reference equality to prevent unnecessary renders. The -``memoized_callback`` will only change when any local variables is references do. - -.. note:: - - You may manually specify what values the callback depends on in the :ref:`same way - as effects ` using the ``dependencies`` parameter. - - -Use Memo --------- - -.. code-block:: - - memoized_value = use_memo(lambda: compute_something_expensive(a, b)) - -Returns a `memoized `_ value. By passing a constructor function accepting -no arguments and an array of dependencies for that constructor, the ``use_callback`` -hook will return the value computed by the constructor. The ``memoized_value`` will only -be recomputed if a local variable referenced by the constructor changes (e.g. ``a`` or -``b`` here). This optimizes performance because you don't need to -``compute_something_expensive`` on every render. - -Unlike ``use_effect`` the constructor function is called during each render (instead of -after) and should not incur side effects. - -.. warning:: - - Remember that you shouldn't optimize something unless you know it's a performance - bottleneck. Write your code without ``use_memo`` first and then add it to targeted - sections that need a speed-up. - -.. note:: - - You may manually specify what values the callback depends on in the :ref:`same way - as effects ` using the ``dependencies`` parameter. - - -Use Ref -------- - -.. code-block:: - - ref_container = use_ref(initial_value) - -Returns a mutable :class:`~reactpy.utils.Ref` object that has a single -:attr:`~reactpy.utils.Ref.current` attribute that at first contains the ``initial_state``. -The identity of the ``Ref`` object will be preserved for the lifetime of the component. - -A ``Ref`` is most useful if you need to incur side effects since updating its -``.current`` attribute doesn't trigger a re-render of the component. You'll often use this -hook alongside :ref:`Use Effect` or in response to component event handlers. - - -.. links -.. ===== - -.. _React Hooks: https://reactjs.org/docs/hooks-reference.html -.. _side effects: https://en.wikipedia.org/wiki/Side_effect_(computer_science) -.. _memoization: https://en.wikipedia.org/wiki/Memoization - - -Rules of Hooks -============== - -Hooks are just normal Python functions, but there's a bit of magic to them, and in order -for that magic to work you've got to follow two rules. Thankfully we supply a -:ref:`Flake8 Plugin` to help enforce them. - - -Only call outside flow controls -------------------------------- - -**Don't call hooks inside loops, conditions, or nested functions.** Instead you must -always call hooks at the top level of your functions. By adhering to this rule you -ensure that hooks are always called in the exact same order. This fact is what allows -ReactPy to preserve the state of hooks between multiple calls to ``useState`` and -``useEffect`` calls. - - -Only call in render functions ------------------------------ - -**Don't call hooks from regular Python functions.** Instead you should: - -- ✅ Call Hooks from a component's render function. - -- ✅ Call Hooks from another custom hook - -Following this rule ensures stateful logic for ReactPy component is always clearly -separated from the rest of your codebase. - - -Flake8 Plugin -------------- - -We provide a Flake8 plugin called `flake8-reactpy-hooks `_ that helps -to enforce the two rules described above. You can ``pip`` install it directly, or with -the ``lint`` extra for ReactPy: - -.. code-block:: bash - - pip install flake8-reactpy-hooks - -Once installed running, ``flake8`` on your code will start catching errors. For example: - -.. code-block:: bash - - flake8 my_reactpy_components.py - -Might produce something like the following output: - -.. code-block:: text - - ./my_reactpy_components:10:8 ROH102 hook 'use_effect' used inside if statement - ./my_reactpy_components:23:4 ROH102 hook 'use_state' used outside component or hook definition - -See the Flake8 docs for -`more info `__. - -.. links -.. ===== - -.. _Flake8 Linter Plugin: https://github.com/reactive-python/flake8-reactpy-hooks diff --git a/docs/source/reference/html-attributes.rst b/docs/source/reference/html-attributes.rst deleted file mode 100644 index 91813c355..000000000 --- a/docs/source/reference/html-attributes.rst +++ /dev/null @@ -1,197 +0,0 @@ -.. testcode:: - - from reactpy import html - - -HTML Attributes -=============== - -In ReactPy, HTML attributes are specified using snake_case instead of dash-separated -words. For example, ``tabindex`` and ``margin-left`` become ``tab_index`` and -``margin_left`` respectively. - - -Notable Attributes -------------------- - -Some attributes in ReactPy are renamed, have special meaning, or are used differently -than in HTML. - -``style`` -......... - -As mentioned above, instead of using a string to specify the ``style`` attribute, we use -a dictionary to describe the CSS properties we want to apply to an element. For example, -the following HTML: - -.. code-block:: html - -
-

My Todo List

-
    -
  • Build a cool new app
  • -
  • Share it with the world!
  • -
-
- -Would be written in ReactPy as: - -.. testcode:: - - html.div( - { - "style": { - "width": "50%", - "margin_left": "25%", - }, - }, - html.h1( - { - "style": { - "margin_top": "0px", - }, - }, - "My Todo List", - ), - html.ul( - html.li("Build a cool new app"), - html.li("Share it with the world!"), - ), - ) - -``class`` vs ``class_name`` -........................... - -In HTML, the ``class`` attribute is used to specify a CSS class for an element. In -ReactPy, this attribute is renamed to ``class_name`` to avoid conflicting with the -``class`` keyword in Python. For example, the following HTML: - -.. code-block:: html - -
-

My Todo List

-
    -
  • Build a cool new app
  • -
  • Share it with the world!
  • -
-
- -Would be written in ReactPy as: - -.. testcode:: - - html.div( - {"class_name": "container"}, - html.h1({"class_name": "title"}, "My Todo List"), - html.ul( - {"class_name": "list"}, - html.li({"class_name": "item"}, "Build a cool new app"), - html.li({"class_name": "item"}, "Share it with the world!"), - ), - ) - -``for`` vs ``html_for`` -....................... - -In HTML, the ``for`` attribute is used to specify the ``id`` of the element it's -associated with. In ReactPy, this attribute is renamed to ``html_for`` to avoid -conflicting with the ``for`` keyword in Python. For example, the following HTML: - -.. code-block:: html - -
- - -
- -Would be written in ReactPy as: - -.. testcode:: - - html.div( - html.label({"html_for": "todo"}, "Todo:"), - html.input({"id": "todo", "type": "text"}), - ) - -``dangerously_set_inner_HTML`` -.............................. - -This is used to set the ``innerHTML`` property of an element and should be provided a -dictionary with a single key ``__html`` whose value is the HTML to be set. It should be -used with **extreme caution** as it can lead to XSS attacks if the HTML inside isn't -trusted (for example if it comes from user input). - - -All Attributes --------------- - -`access_key `__ - A string. Specifies a keyboard shortcut for the element. Not generally recommended. - -`aria_* `__ - ARIA attributes let you specify the accessibility tree information for this element. - See ARIA attributes for a complete reference. In ReactPy, all ARIA attribute names are - exactly the same as in HTML. - -`auto_capitalize `__ - A string. Specifies whether and how the user input should be capitalized. - -`content_editable `__ - A boolean. If true, the browser lets the user edit the rendered element directly. This - is used to implement rich text input libraries like Lexical. ReactPy warns if you try - to pass children to an element with ``content_editable = True`` because ReactPy will - not be able to update its content after user edits. - -`data_* `__ - Data attributes let you attach some string data to the element, for example - data-fruit="banana". In ReactPy, they are not commonly used because you would usually - read data from props or state instead. - -`dir `__ - Either ``"ltr"`` or ``"rtl"``. Specifies the text direction of the element. - -`draggable `__ - A boolean. Specifies whether the element is draggable. Part of HTML Drag and Drop API. - -`enter_key_hint `__ - A string. Specifies which action to present for the enter key on virtual keyboards. - -`hidden

Then run the examples themselves and open the built page in a browser. mkdocs build --strict catches broken internal links and unresolvable includes — both abort the build with a nonzero exit. Two things it does not catch: a page missing from nav: (an INFO line, exit 0), and a link whose #anchor does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing.

You are the only gate. The single workflow on new-docs runs mkdocs gh-deploy --force on push, and gh-deploy does not pass --strict — a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live.

Pitfall

Do not trust a component that renders without error. Set REACTPY_DEBUG=1 when probing: with debug off, a component that raised produces {"tagName": "", "error": ""} — a blank region and a 200 response. See Running Tests for the harness traps that make a broken example look green.

Process

Branch from new-docs, one page per branch, and use the same Conventional Commit subjects as the history there — docs(learn): author X for ReactPy v2. A commit that removes a 🚧 badge from nav: should be the same commit that authored the page, never a separate "mark done" commit that could be cherry-picked alone.

No CHANGELOG.md entry: documentation work is not user-facing in the sense the changelog means. See Changelog.

Note

docs/src/dictionary.txt exists for the spellcheck plugin, which is currently commented out in mkdocs.yml. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off.

Recap

  • A page is done when it runs and every claim is verified — then remove its 🚧 badge.
  • Port, do not rename: delete JSX, non-existent hooks, StrictMode, memo, && rendering, and React's dependency-array semantics.
  • Fixed structure: Overview, intro paragraph, You will learn, sections, Recap, Related.
  • Relative .md links; admonitions instead of ReactJS's JSX tags.
  • Snippets are includes from docs/examples/, cropped at # start, formatted at 88 columns.
  • --strict checks links and nothing about behaviour. Run the example.
  • Running Tests — the exact commands and how to read their failures
  • Contributing Code — branch and pull request conventions
  • Changelog — what does and does not deserve an entry
  • Community — where to ask about a page before rewriting it
``: - -.. code-block:: python - - { - "tagName": "div", - "children": [ - "To perform an action", - ... - ], - "attributes": {}, - "eventHandlers": {} - } - -.. note:: - - As we move though our conversation we'll be using ``...`` to fill in places that we - haven't converted yet. - -In this simple case, all we've done is take the name of the HTML element (``div`` in -this case) and inserted it into the ``tagName`` field of a dictionary. Then we've taken -the inner HTML and added to a list of children where the text ``"to perform an action"`` -has been made into a string, and the inner ``input`` (yet to be converted) will be -expanded out into its own VDOM representation. Since the outer ``div`` is pretty simple -there aren't any ``attributes`` or ``eventHandlers``. - -No we come to the inner ``input``. If we expand this out now we'll get the following: - -.. code-block:: python - - { - "tagName": "div", - "children": [ - "To perform an action", - { - "tagName": "input", - "children": [], - "attributes": { - "type": "text", - "minLength": 4, - "maxLength": 8 - }, - "eventHandlers": ... - } - ], - "attributes": {}, - "eventHandlers": {} - } - -Here we've had to add some attributes to our VDOM. Take note of the differing -capitalization - instead of using all lowercase (an HTML convention) we've used -`camelCase `_ which is very common -in JavaScript. - -Last, but not least we come to the ``eventHandlers`` for the ``input``: - -.. code-block:: python - - { - "tagName": "div", - "children": [ - "To perform an action", - { - "tagName": "input", - "children": [], - "attributes": { - "type": "text", - "minLength": 4, - "maxLength": 8 - }, - "eventHandlers": { - "onChange": { - "target": "unique-id-of-a_python_callback", - "preventDefault": False, - "stopPropagation": False - } - } - } - ], - "attributes": {}, - "eventHandlers": {} - } - -Again we've changed the all lowercase ``onchange`` into a cameCase ``onChange`` event -type name. The various properties for the ``onChange`` handler are: - -- ``target``: the unique ID for a Python callback that exists in the backend. - -- ``preventDefault``: Stop the event's default action. More info - `here `__. - -- ``stopPropagation``: prevent the event from bubbling up through the DOM. More info - `here `__. - - -VDOM JSON Schema -................ - -To clearly describe the VDOM spec we've created a `JSON Schema `_: - -.. literalinclude:: _static/vdom-json-schema.json - :language: json - - -JSON Patch ----------- - -Updates to VDOM modules are sent using the `JSON Patch`_ specification. - -... this section is still Under construction 🚧 - - -.. Links -.. ===== -.. _JSON Patch: http://jsonpatch.com/ diff --git a/docs/src/about/changelog.md b/docs/src/about/changelog.md new file mode 100644 index 000000000..40c649d3e --- /dev/null +++ b/docs/src/about/changelog.md @@ -0,0 +1,11 @@ +--- +title: Changelog +hide: + - toc +--- + +!!! note "Attribution" + + {% include-markdown "../../../CHANGELOG.md" start="" end="" %} + +{% include-markdown "../../../CHANGELOG.md" start="" %} diff --git a/docs/src/about/code.md b/docs/src/about/code.md new file mode 100644 index 000000000..b62ac3725 --- /dev/null +++ b/docs/src/about/code.md @@ -0,0 +1,3 @@ +--- +title: Contributing Code 🚧 +--- diff --git a/docs/src/about/community.md b/docs/src/about/community.md new file mode 100644 index 000000000..a2aaf44d8 --- /dev/null +++ b/docs/src/about/community.md @@ -0,0 +1,3 @@ +--- +title: Community 🚧 +--- diff --git a/docs/src/about/docs.md b/docs/src/about/docs.md new file mode 100644 index 000000000..cf935525e --- /dev/null +++ b/docs/src/about/docs.md @@ -0,0 +1,3 @@ +--- +title: Contributing Documentation 🚧 +--- diff --git a/docs/src/about/running-tests.md b/docs/src/about/running-tests.md new file mode 100644 index 000000000..714a7456c --- /dev/null +++ b/docs/src/about/running-tests.md @@ -0,0 +1,3 @@ +--- +title: Running Tests 🚧 +--- diff --git a/docs/src/dictionary.txt b/docs/src/dictionary.txt new file mode 100644 index 000000000..af891a7e5 --- /dev/null +++ b/docs/src/dictionary.txt @@ -0,0 +1,28 @@ +django +nox +websocket +websockets +changelog +async +pre +prefetch +prefetching +preloader +whitespace +refetch +refetched +refetching +html +jupyter +webserver +iframe +keyworded +stylesheet +stylesheets +unstyled +py +idom +reactpy +asgi +postfixed +postprocessing diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 000000000..ae43c6a82 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,57 @@ +--- +hide: + - navigation + - toc +--- + +[ReactPy](https://reactpy.dev/) is a library for building user interfaces in Python without Javascript. ReactPy interfaces are made from components that look and behave similar to those found in [ReactJS](https://reactjs.org/). Designed with simplicity in mind, ReactPy can be used by those without web development experience while also being powerful enough to grow with your ambitions. + + + + + + + + + + + + + + + + +
Supported Backends
Built-inExternal
+ + Flask, FastAPI, Sanic, Tornado + + + Django, + Jupyter, + Plotly-Dash +
+ +# At a Glance + +To get a rough idea of how to write apps in ReactPy, take a look at this tiny _Hello World_ application. + +```python +from reactpy import component, html, run + +@component +def hello_world(): + return html.h1("Hello, World!") + +run(hello_world) +``` + +# Resources + +Follow the links below to find out more about this project. + +- [Try ReactPy (Jupyter Notebook)](https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb) +- [Documentation](https://reactpy.dev/) +- [GitHub Discussions](https://github.com/reactive-python/reactpy/discussions) +- [Discord](https://discord.gg/uNb5P4hA9X) +- [Contributor Guide](https://reactpy.dev/docs/about/contributor-guide.html) +- [Code of Conduct](https://github.com/reactive-python/reactpy/blob/main/CODE_OF_CONDUCT.md) diff --git a/docs/src/learn/add-react-to-an-existing-project.md b/docs/src/learn/add-react-to-an-existing-project.md new file mode 100644 index 000000000..73a3b0bc1 --- /dev/null +++ b/docs/src/learn/add-react-to-an-existing-project.md @@ -0,0 +1,153 @@ +--- +title: Add React to an Existing Project 🚧 +--- + +## Overview + +

+ +If you want to add some interactivity to your existing project, you don't have to rewrite it in React. Add React to your existing stack, and render interactive React components anywhere. + +

+ + + +**You need to install [Node.js](https://nodejs.org/en/) for local development.** Although you can [try React](/learn/installation#try-react) online or with a simple HTML page, realistically most JavaScript tooling you'll want to use for development requires Node.js. + + + +## Using React for an entire subroute of your existing website + +Let's say you have an existing web app at `example.com` built with another server technology (like Rails), and you want to implement all routes starting with `example.com/some-app/` fully with React. + +Here's how we recommend to set it up: + +1. **Build the React part of your app** using one of the [React-based frameworks](/learn/start-a-new-react-project). +2. **Specify `/some-app` as the _base path_** in your framework's configuration (here's how: [Next.js](https://nextjs.org/docs/api-reference/next.config.js/basepath), [Gatsby](https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/path-prefix/)). +3. **Configure your server or a proxy** so that all requests under `/some-app/` are handled by your React app. + +This ensures the React part of your app can [benefit from the best practices](/learn/start-a-new-react-project#can-i-use-react-without-a-framework) baked into those frameworks. + +Many React-based frameworks are full-stack and let your React app take advantage of the server. However, you can use the same approach even if you can't or don't want to run JavaScript on the server. In that case, serve the HTML/CSS/JS export ([`next export` output](https://nextjs.org/docs/advanced-features/static-html-export) for Next.js, default for Gatsby) at `/some-app/` instead. + +## Using React for a part of your existing page + +Let's say you have an existing page built with another technology (either a server one like Rails, or a client one like Backbone), and you want to render interactive React components somewhere on that page. That's a common way to integrate React--in fact, it's how most React usage looked at Meta for many years! + +You can do this in two steps: + +1. **Set up a JavaScript environment** that lets you use the [JSX syntax](/learn/writing-markup-with-jsx), split your code into modules with the [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) / [`export`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) syntax, and use packages (for example, React) from the [npm](https://www.npmjs.com/) package registry. +2. **Render your React components** where you want to see them on the page. + +The exact approach depends on your existing page setup, so let's walk through some details. + +### Step 1: Set up a modular JavaScript environment + +A modular JavaScript environment lets you write your React components in individual files, as opposed to writing all of your code in a single file. It also lets you use all the wonderful packages published by other developers on the [npm](https://www.npmjs.com/) registry--including React itself! How you do this depends on your existing setup: + +- **If your app is already split into files that use `import` statements,** try to use the setup you already have. Check whether writing `
` in your JS code causes a syntax error. If it causes a syntax error, you might need to [transform your JavaScript code with Babel](https://babeljs.io/setup), and enable the [Babel React preset](https://babeljs.io/docs/babel-preset-react) to use JSX. + +- **If your app doesn't have an existing setup for compiling JavaScript modules,** set it up with [Vite](https://vitejs.dev/). The Vite community maintains [many integrations with backend frameworks](https://github.com/vitejs/awesome-vite#integrations-with-backends), including Rails, Django, and Laravel. If your backend framework is not listed, [follow this guide](https://vitejs.dev/guide/backend-integration.html) to manually integrate Vite builds with your backend. + +To check whether your setup works, run this command in your project folder: + + +npm install react react-dom + + +Then add these lines of code at the top of your main JavaScript file (it might be called `index.js` or `main.js`): + +```html + + + + My app + + + + + +``` + +```js +import { createRoot } from "react-dom/client"; + +// Clear the existing HTML content +document.body.innerHTML = '
'; + +// Render your React component instead +const root = createRoot(document.getElementById("app")); +root.render(

Hello, world

); +``` + +If the entire content of your page was replaced by a "Hello, world!", everything worked! Keep reading. + + + +Integrating a modular JavaScript environment into an existing project for the first time can feel intimidating, but it's worth it! If you get stuck, try our [community resources](/community) or the [Vite Chat](https://chat.vitejs.dev/). + + + +### Step 2: Render React components anywhere on the page + +In the previous step, you put this code at the top of your main file: + +```js +import { createRoot } from "react-dom/client"; + +// Clear the existing HTML content +document.body.innerHTML = '
'; + +// Render your React component instead +const root = createRoot(document.getElementById("app")); +root.render(

Hello, world

); +``` + +Of course, you don't actually want to clear the existing HTML content! + +Delete this code. + +Instead, you probably want to render your React components in specific places in your HTML. Open your HTML page (or the server templates that generate it) and add a unique [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id) attribute to any tag, for example: + +```html + + + +``` + +This lets you find that HTML element with [`document.getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) and pass it to [`createRoot`](/reference/react-dom/client/createRoot) so that you can render your own React component inside: + +```html + + + + My app + + +

This paragraph is a part of HTML.

+ +

This paragraph is also a part of HTML.

+ + +``` + +```js +import { createRoot } from "react-dom/client"; + +function NavigationBar() { + // TODO: Actually implement a navigation bar + return

Hello from React!

; +} + +const domNode = document.getElementById("navigation"); +const root = createRoot(domNode); +root.render(); +``` + +Notice how the original HTML content from `index.html` is preserved, but your own `NavigationBar` React component now appears inside the `
hatch fmt --check docs/
 pyright --pythonpath <your-env>/bin/python docs/
 cd docs && mkdocs build --strict; echo "exit=$?"
-
\ No newline at end of file +

Then run the examples themselves and open the built page in a browser. mkdocs build --strict catches broken internal links and unresolvable includes — both abort the build with a nonzero exit. Two things it does not catch: a page missing from nav: (an INFO line, exit 0), and a link whose #anchor does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing.

You are the only gate. The single workflow on new-docs runs mkdocs gh-deploy --force on push, and gh-deploy does not pass --strict — a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live.

Pitfall

Do not trust a component that renders without error. Set REACTPY_DEBUG=1 when probing: with debug off, a component that raised produces {"tagName": "", "error": ""} — a blank region and a 200 response. See Running Tests for the harness traps that make a broken example look green.

Process

Branch from new-docs, one page per branch, and use the same Conventional Commit subjects as the history there — docs(learn): author X for ReactPy v2. A commit that removes a 🚧 badge from nav: should be the same commit that authored the page, never a separate "mark done" commit that could be cherry-picked alone.

No CHANGELOG.md entry: documentation work is not user-facing in the sense the changelog means. See Changelog.

Note

docs/src/dictionary.txt exists for the spellcheck plugin, which is currently commented out in mkdocs.yml. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off.

Recap

  • A page is done when it runs and every claim is verified — then remove its 🚧 badge.
  • Port, do not rename: delete JSX, non-existent hooks, StrictMode, memo, && rendering, and React's dependency-array semantics.
  • Fixed structure: Overview, intro paragraph, You will learn, sections, Recap, Related.
  • Relative .md links; admonitions instead of ReactJS's JSX tags.
  • Snippets are includes from docs/examples/, cropped at # start, formatted at 88 columns.
  • --strict checks links and nothing about behaviour. Run the example.
  • Running Tests — the exact commands and how to read their failures
  • Contributing Code — branch and pull request conventions
  • Changelog — what does and does not deserve an entry
  • Community — where to ask about a page before rewriting it
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/index.html b/docs/site/index.html index 259729a63..0691f2b1f 100644 --- a/docs/site/index.html +++ b/docs/site/index.html @@ -36,7 +36,7 @@ .md-content { display: none; } -

ReactPy

It's React, but in Python.

Create user interfaces from components

ReactPy lets you build user interfaces out of individual pieces called components. Create your own ReactPy components like thumbnail, like_button, and video. Then combine them into entire screens, pages, and apps.

My video

Video description

Whether you work on your own or with thousands of other developers, using React feels the same. It is designed to let you seamlessly combine components written by independent people, teams, and organizations.

Write components with pure Python code

ReactPy components are Python functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try using list comprehension. Learning ReactPy is learning programming.

3 Videos

First video

Video description

Second video

Video description

Third video

Video description

Add interactivity wherever you need it

ReactPy components receive data and return what should appear on the screen. You can pass them new data in response to an interaction, like when the user types into an input. ReactPy will then update the screen to match the new data.

example.com/videos.html

Searchable Videos

Type a search query below.

5 Videos

ReactPy: The Documentary

From web library to taco delivery service

Code using Worst Practices

Harriet Potter (2013)

Introducing ReactPy Foriegn

Tim Cooker (2015)

Introducing ReactPy Cooks

Soap Boat and Dinosaur Dan (2018)

Introducing Quantum Components

Isaac Asimov and Lauren-kun (2020)

\ No newline at end of file +

Verified working — the update arrived as a normal patch. Prefer asyncio.to_thread when you can, since it removes the bookkeeping entirely.

Do not hang state on the component function

my_component.started = True as a run-once guard looks harmless and is a cross-user leak: the function object is module-scope, so the first visitor flips it for everyone. Give the component a prop, key the child, or guard with use_ref. The general version of this is in VDOM Mutations.

Client-only data

The mirror problem: something in the browser that Python cannot see. The proxy narrowed this considerably — anything reachable from the event object is now readable, so await event.target.scrollTop, await event.target.className, or a value a canvas put on its own element are ordinary awaits. What is still out of reach is anything the page will not hand over from that starting point: computed style of an element you never got an event from, localStorage, geolocation, the clipboard. The client refuses the paths which would lead there anyway (window, document, localStorage), so a handler cannot walk off the event and into the page — see Protocol Structure.

Your options, in order of preference:

  1. Move the computation server-side. If the value depends only on data you already have, compute it in Python. Server-side rendering is the default for a reason.
  2. Copy it into a form field and submit, exactly as the file example does. Clunky, and it works.
  3. Use the browser API in inline JavaScript and let it act locally — copy to clipboard, request fullscreen, scroll. Keep it to one call, and never interpolate user input into the string.
  4. Render the data yourself. A chart is usually better as a server-built <svg> or a generated image (reactpy.widgets.image) than as JavaScript in the page.

There is no use_ref-style handle to a DOM node, and {"ref": ...} either vanishes or throws in the browser — see Manipulating the DOM with Refs.

Summary

  • Only two channels exist: the model down, events up. An arbitrary server→client message is logged as Received message without handler and dropped.
  • Server push means use_async_effect plus a state setter — loop, handle CancelledError, and remember the dependency array infers by default.
  • Events are live proxies, not snapshots: each await is a round trip to the browser, a read you never make costs nothing, and submit hands back the form as a list of controls.
  • File inputs send metadata only, never bytes. Read the file in the browser, put the text in a named hidden input, and submit.
  • Use asyncio.to_thread for blocking work. Calling a setter from a raw thread fails with no running event loop and no visible symptom — marshal with call_soon_threadsafe.
  • State never lives on the component function object; that is a leak across connections.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/creating-backends/index.html b/docs/site/learn/creating-backends/index.html index 87d7265ed..0775f615f 100644 --- a/docs/site/learn/creating-backends/index.html +++ b/docs/site/learn/creating-backends/index.html @@ -288,4 +288,4 @@ carrier=None, ) layout = Layout(ConnectionContext(my_component(), value=connection)) -

Without that wrapper, use_connection(), use_location(), and use_scope() all raise RuntimeError: No backend established a connection. — even inside a perfectly healthy socket.

Connection is a three-field dataclass, and as the backend author you decide what goes in it:

  • scope — pass the websocket scope through verbatim. use_scope() returns it, and users expect headers and query strings to be there.
  • location — the stock ASGI executors do not use scope["path"]. The browser reports where it is, and the executor parses it out of the websocket query string: ?path=%2Fdocs%2F&qs=a%3D1 becomes Location(path="/docs/", query_string="a=1"). If you hard-code path="/", every use_location() on your app reports the root regardless of the URL in the address bar. Parse it the same way if your app routes on it.
  • carrier — whatever mediates the connection. Nothing in ReactPy reads it; the executors put their websocket object there so your components can reach it. Put your own in, and document what it is, because User Connection tells people to check its type rather than assume. None is fine if nothing needs it.

Connection is a read-only view by design

There is no deliver, no send, no close on it. Components talk to the client through state, effects, and event handlers — not by pushing messages. Your backend is the only code allowed to touch the socket, precisely because letting a component write to the wire bypasses the protocol and desynchronizes the client. If you find yourself wanting a push channel, read Communicating Data Between Server and Client first.

When to write one

Almost never, and it is worth naming the honest cases:

  • Embedding ReactPy in an ASGI app you cannot wrap. ReactPyMiddleware already covers this — it takes your existing app as its first argument. Reach for a custom backend only when the middleware's routing gets in your way, since it owns {path_prefix}* and nothing else.
  • A non-ASGI host. ReactPy's core is host-agnostic: Layout and serve_layout need only two coroutines, so a socket server on another loop is a routing problem, not a rewrite. This is the case the design genuinely supports.
  • Owning the document. If you generate HTML yourself and want ReactPy components inside it, you need the mount markup and the import map. You usually want server_side_component_html for that rather than a new backend — see Serving a component from your own route.
  • Understanding it. Writing the backend once is the clearest way to learn where the boundary between layout and transport is. It is a good exercise even when the result is deleted.

What it does not buy you: performance. serve_layout in your own code is the same loop. And you take on the pieces the executors handle quietly — config validation, the import-map injection, pyscript_setup, static caching, reconnect options, and the error paths in the four sections above.

Summary

  • A backend serves the bundle, serves web modules, returns a mount document, and runs one Layout per websocket.
  • The message shapes are fixed: layout-update and event-proxy-request down, layout-event and event-proxy-response up.
  • Feed every received frame to layout.deliver and serve_layout routes event-proxy responses for you; filter frames yourself and you must route them yourself.
  • Skip the websocket.connect event in your pump; it is the most common cause of an instantly-dying socket.
  • Speak raw ASGI on the socket; framework websocket wrappers do not have the methods serve_layout needs.
  • Build the Layout per connection, and wrap the component in ConnectionContext or the connection hooks raise.
  • You populate Connection; the stock executors parse location from the query string, not the scope path.
  • Components get a read-only view of the connection by design — that is the protocol's integrity, not an omission.
\ No newline at end of file +

Without that wrapper, use_connection(), use_location(), and use_scope() all raise RuntimeError: No backend established a connection. — even inside a perfectly healthy socket.

Connection is a three-field dataclass, and as the backend author you decide what goes in it:

  • scope — pass the websocket scope through verbatim. use_scope() returns it, and users expect headers and query strings to be there.
  • location — the stock ASGI executors do not use scope["path"]. The browser reports where it is, and the executor parses it out of the websocket query string: ?path=%2Fdocs%2F&qs=a%3D1 becomes Location(path="/docs/", query_string="a=1"). If you hard-code path="/", every use_location() on your app reports the root regardless of the URL in the address bar. Parse it the same way if your app routes on it.
  • carrier — whatever mediates the connection. Nothing in ReactPy reads it; the executors put their websocket object there so your components can reach it. Put your own in, and document what it is, because User Connection tells people to check its type rather than assume. None is fine if nothing needs it.

Connection is a read-only view by design

There is no deliver, no send, no close on it. Components talk to the client through state, effects, and event handlers — not by pushing messages. Your backend is the only code allowed to touch the socket, precisely because letting a component write to the wire bypasses the protocol and desynchronizes the client. If you find yourself wanting a push channel, read Communicating Data Between Server and Client first.

When to write one

Almost never, and it is worth naming the honest cases:

  • Embedding ReactPy in an ASGI app you cannot wrap. ReactPyMiddleware already covers this — it takes your existing app as its first argument. Reach for a custom backend only when the middleware's routing gets in your way, since it owns {path_prefix}* and nothing else.
  • A non-ASGI host. ReactPy's core is host-agnostic: Layout and serve_layout need only two coroutines, so a socket server on another loop is a routing problem, not a rewrite. This is the case the design genuinely supports.
  • Owning the document. If you generate HTML yourself and want ReactPy components inside it, you need the mount markup and the import map. You usually want server_side_component_html for that rather than a new backend — see Serving a component from your own route.
  • Understanding it. Writing the backend once is the clearest way to learn where the boundary between layout and transport is. It is a good exercise even when the result is deleted.

What it does not buy you: performance. serve_layout in your own code is the same loop. And you take on the pieces the executors handle quietly — config validation, the import-map injection, pyscript_setup, static caching, reconnect options, and the error paths in the four sections above.

Summary

  • A backend serves the bundle, serves web modules, returns a mount document, and runs one Layout per websocket.
  • The message shapes are fixed: layout-update and event-proxy-request down, layout-event and event-proxy-response up.
  • Feed every received frame to layout.deliver and serve_layout routes event-proxy responses for you; filter frames yourself and you must route them yourself.
  • Skip the websocket.connect event in your pump; it is the most common cause of an instantly-dying socket.
  • Speak raw ASGI on the socket; framework websocket wrappers do not have the methods serve_layout needs.
  • Build the Layout per connection, and wrap the component in ConnectionContext or the connection hooks raise.
  • You populate Connection; the stock executors parse location from the query string, not the scope path.
  • Components get a read-only view of the connection by design — that is the protocol's integrity, not an omission.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/creating-vdom-event-handlers/index.html b/docs/site/learn/creating-vdom-event-handlers/index.html index 576e80f3d..99f199b98 100644 --- a/docs/site/learn/creating-vdom-event-handlers/index.html +++ b/docs/site/learn/creating-vdom-event-handlers/index.html @@ -168,4 +168,4 @@

This is the way to touch the DOM from ReactPy, and the reason it exists is that the server has no node to hold (Manipulating the DOM with Refs). Keep each snippet to a single call, and never interpolate anything you did not write:

Trusted strings only

The string runs with your application's privileges, in the page. f"this.innerText = '{user_input}'" is an injection point. Build strings from constants, or use a Python handler and pass data through state.

Note also that the pattern is ^on[A-Z]\w+ — a lowercase {"onclick": "..."} matches nothing and becomes a plain attribute that never fires.

Merging handlers

reactpy.core.events.merge_event_handlers combines several handlers into one that runs them concurrently in a task group. It is public API but nothing in the installed package calls it — verified by grepping the whole reactpy package, which finds only its own definition — so you will rarely have a reason to call it. Its failure mode is still worth knowing, because it is a ValueError at build time:

merge_event_handlers([a, b])
 # ValueError: Cannot merge handlers - 'stop_propagation', 'prevent_default',
 # 'debounce', 'throttle', 'target', or 'stop_immediate_propagation' mismatch.
-

Handlers merge only if every option matches. An empty list raises ValueError: No event handlers to merge.

What to check when a control does nothing

Every failure mode on this page is silent in the browser, so work through them in this order:

  1. Is it declared async def? A def, a lambda, or a state setter raises while the component renders, and the traceback names the function.
  2. Did you pass the function or call it? A handler is a coroutine, so {"onClick": handler()} hands over a coroutine object — not callable, so it falls through to the attributes and dies when the model is encoded: TypeError: Type is not JSON serializable: coroutine from the send path. A handler which returns something instead (None from a helper) ships as an inert attribute and never fires. Either way: pass the function, do not call it.
  3. Is the key spelled the way the DOM spells its event, with a leading on and a capital letter? ReactPy passes the key straight through, so a on_click registers a listener for an event named _click, which nothing ever dispatches. Copy the DOM's camelCase spelling (onClick, onDoubleClick, onKeyDown) rather than guessing.
  4. Does the handler take exactly one parameter? Two required parameters means a server-side TypeError and a dead button.
  5. Is it a bound method? Then preventDefault/stopPropagation/debounce are not detected — use @event.
  6. Read the server log. Failed to execute event handler is where swallowed handler errors go; the browser console will show nothing.

Summary

  • Values are sorted by kind, not name: callable → eventHandlers (and raises there unless it can be awaited), string under onXxx → inlineJavaScript, everything else → attributes.
  • None, True, and dict handlers all fail silently as attributes.
  • A handler is a coroutine — a def or a lambda is refused with a TypeError when the element is built, not when it is clicked.
  • A plain coroutine receives the argument list unpacked (so, the event proxy); an EventHandler receives the list.
  • Set options with @event(prevent_default=..., stop_propagation=..., stop_immediate_propagation=..., debounce=..., throttle=...); the bytecode detector is a convenience with real limits, notably bound methods.
  • Both debounce and throttle rate-limit client→server traffic: debounce coalesces outgoing events on a trailing edge and only on user-input elements, throttle fires on a leading edge on any element. The server's echo of a new value is reconciled by _reactpy_ack_seq, not by debounce. Invalid values are dropped with a console warning, not an error.
  • Handler errors surface in the server log only.
\ No newline at end of file +

Handlers merge only if every option matches. An empty list raises ValueError: No event handlers to merge.

What to check when a control does nothing

Every failure mode on this page is silent in the browser, so work through them in this order:

  1. Is it declared async def? A def, a lambda, or a state setter raises while the component renders, and the traceback names the function.
  2. Did you pass the function or call it? A handler is a coroutine, so {"onClick": handler()} hands over a coroutine object — not callable, so it falls through to the attributes and dies when the model is encoded: TypeError: Type is not JSON serializable: coroutine from the send path. A handler which returns something instead (None from a helper) ships as an inert attribute and never fires. Either way: pass the function, do not call it.
  3. Is the key spelled the way the DOM spells its event, with a leading on and a capital letter? ReactPy passes the key straight through, so a on_click registers a listener for an event named _click, which nothing ever dispatches. Copy the DOM's camelCase spelling (onClick, onDoubleClick, onKeyDown) rather than guessing.
  4. Does the handler take exactly one parameter? Two required parameters means a server-side TypeError and a dead button.
  5. Is it a bound method? Then preventDefault/stopPropagation/debounce are not detected — use @event.
  6. Read the server log. Failed to execute event handler is where swallowed handler errors go; the browser console will show nothing.

Summary

  • Values are sorted by kind, not name: callable → eventHandlers (and raises there unless it can be awaited), string under onXxx → inlineJavaScript, everything else → attributes.
  • None, True, and dict handlers all fail silently as attributes.
  • A handler is a coroutine — a def or a lambda is refused with a TypeError when the element is built, not when it is clicked.
  • A plain coroutine receives the argument list unpacked (so, the event proxy); an EventHandler receives the list.
  • Set options with @event(prevent_default=..., stop_propagation=..., stop_immediate_propagation=..., debounce=..., throttle=...); the bytecode detector is a convenience with real limits, notably bound methods.
  • Both debounce and throttle rate-limit client→server traffic: debounce coalesces outgoing events on a trailing edge and only on user-input elements, throttle fires on a leading edge on any element. The server's echo of a new value is reconciled by _reactpy_ack_seq, not by debounce. Invalid values are dropped with a console warning, not an error.
  • Handler errors surface in the server log only.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/queueing-a-series-of-state-updates/index.html b/docs/site/learn/queueing-a-series-of-state-updates/index.html index 422a7dee7..c44001318 100644 --- a/docs/site/learn/queueing-a-series-of-state-updates/index.html +++ b/docs/site/learn/queueing-a-series-of-state-updates/index.html @@ -154,4 +154,4 @@
  1. set_number(number + 5) — number is 0, so state becomes 5.
  2. set_number(lambda n: n + 1) — updater, so 5 + 1 = 6.
  3. set_number(42) — a plain value, so state becomes 42.

The result is 42. The two updaters still ran, and their work was then overwritten — which is the same answer React's queue produces, arrived at by a different route.

To summarize what you can pass to a state setter:

  • An updater function (e.g. lambda n: n + 1) is applied to the current value.
  • Any other value (e.g. 5) replaces the state outright, discarding whatever the updaters produced.

Updater functions run while your handler runs, inside the setter call itself, so — like components — they must be pure. Don't set state from inside one or perform side effects there. They are eager, not deferred, which means the one thing they can still get wrong is the closure: a name the updater reads from the enclosing render is the value that render saw, not the current one. The updater argument is the only value guaranteed fresh, so take everything you need from n.

An updater that raises fails silently

Updaters run eagerly, inside the setter call itself (core/hooks.py, _CurrentState.dispatch calls new(self.value) before scheduling anything) — not later during rendering. Verified: an updater's side effect lands before the line after its set_number(...).

That has a nasty consequence. If an updater raises, the state is never assigned and no render is scheduled; ReactPy logs Failed to execute event handler on the server and swallows the error. The browser shows nothing, the console stays quiet, and the component simply stops responding to that button. Reproduced: a lambda x: 1 / 0 updater produced a server-side traceback and then an indefinite absence of any render.

Two habits follow: keep updaters to one pure expression, and when a counter "stops working" with no error in the browser, check the server log for Failed to execute event handler.

Naming conventions

Name the updater's argument after the state variable it updates. Python has no single-letter convention as strong as JavaScript's, but the intent is the same — the argument is the previous value:

set_enabled(lambda enabled: not enabled)
 set_last_name(lambda last_name: last_name[::-1])
 set_friend_count(lambda friend_count: friend_count * 2)
-

For clarity, prefer a descriptive name over n: lambda previous: previous + 1 reads better in a handler that also does other work.

Recap

  • Setting state does not change the variable in the existing render; it requests a new render.
  • ReactPy processes state updates after a handler which never awaits has finished. That is batching.
  • Batching does not survive an await inside an async def handler — each await can flush a render with partial updates.
  • To update the same state several times in one handler, use updater functions: set_number(lambda n: n + 1).
  • A plain value replaces the state and discards earlier updater results; an updater is applied to the current value.
  • Updater functions must be pure, and ReactPy will not check them for you.
\ No newline at end of file +

For clarity, prefer a descriptive name over n: lambda previous: previous + 1 reads better in a handler that also does other work.

Recap

  • Setting state does not change the variable in the existing render; it requests a new render.
  • ReactPy processes state updates after a handler which never awaits has finished. That is batching.
  • Batching does not survive an await inside an async def handler — each await can flush a render with partial updates.
  • To update the same state several times in one handler, use updater functions: set_number(lambda n: n + 1).
  • A plain value replaces the state and discards earlier updater results; an updater is applied to the current value.
  • Updater functions must be pure, and ReactPy will not check them for you.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/responding-to-events/index.html b/docs/site/learn/responding-to-events/index.html index 2e8bc8ee2..49fd97698 100644 --- a/docs/site/learn/responding-to-events/index.html +++ b/docs/site/learn/responding-to-events/index.html @@ -365,4 +365,4 @@ {"onClickCapture": handle_toolbar_click}, html.button({"onClick": handle_play_click}, "Play Movie"), ) -

ReactPy forwards the key untouched — there is no capture logic in vdom.tsx at all. The suffix is honoured one layer down, by Preact, which strips a trailing Capture, registers the listener with addEventListener(name, handler, true), and dispatches "click" to it in the capture phase. So "onClickCapture" fires before the button's own "onClick", and the ordering is the browser's, not ReactPy's. Nothing in ReactPy checks that the name you gave is a real event, which is why the same freedom lets "onMyThing" through and lets "on_click" register a listener for "_click" that nothing ever dispatches.

Can event handlers have side effects?

Yes. Unlike component bodies, which must stay pure, event handlers run in response to an action rather than during rendering, so they are the correct place to change things. See Where you can cause side effects for where side effects belong and why.

Recap

  • Declare the handler with async def. A def, a lambda, or a state setter is refused with a TypeError when the element is built.
  • Add a handler by putting the DOM event name in camelCase into the attributes dictionary: {"onClick": handle_click}.
  • Only names you define are snake_case; "on_click" renders but never fires.
  • Pass a function, never the result of calling one; a string value means client-side JavaScript, not Python.
  • Handlers may read props, call setters, and have side effects.
  • The event object is a live proxy onto the browser object, not a snapshot and not a SyntheticEvent. Read await event.target.value and await event.key; batch with await event.read("target.value", "key"). Subscripting it raises.
  • Events bubble natively, with the browser's own exceptions such as "onScroll". Stop them with event.stopPropagation(), which is detected from your bytecode — or declare it with @event(stop_propagation=True).
\ No newline at end of file +

ReactPy forwards the key untouched — there is no capture logic in vdom.tsx at all. The suffix is honoured one layer down, by Preact, which strips a trailing Capture, registers the listener with addEventListener(name, handler, true), and dispatches "click" to it in the capture phase. So "onClickCapture" fires before the button's own "onClick", and the ordering is the browser's, not ReactPy's. Nothing in ReactPy checks that the name you gave is a real event, which is why the same freedom lets "onMyThing" through and lets "on_click" register a listener for "_click" that nothing ever dispatches.

Can event handlers have side effects?

Yes. Unlike component bodies, which must stay pure, event handlers run in response to an action rather than during rendering, so they are the correct place to change things. See Where you can cause side effects for where side effects belong and why.

Recap

  • Declare the handler with async def. A def, a lambda, or a state setter is refused with a TypeError when the element is built.
  • Add a handler by putting the DOM event name in camelCase into the attributes dictionary: {"onClick": handle_click}.
  • Only names you define are snake_case; "on_click" renders but never fires.
  • Pass a function, never the result of calling one; a string value means client-side JavaScript, not Python.
  • Handlers may read props, call setters, and have side effects.
  • The event object is a live proxy onto the browser object, not a snapshot and not a SyntheticEvent. Read await event.target.value and await event.key; batch with await event.read("target.value", "key"). Subscripting it raises.
  • Events bubble natively, with the browser's own exceptions such as "onScroll". Stop them with event.stopPropagation(), which is detected from your bytecode — or declare it with @event(stop_propagation=True).
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/separating-events-from-effects/index.html b/docs/site/learn/separating-events-from-effects/index.html index b41665ddb..e0c90db53 100644 --- a/docs/site/learn/separating-events-from-effects/index.html +++ b/docs/site/learn/separating-events-from-effects/index.html @@ -506,4 +506,4 @@ def app(): return search()
1
# TODO
-

Verified: type shoes, press Run, type shoes2, press Run again. The page shows the new query both times; the console says handler sees query='' both times. The counter keeps counting (Run (2)), which is what makes this so easy to miss — the callback looks alive while reading a snapshot from mount.

Depend on the scalar values the callback actually reads, and never on a function defined inside the component.

Recap

  • Ask when the logic should run. Interaction → handler. Synchronization → Effect.
  • State that only a handler sets and only an Effect reads is a smell: call the function from the handler and delete both. Verified symptom: the button works once.
  • Handler logic is deliberately not reactive; that is why you do not need React's Effect Events here.
  • ReactPy stores one handler per target and looks it up at dispatch, so a normal click sees current values — but events delivered before the next render share one snapshot. Use the functional updater for read-modify-write.
  • An async handler may set state after await; after unmount that update is silently dropped.
  • use_callback pins identity, not freshness, and identity buys you nothing for render cost in ReactPy. Reach for a render-updated ref when you need both.
  • Never list a locally defined function as a dependency of a callback or Effect — it compares code-equal and freezes the closure.
\ No newline at end of file +

Verified: type shoes, press Run, type shoes2, press Run again. The page shows the new query both times; the console says handler sees query='' both times. The counter keeps counting (Run (2)), which is what makes this so easy to miss — the callback looks alive while reading a snapshot from mount.

Depend on the scalar values the callback actually reads, and never on a function defined inside the component.

Recap

  • Ask when the logic should run. Interaction → handler. Synchronization → Effect.
  • State that only a handler sets and only an Effect reads is a smell: call the function from the handler and delete both. Verified symptom: the button works once.
  • Handler logic is deliberately not reactive; that is why you do not need React's Effect Events here.
  • ReactPy stores one handler per target and looks it up at dispatch, so a normal click sees current values — but events delivered before the next render share one snapshot. Use the functional updater for read-modify-write.
  • An async handler may set state after await; after unmount that update is silently dropped.
  • use_callback pins identity, not freshness, and identity buys you nothing for render cost in ReactPy. Reach for a render-updated ref when you need both.
  • Never list a locally defined function as a dependency of a callback or Effect — it compares code-equal and freezes the closure.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/sharing-state-between-components/index.html b/docs/site/learn/sharing-state-between-components/index.html index 06955b8fe..c5a3a88d6 100644 --- a/docs/site/learn/sharing-state-between-components/index.html +++ b/docs/site/learn/sharing-state-between-components/index.html @@ -233,4 +233,4 @@ border: 1px solid #aaa; }
1
# TODO
-

This completes lifting state up! Moving state into the common parent component allowed you to coordinate the two panels. Using the active index instead of two "is shown" flags ensured that only one panel is active at a given time. And passing down the event handler to the child allowed the child to change the parent's state.

Controlled and uncontrolled components

It is common to call a component with some local state "uncontrolled". For example, the original panel() component with an is_active state variable is uncontrolled because its parent cannot influence whether the panel is active or not.

In contrast, you might say a component is "controlled" when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final panel() component with the is_active prop is controlled by the accordion() component.

Uncontrolled components are easier to use within their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.

In practice, "controlled" and "uncontrolled" aren't strict technical terms — each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.

When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later.

A single source of truth for each state

In a ReactPy application, many components will have their own state. Some state may "live" close to the leaf components (components at the bottom of the tree) like inputs. Other state may "live" closer to the top of the app. For example, a router implemented in ReactPy would keep the current route in the state of a component near the root, and pass it down by props.

For each unique piece of state, you will choose the component that "owns" it. This principle is also known as having a "single source of truth". It doesn't mean that all state lives in one place — but that for each piece of state, there is a specific component that holds that piece of information. Instead of duplicating shared state between components, lift it up to their common shared parent, and pass it down to the children that need it.

Your app will change as you work on it. It is common that you will move state down or back up while you're still figuring out where each piece of the state "lives". This is all part of the process!

To see what this feels like in practice with a few more components, read Thinking in React.

Summary

  • When you want to coordinate two components, move their state to their common parent.
  • Then pass the information down through props from their common parent.
  • Finally, pass the event handlers down so that the children can change the parent's state.
  • It's useful to consider components as "controlled" (driven by props) or "uncontrolled" (driven by state).
\ No newline at end of file +

This completes lifting state up! Moving state into the common parent component allowed you to coordinate the two panels. Using the active index instead of two "is shown" flags ensured that only one panel is active at a given time. And passing down the event handler to the child allowed the child to change the parent's state.

Controlled and uncontrolled components

It is common to call a component with some local state "uncontrolled". For example, the original panel() component with an is_active state variable is uncontrolled because its parent cannot influence whether the panel is active or not.

In contrast, you might say a component is "controlled" when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final panel() component with the is_active prop is controlled by the accordion() component.

Uncontrolled components are easier to use within their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.

In practice, "controlled" and "uncontrolled" aren't strict technical terms — each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.

When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later.

A single source of truth for each state

In a ReactPy application, many components will have their own state. Some state may "live" close to the leaf components (components at the bottom of the tree) like inputs. Other state may "live" closer to the top of the app. For example, a router implemented in ReactPy would keep the current route in the state of a component near the root, and pass it down by props.

For each unique piece of state, you will choose the component that "owns" it. This principle is also known as having a "single source of truth". It doesn't mean that all state lives in one place — but that for each piece of state, there is a specific component that holds that piece of information. Instead of duplicating shared state between components, lift it up to their common shared parent, and pass it down to the children that need it.

Your app will change as you work on it. It is common that you will move state down or back up while you're still figuring out where each piece of the state "lives". This is all part of the process!

To see what this feels like in practice with a few more components, read Thinking in React.

Summary

  • When you want to coordinate two components, move their state to their common parent.
  • Then pass the information down through props from their common parent.
  • Finally, pass the event handlers down so that the children can change the parent's state.
  • It's useful to consider components as "controlled" (driven by props) or "uncontrolled" (driven by state).
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/state-a-components-memory/index.html b/docs/site/learn/state-a-components-memory/index.html index 95ff7aed9..5bc683667 100644 --- a/docs/site/learn/state-a-components-memory/index.html +++ b/docs/site/learn/state-a-components-memory/index.html @@ -1021,4 +1021,4 @@ margin-bottom: 10px; }
1
# TODO
-

Click "Next" in one gallery and the other doesn't budge. Each call to gallery() is a separate component instance with its own state.

This is the same mechanism behind Passing Props to a Component: the person prop is local to one invocation of Profile(), so two Profile() calls don't share it. State works the same way.

Because state belongs to the instance and not the function, two galleries can drift apart — which is sometimes what you want, and sometimes not. Sharing State Between Components covers the case where they must move together.

How does ReactPy know which state to return?

You might have noticed that the use_state call does not receive any information about which state variable it refers to. There is no "identifier" passed to use_state, so how does it know which state to return? Does it rely on some magic like parsing your functions? The answer is no.

Instead, to enable their concise syntax, Hooks rely on a stable call order on every render of the same component. ReactPy appends each Hook's state to a list on the component's first render, then walks that list by position on every render after (LifeCycleHook.use_state in core/_life_cycle_hook.py). This works well in practice because if you follow the rule above ("only call Hooks at the top level"), Hooks are always called in the same order — and reactpy-flake8 catches most mistakes.

What happens when the order does change is worth knowing, because one of the two outcomes fails silently:

  • A Hook appears where there was none before → IndexError: list index out of range, the render aborts, and the error is logged.
  • A Hook stops being called → the Hooks after it shift up one slot and quietly receive the wrong values. Nothing is logged, and the component keeps rendering.

Measured with two use_state calls that vanish behind a branch, the surviving Hook got the first slot's value while its own was left orphaned. That is far harder to debug than a crash, which is exactly why the top-level rule is enforced rather than merely recommended.

Recap

  • Components need to "remember" things, and that memory is called state.
  • A state variable is declared with use_state, which returns the current value and a setter function.
  • Updating a local variable doesn't re-render, and re-renders throw local variables away. State is what survives.
  • Hooks must be called at the top level, in the same order every render; reactpy-flake8 raises REACTPY102 otherwise.
  • State is local to each invocation of a component, so two instances never share it.
\ No newline at end of file +

Click "Next" in one gallery and the other doesn't budge. Each call to gallery() is a separate component instance with its own state.

This is the same mechanism behind Passing Props to a Component: the person prop is local to one invocation of Profile(), so two Profile() calls don't share it. State works the same way.

Because state belongs to the instance and not the function, two galleries can drift apart — which is sometimes what you want, and sometimes not. Sharing State Between Components covers the case where they must move together.

How does ReactPy know which state to return?

You might have noticed that the use_state call does not receive any information about which state variable it refers to. There is no "identifier" passed to use_state, so how does it know which state to return? Does it rely on some magic like parsing your functions? The answer is no.

Instead, to enable their concise syntax, Hooks rely on a stable call order on every render of the same component. ReactPy appends each Hook's state to a list on the component's first render, then walks that list by position on every render after (LifeCycleHook.use_state in core/_life_cycle_hook.py). This works well in practice because if you follow the rule above ("only call Hooks at the top level"), Hooks are always called in the same order — and reactpy-flake8 catches most mistakes.

What happens when the order does change is worth knowing, because one of the two outcomes fails silently:

  • A Hook appears where there was none before → IndexError: list index out of range, the render aborts, and the error is logged.
  • A Hook stops being called → the Hooks after it shift up one slot and quietly receive the wrong values. Nothing is logged, and the component keeps rendering.

Measured with two use_state calls that vanish behind a branch, the surviving Hook got the first slot's value while its own was left orphaned. That is far harder to debug than a crash, which is exactly why the top-level rule is enforced rather than merely recommended.

Recap

  • Components need to "remember" things, and that memory is called state.
  • A state variable is declared with use_state, which returns the current value and a setter function.
  • Updating a local variable doesn't re-render, and re-renders throw local variables away. State is what survives.
  • Hooks must be called at the top level, in the same order every render; reactpy-flake8 raises REACTPY102 otherwise.
  • State is local to each invocation of a component, so two instances never share it.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/state-as-a-snapshot/index.html b/docs/site/learn/state-as-a-snapshot/index.html index f4d77710c..2fb01d938 100644 --- a/docs/site/learn/state-as-a-snapshot/index.html +++ b/docs/site/learn/state-as-a-snapshot/index.html @@ -345,4 +345,4 @@ margin-bottom: 10px; }
1
# TODO
-

If you press Send and then change "To" before the five seconds elapse, you still get "You said Hello to Alice". Verified: pressing Send and changing the recipient to "Bob" mid-flight produced exactly that string.

That is the payoff of snapshots. handle_submit was created by the render in which to was "Alice", so it keeps saying "Alice" no matter what later renders decide. Had the handler read the state through some live reference instead, changing the field mid-request would have sent the message to Alice while reporting that it went to Bob.

React's own version of this demo uses setTimeout and a browser alert. ReactPy has no alert on the server, so the equivalent here is a status paragraph — and asyncio.sleep rather than a timer callback. If you want the browser to alert, that is the inlineJavaScript escape hatch, not a Python function call.

Recap

  • Setting state does not change the state variable you already have; it requests a render.
  • Every render captures its own snapshot of state, props, and handlers. Values are fixed for the duration of that render.
  • This follows Python's ordinary closure rules: each render is a fresh call, so each handler closes over fresh values.
  • Reading state inside a delayed callback or an await gives you the value from the render that created that callback, which is usually what you want.
  • When you genuinely need the latest value inside a callback, use the updater form set_x(lambda previous: ...).
\ No newline at end of file +

If you press Send and then change "To" before the five seconds elapse, you still get "You said Hello to Alice". Verified: pressing Send and changing the recipient to "Bob" mid-flight produced exactly that string.

That is the payoff of snapshots. handle_submit was created by the render in which to was "Alice", so it keeps saying "Alice" no matter what later renders decide. Had the handler read the state through some live reference instead, changing the field mid-request would have sent the message to Alice while reporting that it went to Bob.

React's own version of this demo uses setTimeout and a browser alert. ReactPy has no alert on the server, so the equivalent here is a status paragraph — and asyncio.sleep rather than a timer callback. If you want the browser to alert, that is the inlineJavaScript escape hatch, not a Python function call.

Recap

  • Setting state does not change the state variable you already have; it requests a render.
  • Every render captures its own snapshot of state, props, and handlers. Values are fixed for the duration of that render.
  • This follows Python's ordinary closure rules: each render is a fresh call, so each handler closes over fresh values.
  • Reading state inside a delayed callback or an await gives you the value from the render that created that callback, which is usually what you want.
  • When you genuinely need the latest value inside a callback, use the updater form set_x(lambda previous: ...).
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/synchronizing-with-effects/index.html b/docs/site/learn/synchronizing-with-effects/index.html index e83903865..ada1fee70 100644 --- a/docs/site/learn/synchronizing-with-effects/index.html +++ b/docs/site/learn/synchronizing-with-effects/index.html @@ -647,4 +647,4 @@ audit_log(room, message), )
1
# TODO
-

Press Type ! twice, then Switch room. The log gains lobby <- hello from the mount, and then dev <- hello!! — the Effect that ran for dev was created by the render whose message was already 'hello!!'. Verified. No entry ever records an intermediate message, because only the renders whose dependencies changed created an Effect.

This is why "my Effect reads an old value" has exactly two correct answers: add the value to the dependency list, or read it through a ref.

There is no double-invoke in development

React remounts every component twice under Strict Mode to expose Effects that are not restart-safe. ReactPy has no Strict Mode and never calls your component twice — verified: a component that increments a counter in its own body counted exactly one render per render.

Two consequences:

  • Nothing will find your unclean Effects for you. The restart-safety check React gives away for free is one you have to run yourself: render the component, unmount it, mount it again, and assert the external system is back where it started. A reactpy.core.layout.Layout in a pytest makes that a ten-line test.
  • Do not add artificial guards to compensate. if already_ran.current: return inside an Effect is evidence the dependency list is wrong, not a fix. Write the clean-up so a restart is free and the question stops mattering.

Recap

  • An Effect is a side effect caused by rendering; an event handler is one caused by a user action. Prefer the handler.
  • Effects are scheduled after the render completes, in the connection's own task. They do not block the render, and the browser may not have the update yet.
  • They run on the server: no DOM, no window, no element to focus.
  • use_effect takes a plain def; use_async_effect takes a zero-argument async def. One mismatch fails loudly, the other (the old async def effect(stop) shape) fails silently.
  • dependencies has four settings. Omitted means inferred from the closure, not "every render" — and the comparison is ==, not identity.
  • Return a clean-up function. It runs before the Effect's next run and on unmount. Skipping it leaks, and a leak in a long-lived server process leaks forever.
  • A long-lived Effect sees a frozen snapshot; read current values from a ref, or restart the Effect by depending on them.
\ No newline at end of file +

Press Type ! twice, then Switch room. The log gains lobby <- hello from the mount, and then dev <- hello!! — the Effect that ran for dev was created by the render whose message was already 'hello!!'. Verified. No entry ever records an intermediate message, because only the renders whose dependencies changed created an Effect.

This is why "my Effect reads an old value" has exactly two correct answers: add the value to the dependency list, or read it through a ref.

There is no double-invoke in development

React remounts every component twice under Strict Mode to expose Effects that are not restart-safe. ReactPy has no Strict Mode and never calls your component twice — verified: a component that increments a counter in its own body counted exactly one render per render.

Two consequences:

  • Nothing will find your unclean Effects for you. The restart-safety check React gives away for free is one you have to run yourself: render the component, unmount it, mount it again, and assert the external system is back where it started. A reactpy.core.layout.Layout in a pytest makes that a ten-line test.
  • Do not add artificial guards to compensate. if already_ran.current: return inside an Effect is evidence the dependency list is wrong, not a fix. Write the clean-up so a restart is free and the question stops mattering.

Recap

  • An Effect is a side effect caused by rendering; an event handler is one caused by a user action. Prefer the handler.
  • Effects are scheduled after the render completes, in the connection's own task. They do not block the render, and the browser may not have the update yet.
  • They run on the server: no DOM, no window, no element to focus.
  • use_effect takes a plain def; use_async_effect takes a zero-argument async def. One mismatch fails loudly, the other (the old async def effect(stop) shape) fails silently.
  • dependencies has four settings. Omitted means inferred from the closure, not "every render" — and the comparison is ==, not identity.
  • Return a clean-up function. It runs before the Effect's next run and on unmount. Skipping it leaks, and a leak in a long-lived server process leaks forever.
  • A long-lived Effect sees a frozen snapshot; read current values from a ref, or restart the Effect by depending on them.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/tutorial-tic-tac-toe/index.html b/docs/site/learn/tutorial-tic-tac-toe/index.html index bdddb9e9e..33f5857ab 100644 --- a/docs/site/learn/tutorial-tic-tac-toe/index.html +++ b/docs/site/learn/tutorial-tic-tac-toe/index.html @@ -2554,4 +2554,4 @@ margin-left: 20px; }
1
# TODO
-

Verified against this file: play three moves to put X in the corners, click Go to game start, and the board is empty again with Next player: X. Jump to Go to move #1, play a different square, and the list retracts to two entries — the abandoned branch is gone.

None of that would work if the handlers mutated squares in place. Every stored board would be the same list, and rewinding would show the current game. This is the payoff for the rule from Why immutability is important.

Pitfall

Do not store one board and a separate list of moves that points into it. The history must own the boards, and the boards must never change once stored. If you find yourself reaching for deepcopy here, that is a sign something is still being mutated — with the code above, the boards are never modified after they are created, so a plain slice is enough.

Final cleanup

The finished game holds its state in three places, each as low as it can be while still seeing what it needs:

  • game owns history and current_move, because it renders both the board and the move list.
  • board owns nothing. It receives x_is_next, squares, and on_play.
  • square owns nothing. It receives value and on_click.

That is the pattern this tutorial was built around. State belongs to the component that needs it and to every component above it — no lower, because then a sibling cannot see it, and no higher, because then unrelated renders redo the work. When two components need the same data, lift it to their nearest common ancestor.

Wrapping up

  • A component is a @component function returning one thing; several children go inside html.fragment(...) or a list.
  • Props are ordinary Python parameters, and they are read-only.
  • use_state gives a component memory; calling the setter re-renders it.
  • Handlers take exactly one argument, the event. Extra context is bound on the Python side, usually with a factory function.
  • Never mutate a list you have put in state — copy it, or the setter silently does nothing.
  • Derive what you can during render instead of storing it: winner, x_is_next, current_squares.
  • Lift state to the lowest component that can see everything that needs it.
  • Give list items a key — in the attribute dict for elements, as a keyword for components.
  • Time travel is free once your data is immutable, and impossible when it is not.
\ No newline at end of file +

Verified against this file: play three moves to put X in the corners, click Go to game start, and the board is empty again with Next player: X. Jump to Go to move #1, play a different square, and the list retracts to two entries — the abandoned branch is gone.

None of that would work if the handlers mutated squares in place. Every stored board would be the same list, and rewinding would show the current game. This is the payoff for the rule from Why immutability is important.

Pitfall

Do not store one board and a separate list of moves that points into it. The history must own the boards, and the boards must never change once stored. If you find yourself reaching for deepcopy here, that is a sign something is still being mutated — with the code above, the boards are never modified after they are created, so a plain slice is enough.

Final cleanup

The finished game holds its state in three places, each as low as it can be while still seeing what it needs:

  • game owns history and current_move, because it renders both the board and the move list.
  • board owns nothing. It receives x_is_next, squares, and on_play.
  • square owns nothing. It receives value and on_click.

That is the pattern this tutorial was built around. State belongs to the component that needs it and to every component above it — no lower, because then a sibling cannot see it, and no higher, because then unrelated renders redo the work. When two components need the same data, lift it to their nearest common ancestor.

Wrapping up

  • A component is a @component function returning one thing; several children go inside html.fragment(...) or a list.
  • Props are ordinary Python parameters, and they are read-only.
  • use_state gives a component memory; calling the setter re-renders it.
  • Handlers take exactly one argument, the event. Extra context is bound on the Python side, usually with a factory function.
  • Never mutate a list you have put in state — copy it, or the setter silently does nothing.
  • Derive what you can during render instead of storing it: winner, x_is_next, current_squares.
  • Lift state to the lowest component that can see everything that needs it.
  • Give list items a key — in the attribute dict for elements, as a keyword for components.
  • Time travel is free once your data is immutable, and impossible when it is not.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/updating-arrays-in-state/index.html b/docs/site/learn/updating-arrays-in-state/index.html index 4c598f0d7..725f88566 100644 --- a/docs/site/learn/updating-arrays-in-state/index.html +++ b/docs/site/learn/updating-arrays-in-state/index.html @@ -460,4 +460,4 @@

Tick one checkbox and only that artwork's "seen" flips — verified, the pair goes from [False, False] to [True, False]. The comprehension copies every item, and only the targeted one is rebuilt with the changed field.

A shallow copy of the list is not enough

It is tempting to write:

next_items = list(artworks)          # new list...
 next_items[0]["seen"] = True         # ...but the SAME dicts inside
 set_items(next_items)
-

That is a new list containing the same dict objects. Two problems follow. The assignment mutates a dict the previous state still references, so the change is invisible to ReactPy's comparison. And anything else holding that dict — another component seeded from the same fixture, a cached render — sees it flip too.

Copying is shallow at every level in Python; there is no deep copy on assignment or unpacking. Build the changed dict explicitly, as the example does. See Updating a nested object for the object-side version of this.

Keep identity stable for unchanged items

Notice the else artwork branch: unchanged items are passed through as the same objects, not rebuilt. That is both cheaper and useful — if you rebuild everything on each keystroke, nothing can be recognised as unchanged later.

Writing concise update logic

React's docs finish this page with Immer, which lets you write draft.push(...) behind a proxy. ReactPy has no Immer and no equivalent. There is no proxy layer to record your mutations, and no use_immer to import.

What you have instead is what this page already used:

  • List unpacking and comprehensions — [*items, x], [f(i) for i in items], [*items[:i], x, *items[i:]]. Verbose but explicit, and it fails loudly rather than silently.
  • copy.deepcopy — for a structure too nested to copy by hand.
  • use_reducer — moves the update logic out of the component, which is where the verbosity actually starts to hurt. See Extracting State Logic into a Reducer.

A homegrown Immer is worth avoiding specifically because it would paper over the failure mode this page is about: mutations are more dangerous here than in the browser, because ReactPy decides whether to render by comparing the old and new values on the server — so a mutation that hands back the same object, or an equal one, is dropped without a trace.

Recap

  • Treat lists in state as read-only: never call append(), sort(), insert(), remove(), and friends on them.
  • Build a new list with [*items, x] or a comprehension, and pass that to the setter.
  • Mutating and passing the same list back is silently dropped — no render, no error.
  • Never store filter()/map() results in state; they are single-use iterators and the second render will be empty.
  • To change an object inside a list, copy the list and copy the changed dict.
  • There is no Immer in ReactPy; use comprehensions, deepcopy, or use_reducer.
\ No newline at end of file +

That is a new list containing the same dict objects. Two problems follow. The assignment mutates a dict the previous state still references, so the change is invisible to ReactPy's comparison. And anything else holding that dict — another component seeded from the same fixture, a cached render — sees it flip too.

Copying is shallow at every level in Python; there is no deep copy on assignment or unpacking. Build the changed dict explicitly, as the example does. See Updating a nested object for the object-side version of this.

Keep identity stable for unchanged items

Notice the else artwork branch: unchanged items are passed through as the same objects, not rebuilt. That is both cheaper and useful — if you rebuild everything on each keystroke, nothing can be recognised as unchanged later.

Writing concise update logic

React's docs finish this page with Immer, which lets you write draft.push(...) behind a proxy. ReactPy has no Immer and no equivalent. There is no proxy layer to record your mutations, and no use_immer to import.

What you have instead is what this page already used:

  • List unpacking and comprehensions — [*items, x], [f(i) for i in items], [*items[:i], x, *items[i:]]. Verbose but explicit, and it fails loudly rather than silently.
  • copy.deepcopy — for a structure too nested to copy by hand.
  • use_reducer — moves the update logic out of the component, which is where the verbosity actually starts to hurt. See Extracting State Logic into a Reducer.

A homegrown Immer is worth avoiding specifically because it would paper over the failure mode this page is about: mutations are more dangerous here than in the browser, because ReactPy decides whether to render by comparing the old and new values on the server — so a mutation that hands back the same object, or an equal one, is dropped without a trace.

Recap

  • Treat lists in state as read-only: never call append(), sort(), insert(), remove(), and friends on them.
  • Build a new list with [*items, x] or a comprehension, and pass that to the setter.
  • Mutating and passing the same list back is silently dropped — no render, no error.
  • Never store filter()/map() results in state; they are single-use iterators and the second render will be empty.
  • To change an object inside a list, copy the list and copy the changed dict.
  • There is no Immer in ReactPy; use comprehensions, deepcopy, or use_reducer.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/updating-objects-in-state/index.html b/docs/site/learn/updating-objects-in-state/index.html index 9a64421b9..a67f52faa 100644 --- a/docs/site/learn/updating-objects-in-state/index.html +++ b/docs/site/learn/updating-objects-in-state/index.html @@ -288,4 +288,4 @@

copy["theme"] and person["theme"] are the same dict. Python never deep-copies on assignment or on unpacking — copying is shallow for dicts, lists, and everything else. To get a genuinely independent copy you either copy each level explicitly, as the example does, or use copy.deepcopy:

import copy
 
 set_people(copy.deepcopy(people))
-

deepcopy is the blunt instrument: correct, but slower, and it copies everything reachable, including objects you did not mean to duplicate. Prefer explicit copies for the paths you actually change, and keep state to plain dicts, lists, and primitives so deepcopy remains a safe fallback.

Shallow sharing is also why two components seeded from the same module-level fixture end up aliasing each other's data — see Updating Arrays in State for the list version of that bug.

Writing concise update logic

React's docs reach for Immer here, which records your mutations through a proxy and turns them into copies. ReactPy has no Immer and no equivalent. There is no proxy layer, so nothing could observe your mutations, and there is no use_immer to import.

What you have instead:

  • Dict and list unpacking — {**obj, "k": v}, [*items, x]. This covers the overwhelming majority of updates and is what every example above uses.
  • copy.deepcopy — for the rare shape too deep to copy by hand.
  • use_reducer — does not shorten updates, but moves them out of the component so their verbosity costs you less. See Extracting State Logic into a Reducer.

Resist building your own Immer. A proxy that logs mutations would have to run on the server, after a websocket round trip, which is not the problem Immer solves in the browser. If update logic feels genuinely painful, that usually means the state shape is wrong — see Choosing the State Structure.

Recap

  • Treat everything ReactPy hands you as state or props as read-only: never assign into it, never call a mutating method on it.
  • Build a new object with {**old, ...} and pass that to the setter.
  • Mutating in place and passing the same object back is silently dropped — ReactPy compares the new value with the old and schedules no render when they are equal. Nothing in the browser will tell you.
  • Copying is shallow at every level; copy all the way up from the field that changed, or use copy.deepcopy.
  • Mutating a value you created during this render is fine.
  • There is no Immer in ReactPy; use dict and list unpacking, or restructure the state.
\ No newline at end of file +

deepcopy is the blunt instrument: correct, but slower, and it copies everything reachable, including objects you did not mean to duplicate. Prefer explicit copies for the paths you actually change, and keep state to plain dicts, lists, and primitives so deepcopy remains a safe fallback.

Shallow sharing is also why two components seeded from the same module-level fixture end up aliasing each other's data — see Updating Arrays in State for the list version of that bug.

Writing concise update logic

React's docs reach for Immer here, which records your mutations through a proxy and turns them into copies. ReactPy has no Immer and no equivalent. There is no proxy layer, so nothing could observe your mutations, and there is no use_immer to import.

What you have instead:

  • Dict and list unpacking — {**obj, "k": v}, [*items, x]. This covers the overwhelming majority of updates and is what every example above uses.
  • copy.deepcopy — for the rare shape too deep to copy by hand.
  • use_reducer — does not shorten updates, but moves them out of the component so their verbosity costs you less. See Extracting State Logic into a Reducer.

Resist building your own Immer. A proxy that logs mutations would have to run on the server, after a websocket round trip, which is not the problem Immer solves in the browser. If update logic feels genuinely painful, that usually means the state shape is wrong — see Choosing the State Structure.

Recap

  • Treat everything ReactPy hands you as state or props as read-only: never assign into it, never call a mutating method on it.
  • Build a new object with {**old, ...} and pass that to the setter.
  • Mutating in place and passing the same object back is silently dropped — ReactPy compares the new value with the old and schedules no render when they are equal. Nothing in the browser will tell you.
  • Copying is shallow at every level; copy all the way up from the field that changed, or use copy.deepcopy.
  • Mutating a value you created during this render is fine.
  • There is no Immer in ReactPy; use dict and list unpacking, or restructure the state.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/learn/you-might-not-need-an-effect/index.html b/docs/site/learn/you-might-not-need-an-effect/index.html index 7d322ee88..429c42159 100644 --- a/docs/site/learn/you-might-not-need-an-effect/index.html +++ b/docs/site/learn/you-might-not-need-an-effect/index.html @@ -736,4 +736,4 @@ )
1
# TODO
 

Verified: the Effect stores the raw response; the labels, the per-row formatting, and the total are computed during render. One request per report name (['orders', 'users']), and no rows or total state variables that could fall out of step with the response.

The example carries one more lesson in its state shape. It stores the response together with the name that produced it and derives the read:

raw = loaded["data"] if loaded["name"] == name else {}
-

Storing the response alone looks fine and is wrong: switch from orders to users and the component re-renders immediately, before the new response arrives, so the client receives users: 55 total above the orders rows — 55 being orders' total. Measured, and the derived read above is what fixes it, without remounting the component.

The anti-pattern is a second Effect that watches the response and sets three more state variables from it. Each of those is a value you can calculate, and each adds a render in which the page is half-updated.

Because ReactPy already runs on the server, there is usually a better option than fetching in the child at all: fetch in a parent during render, or at startup, and pass the result down as a prop. Then the child has no Effect and no loading state, and the response is shared rather than re-requested. Keep the Effect for data that depends on something only the connection knows, like use_location.

How to tell whether an Effect is necessary

The Effect... Verdict
reads state or props and sets other state unnecessary — compute it during render
caches a calculation unnecessary — use_memo
resets state when a prop changes unnecessary — pass a key
invalidates state when a prop changes unnecessary — derive the read from what it belonged to
runs logic shared by two handlers unnecessary — call a function
reacts to a user action unnecessary — put it in the event handler
calls a parent's callback when a value changes unnecessary — call it from the handler
subscribes to a queue, socket, or store necessary, with a clean-up
starts async work that outlives the render necessary, with use_async_effect
starts per-connection work on mount necessary, with dependencies=[]

If you cannot name what the Effect is synchronizing with, it is probably doing a render's job.

Two failure modes that cost hours

  • An Effect that sets state it depends on loops silently. ReactPy has no loop detector. Measured: 86 renders and counting before the probe stopped.
  • Hooks do not work inside an effect body. use_context, use_state, and friends all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? — effects run in their own task, with no hook on the stack. Read what you need during render and let the closure carry it in. The traceback appears only in the server log; the page just goes quiet.

Recap

  • An Effect runs after a render. If the work could happen during the render, it should.
  • Values calculated from props or state are not state. Compute them; do not store and re-sync them. Measured: the Effect version costs twice the renders and ships a wrong first frame.
  • use_memo caches expensive calculations without adding a render — but it caches values, not component renders.
  • Reset state with a key, not with an Effect.
  • ReactPy cannot use React's render-phase setter trick: the stale patch reaches the client. Derive the value instead.
  • Sharing logic between handlers, notifying a parent, and submitting a form are all handler work.
  • Subscriptions and async streams are the real use for Effects — with a clean-up, always.
\ No newline at end of file +

Storing the response alone looks fine and is wrong: switch from orders to users and the component re-renders immediately, before the new response arrives, so the client receives users: 55 total above the orders rows — 55 being orders' total. Measured, and the derived read above is what fixes it, without remounting the component.

The anti-pattern is a second Effect that watches the response and sets three more state variables from it. Each of those is a value you can calculate, and each adds a render in which the page is half-updated.

Because ReactPy already runs on the server, there is usually a better option than fetching in the child at all: fetch in a parent during render, or at startup, and pass the result down as a prop. Then the child has no Effect and no loading state, and the response is shared rather than re-requested. Keep the Effect for data that depends on something only the connection knows, like use_location.

How to tell whether an Effect is necessary

The Effect... Verdict
reads state or props and sets other state unnecessary — compute it during render
caches a calculation unnecessary — use_memo
resets state when a prop changes unnecessary — pass a key
invalidates state when a prop changes unnecessary — derive the read from what it belonged to
runs logic shared by two handlers unnecessary — call a function
reacts to a user action unnecessary — put it in the event handler
calls a parent's callback when a value changes unnecessary — call it from the handler
subscribes to a queue, socket, or store necessary, with a clean-up
starts async work that outlives the render necessary, with use_async_effect
starts per-connection work on mount necessary, with dependencies=[]

If you cannot name what the Effect is synchronizing with, it is probably doing a render's job.

Two failure modes that cost hours

  • An Effect that sets state it depends on loops silently. ReactPy has no loop detector. Measured: 86 renders and counting before the probe stopped.
  • Hooks do not work inside an effect body. use_context, use_state, and friends all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? — effects run in their own task, with no hook on the stack. Read what you need during render and let the closure carry it in. The traceback appears only in the server log; the page just goes quiet.

Recap

  • An Effect runs after a render. If the work could happen during the render, it should.
  • Values calculated from props or state are not state. Compute them; do not store and re-sync them. Measured: the Effect version costs twice the renders and ships a wrong first frame.
  • use_memo caches expensive calculations without adding a render — but it caches values, not component renders.
  • Reset state with a key, not with an Effect.
  • ReactPy cannot use React's render-phase setter trick: the stale patch reaches the client. Derive the value instead.
  • Sharing logic between handlers, notifying a parent, and submitting a form are all handler work.
  • Subscriptions and async streams are the real use for Effects — with a clean-up, always.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/client-api/index.html b/docs/site/reference/client-api/index.html index 0bee5f294..4e91ffaf3 100644 --- a/docs/site/reference/client-api/index.html +++ b/docs/site/reference/client-api/index.html @@ -225,4 +225,4 @@ root_components=["main.counter"], )
1
# TODO
-

The component is registered with root_components, and the componentPath in the page — "main.counter/" — is that dotted path with a slash appended.

The 404 in the console is usually not yours

A page like this logs one Failed to load resource: 404 on every load. It is /favicon.ico, which no browser should be told about by a component framework. Verified: it appears with everything else working. Ignore it; a second 404 naming a /reactpy/... path means the bundle could not be fetched, which does mean the mount will not happen.


Failure modes

Symptom Cause
TypeError: Cannot read properties of null (reading '__k') mountElement is None. The id does not exist, or the script ran before the element was parsed.
TypeError: Failed to construct 'URL': Invalid URL pathPrefix missing, or missing a leading or trailing slash. No request is ever made.
mountReactPy() throws Cannot read properties of undefined No options object at all.
Failed to connect!, nothing rendered The socket URL did not match the server's route. Read the URL in the browser's own error line.
[ReactPy] Connected!, empty element, nothing in the browser console The component path is unknown to the server. The failure is server-side: RuntimeError: Attempting to use an unregistered root component <path>. Check the server log, not the browser.
Renders once, never updates Handlers are not firing — usually a snake_case event name such as on_click. See Responding to Events.
Component vanishes when you remove it from the DOM, socket still open Expected. There is no unmount that closes the socket.
Two import maps in the document, React warnings about a removed rule Your map does not use id="reactpy-importmap".

  • Protocol Structure — what travels over the socket this function opens.
  • ReactPy and ReactPyMiddleware — the executors that generate this call.
  • Jinja — emitting mounts from templates, including {% pyscript_component %} for running Python in the page.
  • Use Location — reading back the path and qs the client sends.
\ No newline at end of file +

The component is registered with root_components, and the componentPath in the page — "main.counter/" — is that dotted path with a slash appended.

The 404 in the console is usually not yours

A page like this logs one Failed to load resource: 404 on every load. It is /favicon.ico, which no browser should be told about by a component framework. Verified: it appears with everything else working. Ignore it; a second 404 naming a /reactpy/... path means the bundle could not be fetched, which does mean the mount will not happen.


Failure modes

Symptom Cause
TypeError: Cannot read properties of null (reading '__k') mountElement is None. The id does not exist, or the script ran before the element was parsed.
TypeError: Failed to construct 'URL': Invalid URL pathPrefix missing, or missing a leading or trailing slash. No request is ever made.
mountReactPy() throws Cannot read properties of undefined No options object at all.
Failed to connect!, nothing rendered The socket URL did not match the server's route. Read the URL in the browser's own error line.
[ReactPy] Connected!, empty element, nothing in the browser console The component path is unknown to the server. The failure is server-side: RuntimeError: Attempting to use an unregistered root component <path>. Check the server log, not the browser.
Renders once, never updates Handlers are not firing — usually a snake_case event name such as on_click. See Responding to Events.
Component vanishes when you remove it from the DOM, socket still open Expected. There is no unmount that closes the socket.
Two import maps in the document, React warnings about a removed rule Your map does not use id="reactpy-importmap".

  • Protocol Structure — what travels over the socket this function opens.
  • ReactPy and ReactPyMiddleware — the executors that generate this call.
  • Jinja — emitting mounts from templates, including {% pyscript_component %} for running Python in the page.
  • Use Location — reading back the path and qs the client sends.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/common-props/index.html b/docs/site/reference/common-props/index.html index a933459c2..6318dff85 100644 --- a/docs/site/reference/common-props/index.html +++ b/docs/site/reference/common-props/index.html @@ -187,4 +187,4 @@
KeyError: 'Invalid key: bogus'
 

A dict used as a child with no "tagName" does not raise on the server. The layout catches it, logs the traceback, and sends an element whose error field carries the message — which the client prints in the page, in a <pre> that replaces the component, not in the console:

html.div(None, {"children": ["x"]})
 
ValueError: Expected a VDOM element dict, not {'children': ['x']}
-

That text exists only under REACTPY_DEBUG=1. Verified in a browser with a bad child between two headings: with debug on, the page read above / ValueError: Expected a VDOM element dictâ€Ļ / below; with debug off the bad component simply vanished — above / below, no <pre>, and nothing at all in the console. The server log has the traceback either way. So when an element renders as nothing, check the server log first and re-run with REACTPY_DEBUG=1 before trusting the page. See Protocol Structure.


At a glance

VdomDict keys

Key Type Notes
tagName str required; "" means a fragment
children list any accepted child type, flattened
attributes dict[str, Any] must be JSON-serializable
eventHandlers dict[str, EventHandler] written for you from callables
inlineJavaScript dict[str, str] written for you from str handlers
importSource dict written for you by web modules

Any other key raises, as shown above. VdomDict is a dict subclass, so you can read and update it with normal dict access — but not add a seventh key.

Attribute names worth knowing

Attribute Value
class / className str of space-separated class names
for / htmlFor str, the id of the labelled control
style dict of CSS property to value; include units in strings
dangerouslySetInnerHTML {"__html": str}
key str or int, unique among siblings
anything else forwarded to the browser verbatim
  • Usage — the tag API itself: naming rules, custom tags, SVG
  • Responding to Events — the event proxy, and why handlers must be coroutines
  • Protocol Structure — what an event costs on the wire
  • Rendering Lists — keys and why rows keep the wrong state
  • Use Ref — ReactPy's Ref, which is never attached to an element
\ No newline at end of file +

That text exists only under REACTPY_DEBUG=1. Verified in a browser with a bad child between two headings: with debug on, the page read above / ValueError: Expected a VDOM element dictâ€Ļ / below; with debug off the bad component simply vanished — above / below, no <pre>, and nothing at all in the console. The server log has the traceback either way. So when an element renders as nothing, check the server log first and re-run with REACTPY_DEBUG=1 before trusting the page. See Protocol Structure.


At a glance

VdomDict keys

Key Type Notes
tagName str required; "" means a fragment
children list any accepted child type, flattened
attributes dict[str, Any] must be JSON-serializable
eventHandlers dict[str, EventHandler] written for you from callables
inlineJavaScript dict[str, str] written for you from str handlers
importSource dict written for you by web modules

Any other key raises, as shown above. VdomDict is a dict subclass, so you can read and update it with normal dict access — but not add a seventh key.

Attribute names worth knowing

Attribute Value
class / className str of space-separated class names
for / htmlFor str, the id of the labelled control
style dict of CSS property to value; include units in strings
dangerouslySetInnerHTML {"__html": str}
key str or int, unique among siblings
anything else forwarded to the browser verbatim
  • Usage — the tag API itself: naming rules, custom tags, SVG
  • Responding to Events — the event proxy, and why handlers must be coroutines
  • Protocol Structure — what an event costs on the wire
  • Rendering Lists — keys and why rows keep the wrong state
  • Use Ref — ReactPy's Ref, which is never attached to an element
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/protocol-structure/index.html b/docs/site/reference/protocol-structure/index.html index 05a088103..831ee9724 100644 --- a/docs/site/reference/protocol-structure/index.html +++ b/docs/site/reference/protocol-structure/index.html @@ -400,4 +400,4 @@ asyncio.run(main())
1
# TODO
-

Both were run against each other, which is where the mount message, the target, and the patch above come from. The counter's handler only calls a setter, so it never reads a property and the proxy messages never appear — the observer answers them, and gets nothing to answer, which is the "a read you never make costs nothing" rule from Responding to Events showing up on the wire. The two proxy frames further up the page were captured from a handler which does read, against the same middleware. Nothing about the protocol is private to ReactPy's client — given a mount element and a correct target, you can drive a server-rendered component from any language.

The server trusts the client completely

Nothing checks that target names a real handler, that data describes a real event, or that the sender is the page that mounted the component. Whatever your handler reads out of data, it read because the browser asserted it. Treat it as user input: await event.target.value is a string an attacker chose, at any length. The one thing which does not travel in the message at all is the ASGI scope: the server built that itself when the socket was accepted, which is why Use Connection and User Scope are the trusted side of the picture — even there, remember that the headers and peer address it reports are what the client chose to send.

The proxy did not weaken this; it moved the trust question rather than answering it. EventProxyBackend._route takes "value" from a response verbatim — no schema, no type check — so a crafted client can make any read return anything, including a list where a str was expected. And a call: true request runs a method in the page, which is why the client, not the server, holds the veto: BLOCKED_MEMBERS and the /^on[a-z]/i rule are the page's defence against a hostile server. Read that as what it is — the two sides do not trust each other, and only the page defends the page. Anything the client volunteers without being asked ("type") is the same kind of assertion as the rest of data.


Failure modes

Symptom Cause
Handshake refused, [ReactPy] Failed to connect! Missing trailing slash in componentPath, or a path_prefix that differs between the bootstrap and the server.
Socket opens then closes; server logs RuntimeError: Attempting to use an unregistered root component <path>. The dotted path is not in root_components. The socket is accepted before the component is resolved, so the failure is visible only in the server log.
Server log: Unknown message type: <type> Anything other than "layout-event" or "event-proxy-response". The server accepts those two and drops the rest, leaving the connection open.
Server log: Ignored event - handler ... does not exist or its component unmounted Stale or wrong target.
Console: Received message without type or Received message without handler A frame the client has no handler for. Ignored; the connection stays up.
A <pre> with an exception message appears in the page The component raised. See Errors arrive as a model.
Component renders once and never updates The handler never fires — usually a snake_case event name such as on_click, so no event is ever sent.
[ReactPy] Disconnected! then Reconnecting in â€Ļ The socket dropped and backoff is in effect. If Connection max retries exhausted! follows, the page needs a reload.

  • Common Props — the attribute names that become eventHandlers and inlineJavaScript.
  • Usage — writing the model by hand as a plain dict.
  • ReactPyMiddleware — the paths the server claims, and the bootstrap that opens the socket.
  • Use Connection and Use Location — the server-side view of the two query parameters.
\ No newline at end of file +

Both were run against each other, which is where the mount message, the target, and the patch above come from. The counter's handler only calls a setter, so it never reads a property and the proxy messages never appear — the observer answers them, and gets nothing to answer, which is the "a read you never make costs nothing" rule from Responding to Events showing up on the wire. The two proxy frames further up the page were captured from a handler which does read, against the same middleware. Nothing about the protocol is private to ReactPy's client — given a mount element and a correct target, you can drive a server-rendered component from any language.

The server trusts the client completely

Nothing checks that target names a real handler, that data describes a real event, or that the sender is the page that mounted the component. Whatever your handler reads out of data, it read because the browser asserted it. Treat it as user input: await event.target.value is a string an attacker chose, at any length. The one thing which does not travel in the message at all is the ASGI scope: the server built that itself when the socket was accepted, which is why Use Connection and User Scope are the trusted side of the picture — even there, remember that the headers and peer address it reports are what the client chose to send.

The proxy did not weaken this; it moved the trust question rather than answering it. EventProxyBackend._route takes "value" from a response verbatim — no schema, no type check — so a crafted client can make any read return anything, including a list where a str was expected. And a call: true request runs a method in the page, which is why the client, not the server, holds the veto: BLOCKED_MEMBERS and the /^on[a-z]/i rule are the page's defence against a hostile server. Read that as what it is — the two sides do not trust each other, and only the page defends the page. Anything the client volunteers without being asked ("type") is the same kind of assertion as the rest of data.


Failure modes

Symptom Cause
Handshake refused, [ReactPy] Failed to connect! Missing trailing slash in componentPath, or a path_prefix that differs between the bootstrap and the server.
Socket opens then closes; server logs RuntimeError: Attempting to use an unregistered root component <path>. The dotted path is not in root_components. The socket is accepted before the component is resolved, so the failure is visible only in the server log.
Server log: Unknown message type: <type> Anything other than "layout-event" or "event-proxy-response". The server accepts those two and drops the rest, leaving the connection open.
Server log: Ignored event - handler ... does not exist or its component unmounted Stale or wrong target.
Console: Received message without type or Received message without handler A frame the client has no handler for. Ignored; the connection stays up.
A <pre> with an exception message appears in the page The component raised. See Errors arrive as a model.
Component renders once and never updates The handler never fires — usually a snake_case event name such as on_click, so no event is ever sent.
[ReactPy] Disconnected! then Reconnecting in â€Ļ The socket dropped and backoff is in effect. If Connection max retries exhausted! follows, the page needs a reload.

  • Common Props — the attribute names that become eventHandlers and inlineJavaScript.
  • Usage — writing the model by hand as a plain dict.
  • ReactPyMiddleware — the paths the server claims, and the bootstrap that opens the socket.
  • Use Connection and Use Location — the server-side view of the two query parameters.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/reactpy/index.html b/docs/site/reference/reactpy/index.html index 33bd50ddb..58640a3d2 100644 --- a/docs/site/reference/reactpy/index.html +++ b/docs/site/reference/reactpy/index.html @@ -56,4 +56,4 @@ mountReactPy({ mountElement: document.getElementById("app"), ... }); </script> </body> -

Verified by fetching the index route: your root component's markup is not in the response. The browser connects over the websocket and ReactPy renders and streams from there.

What is server-rendered into the first response:

  • prepend_body, converted with reactpy_to_string.
  • html_head.
  • The document shell: lang, static asset paths, the mount script.

Practical consequences:

  • Search engines and curl see an empty app. For content that must exist without JavaScript, render it yourself — reactpy_to_string turns a component into an HTML string, which you can place in prepend_body or emit from your own route. It renders the component's first render directly, without a layout, so the component cannot use any hook: use_state, use_ref, use_effect, use_location and the rest all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout?. Pass plain html.* elements, or a component that calls none of them.
  • There is no first-paint flash to optimise away, because there is no first paint to replace.
  • A component that fails on the server fails after the page loads, leaving a blank <div id="app">. When the page loads but shows nothing, check the server log before the browser console.

Not the same as ReactPyCsr

ReactPyCsr is the one that runs component code in the browser via PyScript. ReactPy runs components on the server and sends VDOM over a websocket. Both need a server; they differ in where your Python executes. See ReactPyMiddleware for mounting into an existing ASGI app.

\ No newline at end of file +

Verified by fetching the index route: your root component's markup is not in the response. The browser connects over the websocket and ReactPy renders and streams from there.

What is server-rendered into the first response:

  • prepend_body, converted with reactpy_to_string.
  • html_head.
  • The document shell: lang, static asset paths, the mount script.

Practical consequences:

  • Search engines and curl see an empty app. For content that must exist without JavaScript, render it yourself — reactpy_to_string turns a component into an HTML string, which you can place in prepend_body or emit from your own route. It renders the component's first render directly, without a layout, so the component cannot use any hook: use_state, use_ref, use_effect, use_location and the rest all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout?. Pass plain html.* elements, or a component that calls none of them.
  • There is no first-paint flash to optimise away, because there is no first paint to replace.
  • A component that fails on the server fails after the page loads, leaving a blank <div id="app">. When the page loads but shows nothing, check the server log before the browser console.

Not the same as ReactPyCsr

ReactPyCsr is the one that runs component code in the browser via PyScript. ReactPy runs components on the server and sends VDOM over a websocket. Both need a server; they differ in where your Python executes. See ReactPyMiddleware for mounting into an existing ASGI app.

Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/rules-of-hooks/index.html b/docs/site/reference/rules-of-hooks/index.html index 6d2fe1285..b66a3df03 100644 --- a/docs/site/reference/rules-of-hooks/index.html +++ b/docs/site/reference/rules-of-hooks/index.html @@ -180,4 +180,4 @@ for line, col, msg in run_checks(ast.parse(source), exhaustive_hook_deps=False): print(line, col, msg) -

Verified against a component with a hook after an early return and a hook inside an if: it reports exactly REACTPY103 hook 'use_state' used after an early return on line 7 and REACTPY102 hook 'use_state' used inside if statement. Pass exhaustive_hook_deps=True to add the dependency-shape checks (REACTPY200–REACTPY202), which cover far less than people expect — see Lifecycle of Reactive Effects.

What the linter cannot do is check the order rule, because order is a runtime property. There is still no runtime check, so the two habits remain:

  • Run with REACTPY_DEBUG=1 during development, so a hook-count increase becomes a visible error instead of an empty region.
  • Treat any use_ call that appears below the first non-hook statement of a component body, or inside an if / for / try / nested def, as a review defect — and wire reactpy-flake8 into CI so you are not the only one who spots it.

The linter also matches on names: it flags anything whose name fits ^_*use_\w+ and treats a function as a component only if decorated with something matching ^(component|[\w.]+\.component)$. Rename the decorator or the hook and the checks silently stop applying; the patterns are configurable via --component-decorator-pattern and --hook-function-pattern.


Reference

Pattern ReactPy behaviour Verdict
Hook at component top level Works Do this
Hook in a custom hook called from a body Works Do this
Hook in an if that never changes Works by accident Broken
Hook in an if that can change Fewer hooks: silent state corruption. More hooks: IndexError Broken
Hook in a for Frozen initial values; IndexError if the loop grows Broken
Hook after an early return Slot shift when the branch flips Broken
Hook in an event handler RuntimeError, server log only Broken
Hook in an effect body RuntimeError, server log only Broken
Hook at module level RuntimeError: No life cycle hook is active. Broken
\ No newline at end of file +

Verified against a component with a hook after an early return and a hook inside an if: it reports exactly REACTPY103 hook 'use_state' used after an early return on line 7 and REACTPY102 hook 'use_state' used inside if statement. Pass exhaustive_hook_deps=True to add the dependency-shape checks (REACTPY200–REACTPY202), which cover far less than people expect — see Lifecycle of Reactive Effects.

What the linter cannot do is check the order rule, because order is a runtime property. There is still no runtime check, so the two habits remain:

  • Run with REACTPY_DEBUG=1 during development, so a hook-count increase becomes a visible error instead of an empty region.
  • Treat any use_ call that appears below the first non-hook statement of a component body, or inside an if / for / try / nested def, as a review defect — and wire reactpy-flake8 into CI so you are not the only one who spots it.

The linter also matches on names: it flags anything whose name fits ^_*use_\w+ and treats a function as a component only if decorated with something matching ^(component|[\w.]+\.component)$. Rename the decorator or the hook and the checks silently stop applying; the patterns are configurable via --component-decorator-pattern and --hook-function-pattern.


Reference

Pattern ReactPy behaviour Verdict
Hook at component top level Works Do this
Hook in a custom hook called from a body Works Do this
Hook in an if that never changes Works by accident Broken
Hook in an if that can change Fewer hooks: silent state corruption. More hooks: IndexError Broken
Hook in a for Frozen initial values; IndexError if the loop grows Broken
Hook after an early return Slot shift when the branch flips Broken
Hook in an event handler RuntimeError, server log only Broken
Hook in an effect body RuntimeError, server log only Broken
Hook at module level RuntimeError: No life cycle hook is active. Broken
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/use-ref/index.html b/docs/site/reference/use-ref/index.html index 76b0efe5e..cc4c749b3 100644 --- a/docs/site/reference/use-ref/index.html +++ b/docs/site/reference/use-ref/index.html @@ -230,4 +230,4 @@

pick is a factory: it returns a fresh coroutine per button, so each button gets its own handler and the theme it belongs to is bound by the closure. swap_back is where set_current earns its place — set_theme(previous.set_current(theme)) exchanges the two in one expression, which an assignment could not do because an assignment is not a value. The re-render still comes from set_theme, not from the write to the ref.


Troubleshooting

My ref value is stale

Check which of these you did:

  • You rendered the box, not its contents. html.p(my_ref) prints Ref(...). Use my_ref.current.
  • You expected the write to re-render. A ref never schedules a render. If the screen must change, that value is state.
  • You listed the ref as a dependency. Mutating current does not invalidate effects or memos — the box always compares equal to itself. Put the value in the dependency list, or use state.
  • You read a ref through an old closure. Inside a long-lived effect, re-read box.current on each pass instead of binding it to a local once.

My expensive setup runs on every render

You passed it as the argument: use_ref(build()) calls build() every render. Move it behind a guard:

box = use_ref(None)
 if box.current is None:
     box.current = build()
-

I need to focus an input, or scroll to an element

There is no way to do this from a ReactPy server component, and no "ref_current" attribute will add one. Use the declarative attributes the browser already understands — "autoFocus", "checked", "value", "disabled" — and reach for a client-side app if you truly need imperative browser access.

\ No newline at end of file +

I need to focus an input, or scroll to an element

There is no way to do this from a ReactPy server component, and no "ref_current" attribute will add one. Use the declarative attributes the browser already understands — "autoFocus", "checked", "value", "disabled" — and reach for a client-side app if you truly need imperative browser access.

Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/reference/use-state/index.html b/docs/site/reference/use-state/index.html index 62134eaea..fa2d818fa 100644 --- a/docs/site/reference/use-state/index.html +++ b/docs/site/reference/use-state/index.html @@ -276,4 +276,4 @@ html.p("Previous values: " + (", ".join(map(str, history)) or "-")), )
1
# TODO
-

Clicking twice records Previous values: 0, 1 — verified. The history name in the closure would still be the render-time value, which is exactly why the updater form matters here. For genuinely mutable, non-rendering values, use use_ref instead.

\ No newline at end of file +

Clicking twice records Previous values: 0, 1 — verified. The history name in the closure would still be the render-time value, which is exactly why the updater form matters here. For genuinely mutable, non-rendering values, use use_ref instead.

Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/search/search_index.json b/docs/site/search/search_index.json index c6ae5670b..3154f3720 100644 --- a/docs/site/search/search_index.json +++ b/docs/site/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"about/changelog/","title":"Changelog","text":"

Notable changes to ReactPy, newest first. Read the Unreleased section for what has landed since the last release, and the versioned sections for what shipped in each.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Note

Entries use the Keep a Changelog categories \u2014 Added, Changed, Deprecated, Removed, Fixed, Security \u2014 and versions follow Semantic Versioning. Pre-release versions carry a bN suffix, and behaviour can shift between betas without a major-version bump, so pin an exact version if you depend on one.

The authoritative list of shipped artefacts, with downloads and commit ranges, is GitHub Releases; the published packages are on PyPI. This page is the curated narrative of what changed, not the release log.

"},{"location":"about/changelog/#unreleased","title":"Unreleased","text":""},{"location":"about/changelog/#added","title":"Added","text":"
  • Added support for Python 3.12, 3.13, and 3.14.
  • Added type hints to reactpy.html attributes.
  • Added support for nested components in web modules
  • Added support for inline JavaScript as event handlers or other attributes that expect a callable via reactpy.types.InlineJavaScript
  • Event functions can now call event.preventDefault() and event.stopPropagation() methods directly on the event data object, rather than using the @event decorator.
  • Event data now supports accessing properties via dot notation (ex. event.target.value).
  • Added support for partial functions in EventHandler
  • Added reactpy.types.Event to provide type hints for the standard data function argument (for example def on_click(event: Event): ...).
  • Added asgi and jinja installation extras (for example pip install reactpy[asgi, jinja]).
  • Added reactpy.executors.asgi.ReactPy that can be used to run ReactPy in standalone mode via ASGI.
  • Added reactpy.executors.asgi.ReactPyCsr that can be used to run ReactPy in standalone mode via ASGI, but rendered entirely client-sided.
  • Added reactpy.executors.asgi.ReactPyMiddleware that can be used to utilize ReactPy within any ASGI compatible framework.
  • Added reactpy.templatetags.ReactPyJinja that can be used alongside ReactPyMiddleware to embed several ReactPy components into your existing application. This includes the following template tags: {% component %}, {% pyscript_component %}, and {% pyscript_setup %}.
  • Added reactpy.pyscript_component that can be used to embed ReactPy components into your existing application.
  • Added reactpy.use_async_effect hook.
  • Added reactpy.Vdom primitive interface for creating VDOM dictionaries.
  • Added reactpy.reactjs.component_from_file to import ReactJS components from a file.
  • Added reactpy.reactjs.component_from_url to import ReactJS components from a URL.
  • Added reactpy.reactjs.component_from_string to import ReactJS components from a string.
  • Added reactpy.reactjs.component_from_npm to import ReactJS components from NPM.
  • Added reactpy.h as a shorthand alias for reactpy.html.
  • Added reactpy.config.REACTPY_MAX_QUEUE_SIZE to configure the maximum size of all ReactPy asyncio queues (e.g. receive buffer, send buffer, event buffer) before ReactPy begins waiting until a slot frees up. This can be used to constraint memory usage.
  • Events now support debounce and throttle, configurable per event via event.debounce = <milliseconds> and EventHandler(fn, throttle=<milliseconds>) respectively.
    • debounce waits until activity stops, then fires once. Default is 200 ms on input/select/textarea, 0 ms elsewhere.
    • throttle caps the rate how often an event is allowed to execute. No default; opt in per event.
"},{"location":"about/changelog/#changed","title":"Changed","text":"
  • The key attribute is now stored within attributes in the VDOM spec.
  • Substitute client-side usage of react with preact.
  • Script elements no longer support behaving like effects. They now strictly behave like plain HTML scripts.
  • The reactpy.html module has been modified to allow for auto-creation of any HTML nodes. For example, you can create a <data-table> element by calling html.data_table().
  • Change set_state comparison method to check equality with == more consistently.
  • Add support for rendering @component children within vdom_to_html.
  • Renamed the use_location hook's search attribute to query_string.
  • Renamed the use_location hook's pathname attribute to path.
  • Renamed reactpy.config.REACTPY_DEBUG_MODE to reactpy.config.REACTPY_DEBUG.
  • ReactPy no longer auto-converts snake_case props to camelCase. It is now the responsibility of the user to ensure that props are in the correct format.
  • Rewrite the event-to-object package to be more robust at handling properties on events.
  • Custom JS components will now automatically assume you are using ReactJS in the absence of a bind function.
  • Refactor layout rendering logic to improve readability and maintainability.
  • The JavaScript package @reactpy/client now exports React and ReactDOM, which allows third-party components to re-use the same React instance as ReactPy.
  • reactpy.html will now automatically flatten lists recursively (ex. reactpy.html([\"child1\", [\"child2\"]]))
  • reactpy.utils.reactpy_to_string will now retain the user's original casing for data-* and aria-* attributes.
  • reactpy.utils.string_to_reactpy has been upgraded to handle more complex scenarios without causing ReactJS rendering errors.
  • reactpy.core.vdom._CustomVdomDictConstructor has been moved to reactpy.types.CustomVdomConstructor.
  • reactpy.core.vdom._EllipsisRepr has been moved to reactpy.types.EllipsisRepr.
  • reactpy.types.VdomDictConstructor has been renamed to reactpy.types.VdomConstructor.
  • REACTPY_ASYNC_RENDERING can now de-duplicate renders where necessary.
  • REACTPY_ASYNC_RENDERING is now defaulted to True for up to 40x performance improvements in environments with high concurrency.
"},{"location":"about/changelog/#deprecated","title":"Deprecated","text":"
  • reactpy.web.module_from_file is deprecated. Use reactpy.reactjs.component_from_file instead.
  • reactpy.web.module_from_url is deprecated. Use reactpy.reactjs.component_from_url instead.
  • reactpy.web.module_from_string is deprecated. Use reactpy.reactjs.component_from_string instead.
  • reactpy.web.export is deprecated. Use reactpy.reactjs.component_from_* instead.
  • reactpy.web.* is deprecated. Use reactpy.reactjs.* instead.
"},{"location":"about/changelog/#removed","title":"Removed","text":"
  • Removed support for Python 3.9 and 3.10.
  • Removed the ability to import reactpy.html.* elements directly. You must now call html.* to access the elements.
  • Removed backend specific installation extras (such as pip install reactpy[starlette]).
  • Removed support for async functions within reactpy.use_effect hook. Use reactpy.use_async_effect instead.
  • Removed deprecated function module_from_template.
  • Removed deprecated exception type reactpy.core.serve.Stop.
  • Removed deprecated component reactpy.widgets.hotswap.
  • Removed reactpy.sample module.
  • Removed reactpy.svg module. Contents previously within reactpy.svg.* can now be accessed via reactpy.html.svg.*.
  • Removed reactpy.html._ function. Use reactpy.html(...) or reactpy.html.fragment(...) instead.
  • Removed reactpy.run. See the documentation for the new method to run ReactPy applications.
  • Removed reactpy.backend.*. See the documentation for the new method to run ReactPy applications.
  • Removed reactpy.core.types module. Use reactpy.types instead.
  • Removed reactpy.utils.str_to_bool.
  • Removed reactpy.utils.html_to_vdom. Use reactpy.utils.string_to_reactpy instead.
  • Removed reactpy.utils.vdom_to_html. Use reactpy.utils.reactpy_to_string instead.
  • Removed reactpy.vdom. Use reactpy.Vdom instead.
  • Removed reactpy.core.make_vdom_constructor. Use reactpy.Vdom instead.
  • Removed reactpy.core.custom_vdom_constructor. Use reactpy.Vdom instead.
  • Removed reactpy.Layout top-level re-export. Use reactpy.core.layout.Layout instead.
  • Removed reactpy.types.LayoutType. Use reactpy.types.BaseLayout instead.
  • Removed reactpy.types.ContextProviderType. Use reactpy.types.ContextProvider instead.
  • Removed reactpy.core.hooks._ContextProvider. Use reactpy.types.ContextProvider instead.
  • Removed reactpy.web.utils. Use reactpy.reactjs.utils instead.
"},{"location":"about/changelog/#fixed","title":"Fixed","text":"
  • Fixed a bug where script elements would not render to the DOM as plain text.
  • Fixed a bug where the key property provided within server-side ReactPy code was failing to propagate to the front-end JavaScript components.
  • Fixed a bug where RuntimeError(\"Hook stack is in an invalid state\") errors could be generated when using a webserver that reuses threads.
  • Fixed a bug where events on controlled inputs (e.g. html.input({\"onChange\": ...})) could be lost during rapid actions.
  • Allow for ReactPy and ReactJS components to be arbitrarily inserted onto the page with any possible hierarchy.
"},{"location":"about/changelog/#110-2024-11-24","title":"1.1.0 - 2024-11-24","text":""},{"location":"about/changelog/#fixed_1","title":"Fixed","text":"
  • Fixed broken module_from_template due to a recent release of requests.
  • Fixed module_from_template not working when using Flask backend.
  • Fixed UnicodeDecodeError when using reactpy.web.export.
  • Fixed needless unmounting of JavaScript components during each ReactPy render.
  • Fixed missing event[\"target\"][\"checked\"] on checkbox inputs.
  • Fixed missing static files on sdist Python distribution.
"},{"location":"about/changelog/#added_1","title":"Added","text":"
  • Allow concurrently rendering discrete component trees - enable this experimental feature by setting REACTPY_ASYNC_RENDERING=true. This improves the overall responsiveness of your app in situations where larger renders would otherwise block smaller renders from executing.
"},{"location":"about/changelog/#changed_1","title":"Changed","text":"
  • Previously None, when present in an HTML element, would render as the string \"None\". Now None will not render at all. This is now equivalent to how None is handled when returned from components.
  • Move hooks from reactpy.backend.hooks into reactpy.core.hooks.
"},{"location":"about/changelog/#deprecated_1","title":"Deprecated","text":"
  • The Stop exception. Recent releases of anyio have made this exception difficult to use since it now raises an ExceptionGroup. This exception was primarily used for internal testing purposes and so is now deprecated.
  • Deprecate reactpy.backend.hooks since the hooks have been moved into reactpy.core.hooks.
"},{"location":"about/changelog/#102-2023-07-03","title":"1.0.2 - 2023-07-03","text":""},{"location":"about/changelog/#fixed_2","title":"Fixed","text":"
  • Fix rendering bug when children change positions.
"},{"location":"about/changelog/#101-2023-06-16","title":"1.0.1 - 2023-06-16","text":""},{"location":"about/changelog/#changed_2","title":"Changed","text":"
  • Warn and attempt to fix missing mime types, which can result in reactpy.run not working as expected.
  • Rename reactpy.backend.BackendImplementation to reactpy.backend.BackendType.
  • Allow reactpy.run to fail in more predictable ways.
"},{"location":"about/changelog/#fixed_3","title":"Fixed","text":"
  • Better traceback for JSON serialization errors.
  • Explain that JS component attributes must be JSON.
  • Fix reactpy.run port assignment sometimes attaching to in-use ports on Windows.
  • Fix reactpy.run not recognizing fastapi.
"},{"location":"about/changelog/#100-2023-03-14","title":"1.0.0 - 2023-03-14","text":""},{"location":"about/changelog/#changed_3","title":"Changed","text":"
  • Reverts PR 841 as per the conclusion in discussion 916, but preserves the ability to declare attributes with snake_case.
  • Reverts PR 886 due to issue 896.
  • Revamped element constructor interface. Now instead of passing a dictionary of attributes to element constructors, attributes are declared using keyword arguments. For example, instead of writing:
"},{"location":"about/changelog/#deprecated_2","title":"Deprecated","text":"
  • Declaration of keys via keyword arguments in standard elements. A script has been added to automatically convert old usages where possible.
"},{"location":"about/changelog/#removed_1","title":"Removed","text":"
  • Accidental import of reactpy.testing.
"},{"location":"about/changelog/#fixed_4","title":"Fixed","text":"
  • Minor issues with camelCase rewrite CLI utility.
  • Minor type hint issue with VdomDictConstructor.
  • Stale event handlers after disconnect/reconnect cycle.
  • Fixed CLI not registered as entry point.
  • Unification of component and VDOM constructor interfaces.
"},{"location":"about/changelog/#0440-2023-01-27","title":"0.44.0 - 2023-01-27","text":""},{"location":"about/changelog/#deprecated_3","title":"Deprecated","text":"
  • reactpy.widgets.hotswap. The function has no clear uses outside of some internal applications.
"},{"location":"about/changelog/#removed_2","title":"Removed","text":"
  • Ability to access element value from events via event['value'] key. Use event['target']['value'] instead.
  • Old misspelled option REACTPY_WED_MODULES_DIR.
"},{"location":"about/changelog/#0430-2023-01-09","title":"0.43.0 - 2023-01-09","text":""},{"location":"about/changelog/#deprecated_4","title":"Deprecated","text":"
  • ComponentType.(). This method was implemented based on reading the React/Preact source code.
"},{"location":"about/changelog/#fixed_5","title":"Fixed","text":"
  • Nested context does not update value if outer context should not render.
  • Detached model state on render of context consumer if unmounted and context value does not change.
"},{"location":"about/changelog/#0420-2022-12-02","title":"0.42.0 - 2022-12-02","text":""},{"location":"about/changelog/#added_2","title":"Added","text":"
  • Ability to customize the <head> element of ReactPy's built-in client.
  • vdom_to_html utility function.
  • Ability to subscribe to changes that are made to mutable options.
  • del_html_head_body_transform to remove <html>, <head>, and <body> while preserving children.
  • Support for form element serialization
"},{"location":"about/changelog/#fixed_6","title":"Fixed","text":"
  • REACTPY_DEBUG_MODE is now mutable and can be changed at runtime.
  • Fix html_to_vdom improperly removing <html>, <head>, and <body> nodes.
"},{"location":"about/changelog/#removed_3","title":"Removed","text":"
  • Removed reactpy.html.body as it is currently unusable due to technological limitations.
  • Removed REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY option.
  • Removed serve_static_files option from backend configuration.
"},{"location":"about/changelog/#deprecated_5","title":"Deprecated","text":"
  • module_from_template.
"},{"location":"about/changelog/#0410-2022-11-01","title":"0.41.0 - 2022-11-01","text":""},{"location":"about/changelog/#changed_4","title":"Changed","text":"
  • The hooks use_location and use_scope are no longer implementation specific and are now available as top-level imports.
  • Backend implementations now strip any URL prefix in the pathname for use_location.
  • use_state now returns a named tuple with value and set_value fields.
"},{"location":"about/changelog/#added_3","title":"Added","text":"
  • New use_connection hook which returns a Connection object containing location, scope, and carrier.
"},{"location":"about/changelog/#0402-2022-09-13","title":"0.40.2 - 2022-09-13","text":""},{"location":"about/changelog/#changed_5","title":"Changed","text":"
  • Avoid the use of JSON patch for diffing models.
"},{"location":"about/changelog/#0401-2022-09-11","title":"0.40.1 - 2022-09-11","text":""},{"location":"about/changelog/#fixed_7","title":"Fixed","text":"
  • Child models after a component fail to render.
"},{"location":"about/changelog/#0400-2022-08-13","title":"0.40.0 - 2022-08-13","text":""},{"location":"about/changelog/#fixed_8","title":"Fixed","text":"
  • Fix edge cases where html_to_vdom can fail to convert HTML.
  • Conditionally rendered components cannot use contexts.
  • Use strict equality check for text, numeric, and binary types in hooks.
  • Accidental mutation of old model causes invalid JSON Patch.
"},{"location":"about/changelog/#changed_6","title":"Changed","text":"
  • Set default timeout on Playwright page for testing.
  • Track contexts in hooks as state.
  • Remove non-standard name argument from create_context.
"},{"location":"about/changelog/#added_4","title":"Added","text":"
  • asgiref as a dependency.
  • lxml as a dependency.
"},{"location":"about/changelog/#0390-2022-06-20","title":"0.39.0 - 2022-06-20","text":""},{"location":"about/changelog/#fixed_9","title":"Fixed","text":"
  • No module named 'reactpy.server' from reactpy.run.
  • Setting appropriate MIME type for web modules in sanic server implementation.
"},{"location":"about/changelog/#changed_7","title":"Changed","text":"
  • Renamed various: reactpy.testing.server to reactpy.testing.backend, ServerFixture to BackendFixture, DisplayFixture.server to DisplayFixture.backend.
  • Removed exports_default parameter from module_from_template.
"},{"location":"about/changelog/#added_5","title":"Added","text":"
  • Ability to specify versions with module templates (e.g. module_from_template(\"react@^17.0.0\", ...)).
"},{"location":"about/changelog/#0381-2022-04-15","title":"0.38.1 - 2022-04-15","text":""},{"location":"about/changelog/#fixed_10","title":"Fixed","text":"
  • Missing file extension was causing a problem with WebPack.
"},{"location":"about/changelog/#0380-2022-04-15","title":"0.38.0 - 2022-04-15","text":"

No changes.

"},{"location":"about/changelog/#0372-2022-03-27","title":"0.37.2 - 2022-03-27","text":""},{"location":"about/changelog/#changed_8","title":"Changed","text":"
  • Renamed proto modules to types and added a top-level reactpy.types module.
"},{"location":"about/changelog/#fixed_11","title":"Fixed","text":"
  • Fixed a typo that caused ReactPy to use the insecure ws web-socket protocol on pages loaded with https instead of the secure wss protocol.
"},{"location":"about/changelog/#0371-2022-03-05","title":"0.37.1 - 2022-03-05","text":"

No changes.

"},{"location":"about/changelog/#0370-2022-02-27","title":"0.37.0 - 2022-02-27","text":""},{"location":"about/changelog/#added_6","title":"Added","text":"
  • Support for keys in HTML fragments.
  • Use Context Hook.
"},{"location":"about/changelog/#fixed_12","title":"Fixed","text":"
  • React warning about set state in unmounted component.
  • Missing reset of schedule_render_later flag.
"},{"location":"about/changelog/#0363-2022-02-18","title":"0.36.3 - 2022-02-18","text":""},{"location":"about/changelog/#fixed_13","title":"Fixed","text":"
  • All child states wiped upon any child key change.
  • Allow NoneType returns within components.
"},{"location":"about/changelog/#0362-2022-02-02","title":"0.36.2 - 2022-02-02","text":""},{"location":"about/changelog/#fixed_14","title":"Fixed","text":"
  • Hot fix for newly introduced DeprecatedOption.
"},{"location":"about/changelog/#0361-2022-02-02","title":"0.36.1 - 2022-02-02","text":""},{"location":"about/changelog/#fixed_15","title":"Fixed","text":"
  • Fix Key Error when Cleaning Up Event Handlers.
  • Update Script Tag Behavior.
"},{"location":"about/changelog/#changed_9","title":"Changed","text":"
  • Renamed configuration option REACTPY_WED_MODULES_DIR to REACTPY_WEB_MODULES_DIR.
"},{"location":"about/changelog/#0360-2022-01-30","title":"0.36.0 - 2022-01-30","text":""},{"location":"about/changelog/#added_7","title":"Added","text":"
  • New http.script element which can behave similarly to a standard HTML <script> or, if no attributes are given, operate similarly to an effect.
"},{"location":"about/changelog/#fixed_16","title":"Fixed","text":"
  • State mismatch during component update.
  • Implement a script tag.
"},{"location":"about/changelog/#0354-2022-01-27","title":"0.35.4 - 2022-01-27","text":""},{"location":"about/changelog/#fixed_17","title":"Fixed","text":"
  • Keys for elements at the root of a component were not being tracked. Thus key changes for elements at the root did not trigger unmounts.
"},{"location":"about/changelog/#0353-2022-01-27","title":"0.35.3 - 2022-01-27","text":""},{"location":"about/changelog/#changed_10","title":"Changed","text":"
  • Elements which changed type are now always deeply unmounted.
"},{"location":"about/changelog/#0352-2022-01-26","title":"0.35.2 - 2022-01-26","text":""},{"location":"about/changelog/#fixed_18","title":"Fixed","text":"
  • Allow children with the same key to vary in type.
  • Client always looks for server at \"/\".
  • Web modules get double file extensions with v0.35.x.
"},{"location":"about/changelog/#0351-2022-01-18","title":"0.35.1 - 2022-01-18","text":""},{"location":"about/changelog/#fixed_19","title":"Fixed","text":"
  • Re-add accidentally deleted py.typed file to distribution.
"},{"location":"about/changelog/#0350-2022-01-18","title":"0.35.0 - 2022-01-18","text":""},{"location":"about/changelog/#added_8","title":"Added","text":"
  • Default key for all elements and components is now their index amongst their siblings.
  • use_linked_inputs widget.
  • Starlette server implementation.
"},{"location":"about/changelog/#changed_11","title":"Changed","text":"
  • REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY now defaults to 1.
"},{"location":"about/changelog/#fixed_20","title":"Fixed","text":"
  • Support Starlette Server.
  • Fix unhandled case in module_from_template.
  • Hide \"Children\" within REACTPY_DEBUG_MODE key warnings.
  • Bug in Element Key Identity.
  • Add iFrame to reactpy.html.
  • React warning from module_from_template.
"},{"location":"about/changelog/#0340-2021-12-16","title":"0.34.0 - 2021-12-16","text":""},{"location":"about/changelog/#changed_12","title":"Changed","text":"
  • Element value must now be accessed via event['target']['value'] instead of event['value'].
  • Added event[\"currentTarget\"] and event[\"relatedTarget\"] keys to the event dictionary.
"},{"location":"about/changelog/#fixed_21","title":"Fixed","text":"
  • Move target attributes to event['target'].
"},{"location":"about/changelog/#0333-2021-10-08","title":"0.33.3 - 2021-10-08","text":""},{"location":"about/changelog/#added_9","title":"Added","text":"
  • Warning stating that REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY=1 will become the default in a future release.
  • Ability to use the default export from a JavaScript module when calling module_from_template by specifying exports_default=True.
"},{"location":"about/changelog/#fixed_22","title":"Fixed","text":"
  • Memory leak in SharedClientStateServer.
  • Cannot use default export in react template.
"},{"location":"about/changelog/#0332-2021-09-05","title":"0.33.2 - 2021-09-05","text":""},{"location":"about/changelog/#fixed_23","title":"Fixed","text":"
  • Memory leak caused by event handlers that were not being removed when components updated.
"},{"location":"about/changelog/#0331-2021-09-02","title":"0.33.1 - 2021-09-02","text":""},{"location":"about/changelog/#fixed_24","title":"Fixed","text":"
  • Regression where the root element of the layout could not be updated.
"},{"location":"about/changelog/#0330-2021-09-02","title":"0.33.0 - 2021-09-02","text":""},{"location":"about/changelog/#fixed_25","title":"Fixed","text":"
  • Client-side error in mount-01d35dc3.js.
  • Style cannot be updated.
  • Displaying error messages in the client via __error__ tag can leak secrets.
  • Examples broken in docs.
"},{"location":"about/changelog/#changed_13","title":"Changed","text":"
  • Modified the Custom JavaScript Component interface to add a create() function to the bind() interface.
"},{"location":"about/changelog/#0320-2021-08-20","title":"0.32.0 - 2021-08-20","text":""},{"location":"about/changelog/#changed_14","title":"Changed","text":"
  • Custom component interface now expects a single bind() function that returns an object with render() and unmount() functions.
"},{"location":"about/changelog/#fixed_26","title":"Fixed","text":"
  • Docs broken on Firefox.
  • URL resolution for web modules does not consider urls starting with /.
  • Query params in package name for module_from_template not stripped.
  • Search broken in docs.
  • Move src/reactpy/client out of Python package.
"},{"location":"about/changelog/#0310-2021-07-14","title":"0.31.0 - 2021-07-14","text":""},{"location":"about/changelog/#changed_15","title":"Changed","text":"
  • Layout is now a prototype and Layout.update is no longer a public API.
  • Refactored the relationship between LifeCycleHook and Layout.
  • ComponentTypes no longer needs a unique id attribute.
"},{"location":"about/changelog/#0301-2021-07-13","title":"0.30.1 - 2021-07-13","text":""},{"location":"about/changelog/#fixed_27","title":"Fixed","text":"
  • Warning if key is parameter of component render function.
"},{"location":"about/changelog/#0300-2021-06-28","title":"0.30.0 - 2021-06-28","text":""},{"location":"about/changelog/#changed_16","title":"Changed","text":"
  • Removed all runtime reliance on NPM.
  • reactpy.client is removed in favor of a stripped down reactpy.web module.
  • REACTPY_CLIENT_BUILD_DIR config option is replaced with REACTPY_WEB_MODULES_DIR.
"},{"location":"about/changelog/#0290-2021-06-20","title":"0.29.0 - 2021-06-20","text":""},{"location":"about/changelog/#changed_17","title":"Changed","text":"
  • Moved the runtime client build directory to a \"user data\" directory.
  • Custom JS component interface has been reworked.
"},{"location":"about/changelog/#0280-2021-06-01","title":"0.28.0 - 2021-06-01","text":""},{"location":"about/changelog/#added_10","title":"Added","text":"
  • Support for currentTime attribute of audio/video elements.
  • Support for the files attribute from the target of input elements.
  • Model children are now passed to the JavaScript mount() function.
  • mountLayoutWithWebSocket function to @reactpy/client.
"},{"location":"about/changelog/#changed_18","title":"Changed","text":"
  • Refactored existing server implementations as functions adhering to a protocol.
  • Switched to a monorepo-style structure for JavaScript.
  • Use a loadImportSource() function instead of trying to infer the path to dynamic modules.
"},{"location":"about/changelog/#0270-2021-05-14","title":"0.27.0 - 2021-05-14","text":""},{"location":"about/changelog/#added_11","title":"Added","text":"
  • mount(element, component, props) function which can be used to bind new elements to the DOM.
"},{"location":"about/changelog/#0260-2021-05-07","title":"0.26.0 - 2021-05-07","text":""},{"location":"about/changelog/#fixed_28","title":"Fixed","text":"
  • Fix for situations where a Layout can raise an error when a component whose state has been deleted is rendered.
"},{"location":"about/changelog/#0250-2021-04-30","title":"0.25.0 - 2021-04-30","text":""},{"location":"about/changelog/#changed_19","title":"Changed","text":"
  • Refactored layout dispatcher by switching from a class-based approach to one that leverages pure functions.
"},{"location":"about/changelog/#0240-2021-04-18","title":"0.24.0 - 2021-04-18","text":""},{"location":"about/changelog/#added_12","title":"Added","text":"
  • Components and elements can now have \"identity\" - their state can be preserved across updates.
  • REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY feature flag.
"},{"location":"about/changelog/#0231-2021-04-02","title":"0.23.1 - 2021-04-02","text":""},{"location":"about/changelog/#fixed_29","title":"Fixed","text":"
  • Non-deterministic return order in install().
"},{"location":"about/changelog/#0230-2021-04-01","title":"0.23.0 - 2021-04-01","text":""},{"location":"about/changelog/#added_13","title":"Added","text":"
  • Changelog to docs.
  • Automatically reconnect to server.
  • Allow no reconnect in client.
  • Cleaner way to specify import sources.
  • FastAPI render server.
"},{"location":"about/changelog/#0221-2021-01-02","title":"0.22.1 - 2021-01-02","text":""},{"location":"about/changelog/#fixed_30","title":"Fixed","text":"
  • Fix missing find_available_port arg.
"},{"location":"about/changelog/#0220-2020-12-24","title":"0.22.0 - 2020-12-24","text":""},{"location":"about/changelog/#fixed_31","title":"Fixed","text":"
  • Fix doc build.
  • Better server error capture in tests.
  • Fix type annotations.
  • Fix dark mode widget view.
"},{"location":"about/changelog/#changed_20","title":"Changed","text":"
  • Use Nox to run tests.
  • Add .nox to gitignore.
"},{"location":"about/changelog/#0210-2020-12-16","title":"0.21.0 - 2020-12-16","text":""},{"location":"about/changelog/#added_14","title":"Added","text":"
  • Tornado render server.
  • Render server documentation.
"},{"location":"about/changelog/#changed_21","title":"Changed","text":"
  • Renamed element to component.
"},{"location":"about/changelog/#0201-2020-11-13","title":"0.20.1 - 2020-11-13","text":""},{"location":"about/changelog/#fixed_32","title":"Fixed","text":"
  • Avoid sanic import.
"},{"location":"about/changelog/#0200-2020-11-12","title":"0.20.0 - 2020-11-12","text":""},{"location":"about/changelog/#added_15","title":"Added","text":"
  • Flask render server.
"},{"location":"about/changelog/#changed_22","title":"Changed","text":"
  • Moved idom_client_react into separate repo.
  • Removed mountLayoutWithWebSocket.
  • Removed more client implementation docs.
"},{"location":"about/changelog/#fixed_33","title":"Fixed","text":"
  • Fix missing server event.
"},{"location":"about/changelog/#0190-2020-10-19","title":"0.19.0 - 2020-10-19","text":""},{"location":"about/changelog/#fixed_34","title":"Fixed","text":"
  • No cover last server error.
"},{"location":"about/changelog/#changed_23","title":"Changed","text":"
  • Refined test tooling.
  • Added all to testing module.
"},{"location":"about/changelog/#0181-2020-09-25","title":"0.18.1 - 2020-09-25","text":""},{"location":"about/changelog/#fixed_35","title":"Fixed","text":"
  • Fix missing find_available_port arg.
"},{"location":"about/changelog/#0180-2020-09-23","title":"0.18.0 - 2020-09-23","text":""},{"location":"about/changelog/#added_16","title":"Added","text":"
  • Module components should be accessible as attrs.
"},{"location":"about/changelog/#fixed_36","title":"Fixed","text":"
  • Fix doc import source URL.
  • Minor JS fixes.
  • Fix idom install.
  • Better URL join logic in client.
"},{"location":"about/changelog/#changed_24","title":"Changed","text":"
  • Strip down test tooling.
  • Avoid issues with bundlers.
"},{"location":"about/changelog/#0171-2020-08-14","title":"0.17.1 - 2020-08-14","text":""},{"location":"about/changelog/#fixed_37","title":"Fixed","text":"
  • Fix idom install.
"},{"location":"about/changelog/#0170-2020-08-08","title":"0.17.0 - 2020-08-08","text":""},{"location":"about/changelog/#added_17","title":"Added","text":"
  • Import source base URL with vdom fallback.
"},{"location":"about/changelog/#fixed_38","title":"Fixed","text":"
  • Fix types.
  • Fix mypy testing.
"},{"location":"about/changelog/#changed_25","title":"Changed","text":"
  • Bumped idom_client_react version.
  • Use universal wheel.
"},{"location":"about/changelog/#0160-2020-06-26","title":"0.16.0 - 2020-06-26","text":""},{"location":"about/changelog/#fixed_39","title":"Fixed","text":"
  • Put setuptools_scm back in setup.py.
  • Fix pyproject.toml.
"},{"location":"about/changelog/#changed_26","title":"Changed","text":"
  • Remove framework classifier.
  • Only check last error in display context.
  • Refactor and cleanup tests.
  • Add public test tooling.
"},{"location":"about/changelog/#0150-2020-05-02","title":"0.15.0 - 2020-05-02","text":""},{"location":"about/changelog/#added_18","title":"Added","text":"
  • More docs.
  • pyproject.toml support.
"},{"location":"about/changelog/#fixed_40","title":"Fixed","text":"
  • Fix coverage.
  • Fix misc bugs.
  • Fix mypy.
"},{"location":"about/changelog/#changed_27","title":"Changed","text":"
  • Improve docs.
  • Improve log messages.
  • Rename config \"item\" to \"entry\".
  • Upgrade snowpack to max possible.
  • More tests for client management.
"},{"location":"about/changelog/#0143-2020-04-10","title":"0.14.3 - 2020-04-10","text":""},{"location":"about/changelog/#fixed_41","title":"Fixed","text":"
  • Fix JSON patch in-place (broke some components).
  • Revert bad JSON patch usage.
"},{"location":"about/changelog/#0142-2020-03-30","title":"0.14.2 - 2020-03-30","text":""},{"location":"about/changelog/#fixed_42","title":"Fixed","text":"
  • Fix displayed version in docs.
"},{"location":"about/changelog/#0141-2020-03-20","title":"0.14.1 - 2020-03-20","text":""},{"location":"about/changelog/#fixed_43","title":"Fixed","text":"
  • Remove extra spaces in element children.
"},{"location":"about/changelog/#0140-2020-03-20","title":"0.14.0 - 2020-03-20","text":""},{"location":"about/changelog/#added_19","title":"Added","text":"
  • idom.run utility.
"},{"location":"about/changelog/#fixed_44","title":"Fixed","text":"
  • Fix types.
  • Fix doc examples.
  • Simplify server constructor.
"},{"location":"about/changelog/#0130-2020-02-15","title":"0.13.0 - 2020-02-15","text":""},{"location":"about/changelog/#added_20","title":"Added","text":"
  • Well-defined client implementation layer.
"},{"location":"about/changelog/#fixed_45","title":"Fixed","text":"
  • Fix types.
  • Fix tests.
"},{"location":"about/changelog/#changed_28","title":"Changed","text":"
  • Remove ability to install modules at runtime via idom.Module.
"},{"location":"about/changelog/#0120-2020-01-27","title":"0.12.0 - 2020-01-27","text":""},{"location":"about/changelog/#added_21","title":"Added","text":"
  • Utility methods for LayoutUpdate (start, stop).
  • useStateRef hook.
"},{"location":"about/changelog/#changed_29","title":"Changed","text":"
  • Move Jupyter out of base idom into idom-jupyter package.
"},{"location":"about/changelog/#0113-2020-01-14","title":"0.11.3 - 2020-01-14","text":""},{"location":"about/changelog/#fixed_46","title":"Fixed","text":"
  • Add utility method tests to LayoutUpdate.
"},{"location":"about/changelog/#0112-2020-01-14","title":"0.11.2 - 2020-01-14","text":""},{"location":"about/changelog/#added_22","title":"Added","text":"
  • Test dispatcher start and stop.
"},{"location":"about/changelog/#0111-2019-12-20","title":"0.11.1 - 2019-12-20","text":""},{"location":"about/changelog/#changed_30","title":"Changed","text":"
  • Bump JS to 0.4.2.
"},{"location":"about/changelog/#0110-2019-12-18","title":"0.11.0 - 2019-12-18","text":""},{"location":"about/changelog/#added_23","title":"Added","text":"
  • MountLayout utility function.
"},{"location":"about/changelog/#fixed_47","title":"Fixed","text":"
  • Fix issues.
  • Correct package.json for idom-client-react.
"},{"location":"about/changelog/#changed_31","title":"Changed","text":"
  • Rename StateDispatcher to ViewDispatcher.
  • Remove private=true from imports.
  • Require anyio>=2.0.
  • Rename static/ directory to app/.
"},{"location":"about/changelog/#0104-2019-12-01","title":"0.10.4 - 2019-12-01","text":""},{"location":"about/changelog/#fixed_48","title":"Fixed","text":"
  • Fix docs.
"},{"location":"about/changelog/#0103-2019-12-01","title":"0.10.3 - 2019-12-01","text":""},{"location":"about/changelog/#fixed_49","title":"Fixed","text":"
  • Publish package before docs.
"},{"location":"about/changelog/#0102-2019-11-20","title":"0.10.2 - 2019-11-20","text":""},{"location":"about/changelog/#fixed_50","title":"Fixed","text":"
  • Fix up tests.
"},{"location":"about/changelog/#added_24","title":"Added","text":"
  • Material UI example.
  • Concept of build cache.
"},{"location":"about/changelog/#0101-2019-11-08","title":"0.10.1 - 2019-11-08","text":""},{"location":"about/changelog/#fixed_51","title":"Fixed","text":"
  • Upgrade anyio (see https://github.com/agronholm/anyio/issues/155).
"},{"location":"about/changelog/#0100-2019-11-07","title":"0.10.0 - 2019-11-07","text":""},{"location":"about/changelog/#added_25","title":"Added","text":"
  • Fallbacks to imported JS in examples.
"},{"location":"about/changelog/#fixed_52","title":"Fixed","text":"
  • Fix doc updates.
  • Better URL join logic.
"},{"location":"about/changelog/#changed_32","title":"Changed","text":"
  • Removed async event handler.
  • Renamed Renderer to Dispatcher.
  • Removed AbstractElement.id.
"},{"location":"about/changelog/#092-2019-10-14","title":"0.9.2 - 2019-10-14","text":""},{"location":"about/changelog/#fixed_53","title":"Fixed","text":"
  • Fix docs and improve doc example runner.
"},{"location":"about/changelog/#091-2019-10-10","title":"0.9.1 - 2019-10-10","text":""},{"location":"about/changelog/#fixed_54","title":"Fixed","text":"
  • Add setuptools_scm as docs requirement.
"},{"location":"about/changelog/#090-2019-10-06","title":"0.9.0 - 2019-10-06","text":""},{"location":"about/changelog/#added_26","title":"Added","text":"
  • Documentation for async effects.
  • Fix effect cleanup timing.
  • Test async effect.
"},{"location":"about/changelog/#fixed_55","title":"Fixed","text":"
  • Fix layout test.
  • Fix type annotations.
"},{"location":"about/changelog/#changed_33","title":"Changed","text":"
  • Element render functions don't need to be async.
  • Dropped Python 3.6 support.
"},{"location":"about/changelog/#070-2020-08-09","title":"0.7.0 - 2020-08-09","text":""},{"location":"about/changelog/#added_27","title":"Added","text":"
  • use_reducer hook.
  • use_effect tests.
  • Tests for use_ref and use_callback.
  • HookCatcher utility.
  • Layout tests.
"},{"location":"about/changelog/#fixed_56","title":"Fixed","text":"
  • Fix bug in use_memo.
  • Fix types for render and layout.
  • Fix flake8 ignore doc examples.
"},{"location":"about/changelog/#changed_34","title":"Changed","text":"
  • Rename top level examples dir to notebooks.
  • Remove unused test script.
  • Effects run just before and after full render.
  • Simplified rendering queue.
  • Removed abstract layout (renamed to Layout.dispatch).
"},{"location":"about/changelog/#removed_4","title":"Removed","text":"
  • Drag-drop example.
"},{"location":"about/changelog/#060-2020-06-20","title":"0.6.0 - 2020-06-20","text":""},{"location":"about/changelog/#fixed_57","title":"Fixed","text":"
  • Various bug fixes from early development.
"},{"location":"about/changelog/#added_28","title":"Added","text":"
  • Core infrastructure for component rendering.
"},{"location":"about/changelog/#051-2019-10-24","title":"0.5.1 - 2019-10-24","text":""},{"location":"about/changelog/#fixed_58","title":"Fixed","text":"
  • Fix tests and event handlers.
"},{"location":"about/changelog/#050-2019-06-13","title":"0.5.0 - 2019-06-13","text":""},{"location":"about/changelog/#added_29","title":"Added","text":"
  • use_effect hook.
  • Initial idom-jupyter support.
  • Basic client-side rendering.
  • createContext support.
  • useCallback and useMemo.
  • Initial testing infrastructure.
"},{"location":"about/changelog/#fixed_59","title":"Fixed","text":"
  • Fix various rendering and state management bugs.
"},{"location":"about/changelog/#removed_5","title":"Removed","text":"
  • Legacy state management.
"},{"location":"about/changelog/#042-2019-03-20","title":"0.4.2 - 2019-03-20","text":""},{"location":"about/changelog/#fixed_60","title":"Fixed","text":"
  • Revert \"make vdom a simple dict\".
"},{"location":"about/changelog/#041-2019-02-26","title":"0.4.1 - 2019-02-26","text":""},{"location":"about/changelog/#fixed_61","title":"Fixed","text":"
  • Fix tests.
"},{"location":"about/changelog/#040-2019-02-02","title":"0.4.0 - 2019-02-02","text":""},{"location":"about/changelog/#added_30","title":"Added","text":"
  • Core functionality for idom.
"},{"location":"about/changelog/#changed_35","title":"Changed","text":"
  • Restructure around idom-core and idom-legacy.
"},{"location":"about/changelog/#030-2018-12-18","title":"0.3.0 - 2018-12-18","text":""},{"location":"about/changelog/#added_31","title":"Added","text":"
  • Core functionality with hooks (useState, useEffect, etc.).
"},{"location":"about/changelog/#fixed_62","title":"Fixed","text":"
  • Fix initial rendering issues.
"},{"location":"about/changelog/#020-2018-10-22","title":"0.2.0 - 2018-10-22","text":""},{"location":"about/changelog/#added_32","title":"Added","text":"
  • Basic functionality.
"},{"location":"about/changelog/#fixed_63","title":"Fixed","text":"
  • Initial bug fixes.
"},{"location":"about/changelog/#012-2018-08-03","title":"0.1.2 - 2018-08-03","text":""},{"location":"about/changelog/#fixed_64","title":"Fixed","text":"
  • Fix import issues.
"},{"location":"about/changelog/#010-2018-07-30","title":"0.1.0 - 2018-07-30","text":""},{"location":"about/changelog/#added_33","title":"Added","text":"
  • Initial release.
"},{"location":"about/code/","title":"Contributing Code","text":""},{"location":"about/code/#overview","title":"Overview","text":"

ReactPy takes pull requests from anyone, and most of what gets rejected is not wrong \u2014 it is unreviewable. A short branch, a focused diff, and the checks run before you open the pull request is what makes a change easy to say yes to. This page covers the mechanics: where the code lives, how to branch, and what CI will run against you.

You will learn

  • Which branch a change belongs on, and why that matters here more than elsewhere
  • The commit and pull request conventions this repository enforces
  • What CI checks, before CI tells you
"},{"location":"about/code/#know-which-repository-you-are-in","title":"Know which repository you are in","text":"

This documentation site and the library are separate bodies of work, and the commands that work in one do nothing in the other.

Change Repository / branch Library code, hooks, layout, backends reactive-python/reactpy \u2192 main JavaScript client (src/js/) Same, main These documentation pages (docs/src/, docs/examples/) reactive-python/reactpy \u2192 new-docs

The new-docs branch holds only docs/ \u2014 the package and tests/ were removed from it. A test command run there reports success without running anything.

Pitfall

Confirm the branch before you start typing, not after. A library fix committed onto the docs branch looks fine locally and becomes a rebase chore later. git branch --show-current costs nothing and settles it.

"},{"location":"about/code/#before-you-start","title":"Before you start","text":"

Look for an open discussion or issue that matches what you intend to do. If one exists, comment there before writing code \u2014 a maintainer who has already thought about the problem will usually redirect you in one sentence, and that sentence is cheaper than a rejected pull request. If none exists, open a discussion first.

For anything larger than a bug fix, expect the answer to be \"show me the design first\". A draft pull request is the right medium for that: it runs CI, it is reviewable, and it says plainly that it is not ready to merge.

"},{"location":"about/code/#branch-commit-push","title":"Branch, commit, push","text":"

Fork the repository if you do not have write access, then branch from an up-to-date main:

git checkout -b fix-my-change main\n

Name the branch for the change, not for you. fix-key-not-resetting-on-provider beats patch-1 and survives a month of review.

Match the convention of the branch you are landing on; they differ. Documentation commits on new-docs use Conventional Commits subjects \u2014 docs(learn): author X for ReactPy v2, docs(examples): fix include-marker cropping. Library history on main instead uses plain sentence subjects, because pull requests there are squash-merged and the summary is written at merge time \u2014 Fix PyScript root component not hiding after render error (#1347). In both cases the subject is the thing a reader scans to find the functional change in a long history, so write it for that reader.

One commit per logical change. If a commit only makes sense next to a later one, squash them before you push rather than asking the reviewer to mentally reassemble them.

"},{"location":"about/code/#the-checklist","title":"The checklist","text":"

The repository ships a pull request template; fill it in rather than deleting it.

  • [ ] Tests have been developed for bug fixes or new functionality.
  • [ ] The changelog has been updated, if necessary.
  • [ ] Documentation has been updated, if necessary.
  • [ ] GitHub Issues closed by this pull request have been linked.

By submitting, you agree that your contribution complies with the project's open source licenses.

Tests are not optional for a behaviour change. A bug fix should arrive with the test that reproduces the bug \u2014 write it first and watch it fail, then fix and watch it pass. A test that has never failed proves nothing about the thing it claims to protect.

The changelog rule is narrower than it looks: CHANGELOG.md gets an entry under Unreleased for user-facing changes only. Test-only, docs-only, and CI-only changes get none. Adding one for those is a small annoyance to the person doing the release, who then has to notice and delete it. See Changelog.

"},{"location":"about/code/#what-ci-runs","title":"What CI runs","text":"

Understanding the gate is cheaper than discovering it in a failed run.

hatch test --parallel --cover --retries 10          # coverage job\nhatch test --parallel --retries 10                  # ubuntu, macos, windows \u00d7 3.11\u20133.14\nhatch fmt src/reactpy --check                       # lint + format\nhatch run python:type_check                         # pyright\nhatch run javascript:test\nhatch run javascript:check\n

Run the equivalents locally before you push \u2014 the same commands, not approximations of them:

hatch fmt --check\nhatch run python:type_check\nhatch test -k the_test_you_touched\n

CI retries flaky tests, which is a mercy and a trap: a genuinely racy test can go green on the third attempt and stay in the tree. If a test fails once and passes on retry, that is a bug in the test \u2014 usually a bare asyncio.sleep where poll(...) belongs.

A pull request into new-docs gets no CI at all, for two independent reasons. GitHub runs pull_request workflows from the base branch, and the only workflow that exists on new-docs is the docs deploy, which triggers on push \u2014 so nothing matches a PR. And the test-documentation job in check.yml on main is disabled outright with if: 0 (temporarily, while the Sphinx-to-MkDocs transition finishes), so even a PR targeting main runs lint and Python tests but never builds the docs. A docs change is verified by you or by nobody \u2014 run the mkdocs build --strict set from Running Tests and read its exit code. The one thing that does run: pushing to new-docs deploys with mkdocs gh-deploy --force, no --strict \u2014 expect a change to be live the moment it merges, whether or not it builds cleanly.

"},{"location":"about/code/#working-with-reviewers","title":"Working with reviewers","text":"

Respond to every comment, including the ones you are not acting on. \"Not taking this because X\" is a complete reply and saves a round trip; silence invites a re-review of something you already considered.

Push fixes as new commits rather than force-pushing over the reviewed range. Reviewers read the delta between your last push and this one; a force-push that rewrites history they already read makes them re-read the whole diff, and they will notice when they skip it.

If a review asks for something you believe is wrong, say so once, with evidence, and then defer if the maintainer still wants it. Review is a conversation with an asymmetry: they own the file after you leave.

Note

When a review is posted by an automated tool, the maintainers still own the decision. Do not treat \"the bot did not complain\" as approval, and do not treat a bot comment as a demand \u2014 respond on the merits, the same as any other comment.

"},{"location":"about/code/#after-merge","title":"After merge","text":"

Documentation changes on new-docs deploy to the live site on push, so a mistake there is visible immediately rather than at the next release. That is a reason to run the build check locally, not a reason to be nervous about contributing.

Library changes reach users only through a release, which the maintainers cut. Do not ask when your fix will ship, and do not close a closed-issue loop by demanding a release \u2014 the tag is a maintainer's call.

"},{"location":"about/code/#recap","title":"Recap","text":"
  • Library work on main; documentation work on new-docs. Check the branch first.
  • Start from a discussion; a one-sentence redirect beats a rejected pull request.
  • Conventional Commit subjects, one logical change per commit, branch named for the change.
  • Tests accompany behaviour changes, and they must have failed first.
  • Changelog entries for user-facing changes only.
  • Run CI's exact commands locally before pushing; treat a flaky test as a bug.
"},{"location":"about/code/#related","title":"Related","text":"
  • Running Tests \u2014 the commands, and how to read their failures
  • Contributing Documentation \u2014 page conventions and the examples workflow
  • Changelog \u2014 format, categories, and what counts as user-facing
  • Community \u2014 where to raise the issue before you write the fix
"},{"location":"about/community/","title":"Community","text":""},{"location":"about/community/#overview","title":"Overview","text":"

ReactPy is built in the open. Questions, bug reports, and ideas all have a home, and the same places are where you get help when an example on this site stops behaving. This page is a map of those places and what belongs in each.

You will learn

  • Where to ask a question, report a bug, and propose a feature
  • Which channels are official, and which are just links people repeat
  • What the code of conduct expects, and what to include in a report
"},{"location":"about/community/#where-everything-happens","title":"Where everything happens","text":"

The ReactPy repository on GitHub is the centre of gravity. Everything else points back to it.

You want to Go to Ask how to do something Discussions \u2192 Question Report something broken Discussions \u2192 Problem Propose a feature Discussions \u2192 Ideas Show off what you built Discussions \u2192 Share Publish a package you made Discussions \u2192 Downloadable Packages Track a confirmed defect Issues Chat in real time Discord

Discussions is the front door, not Issues. The repository's issue-template configuration disables blank issues and points new visitors at Discussions instead, so a question opened as an issue gets redirected rather than answered. Open an issue once a defect is confirmed and someone has reproduced it \u2014 the discussion that came first usually becomes the link inside it.

"},{"location":"about/community/#chat","title":"Chat","text":"

There is a Discord server for ReactPy, announced in Discussion #997 and linked from the repository README. It is the fastest place to get an answer to a \"why is this doing that\" question, and quiet enough that nobody minds a beginner question.

Note

An invite URL is mutable third-party state. If discord.gg/uNb5P4hA9X ever stops resolving, read the current invite from the README badge or from Discussion #997 \u2014 those two are maintained together \u2014 rather than trusting a search result, which may point at a cloned server.

Discord is good for conversation and bad for searchability. A solution that exists only in chat is invisible to the next person with the same problem. If a chat thread solves something real, post it to Discussions afterwards; if it exposes an error on this site, that is a documentation contribution.

"},{"location":"about/community/#code-of-conduct","title":"Code of conduct","text":"

Participation is governed by the Contributor Covenant code of conduct, which lives at the repository root. It applies everywhere the project is discussed \u2014 Discussions, Issues, pull requests, Discord, and any other space where ReactPy maintainers show up.

The practical version: assume good faith, criticise code rather than people, and accept that \"this is obvious\" is rarely true of the person asking. The enforcement section of the document names the contact route for escalating a problem.

"},{"location":"about/community/#packages-outside-this-repository","title":"Packages outside this repository","text":"

ReactPy's core deliberately stays small, so much of what you might expect to find here lives in sibling repositories. Each has its own issue tracker \u2014 do not file a Django problem against the core repo.

  • reactpy-router \u2014 URL routing
  • reactpy-django \u2014 Django integration
  • reactpy-jupyter \u2014 Jupyter integration
  • reactpy-js-component-template \u2014 starting point for a custom JavaScript component

Community-built packages are announced in Discussions \u2192 Downloadable Packages. Those are third-party: they are not reviewed before being posted, they are not pinned against a ReactPy release, and an unanswered thread there says nothing about the library. See Tools, Libraries, and Packages for how to evaluate one.

"},{"location":"about/community/#supporting-the-project","title":"Supporting the project","text":"

ReactPy is maintained by volunteers on their own time. GitHub Sponsors is the route the project uses, matching the maintainers listed in the repository's funding configuration.

Sponsoring buys maintenance, not priority. It does not queue your issue and does not entitle anyone to a feature. If you need a specific behaviour on a deadline, the reliable path is to write it \u2014 or fund someone to write it \u2014 and open a pull request.

"},{"location":"about/community/#what-to-include-when-you-ask","title":"What to include when you ask","text":"

Most unanswered questions are unanswered because they cannot be reproduced. Four things turn a vague report into one someone can act on:

  • The exact ReactPy version. Run pip show reactpy and paste it. Behaviour shifts between pre-releases, so 2.0.0b13 and 2.0.0 are different answers.
  • The Python version and operating system.
  • The smallest snippet that still misbehaves. Cutting an app down to one component usually finds the bug before you post it.
  • Both sides of the comparison: what happened, and what you expected. For a rendering problem, include what the browser showed, not only what the code says.

Pitfall

\"It doesn't work\" with no traceback is not a question anyone can answer, and the error you think is irrelevant often is not the relevant one. Include the full traceback and the server-side console output. ReactPy logs failing event handlers and effects on the server while the browser stays silent, so a UI that merely stops responding usually has a server line that explains it \u2014 see Creating VDOM Event Handlers.

"},{"location":"about/community/#recap","title":"Recap","text":"
  • Ask in Discussions; open an issue only for a confirmed defect.
  • Real-time chat is on Discord; follow up anything durable in Discussions so it stays searchable.
  • The code of conduct governs every one of those spaces.
  • Integration packages live in sibling repos; file bugs against the repo that owns the behaviour.
  • Reproducible reports get answers: versions, smallest failing snippet, and the server log.
"},{"location":"about/community/#related","title":"Related","text":"
  • Contributing Code \u2014 turning a bug you found into a merged fix
  • Contributing Documentation \u2014 fixing a page that misled you
  • Running Tests \u2014 the commands to run before opening a pull request
  • Tools, Libraries, and Packages \u2014 choosing between built-in and third-party
"},{"location":"about/docs/","title":"Contributing Documentation","text":""},{"location":"about/docs/#overview","title":"Overview","text":"

Most of these pages were ported from the ReactJS documentation, and the port is the whole job: a page is done when every claim in it is true of ReactPy and every snippet in it runs. That is a lower bound on effort and an upper bound on trustworthiness, which is the trade this project made deliberately. This page is how to do the work and how to know it is finished.

You will learn

  • What makes a ported page \"done\", and how the \ud83d\udea7 marker works
  • The page structure, admonitions, and link conventions this site uses
  • Why snippets live in docs/examples/ instead of inside the markdown
"},{"location":"about/docs/#where-things-live","title":"Where things live","text":"
docs/\n\u251c\u2500\u2500 mkdocs.yml          # nav: \u2014 every page must be listed here\n\u251c\u2500\u2500 src/                # the pages, in markdown\n\u2502   \u251c\u2500\u2500 learn/          #   tutorials and concepts\n\u2502   \u251c\u2500\u2500 reference/      #   API reference\n\u2502   \u2514\u2500\u2500 about/          #   this section\n\u251c\u2500\u2500 examples/           # runnable Python, one directory per page slug\n\u2514\u2500\u2500 overrides/          # template overrides\n

Page files are kebab-case.md; example directories are the same name with underscores (learn/scaling-up-with-reducer-and-context.md \u2192 examples/scaling_up_with_reducer_and_context/). A new page must be added to nav: in mkdocs.yml, but the build will not tell you when you forget: a page missing from nav: is still built and served, and mkdocs build --strict exits 0. The only trace is an INFO line listing the unlisted pages. Read it: every page currently on that list carries a Planned / Undeveloped banner in its body and can be ignored, which is exactly how a page you did mean to publish would hide among them. Adding the entry is what puts the page in the site's navigation, which is the actual reason to do it.

"},{"location":"about/docs/#what-done-means","title":"What \"done\" means","text":"

A page is authored, not migrated. Concretely:

  • Every code sample is ReactPy that runs. No JSX, no className= outside an attribute dict, no hook that does not exist.
  • Every behavioural claim has been checked against the package or its tests \u2014 by reading the source, and for anything about rendering or events, by running it.
  • React-only sections are removed or rewritten, not translated.
  • The \ud83d\udea7 badge is gone from its nav: entry.

Note

\ud83d\udea7 in the navigation means \"this is still a ReactJS page with the React names swapped\". It is a promise to the reader that the content is unreliable, so removing it is the meaningful act \u2014 the badge should never come off before the checks above are true. \ud83d\udeab means the page is blocked on something outside this project and is parked; commented-out nav: entries work the same way.

"},{"location":"about/docs/#port-do-not-rename","title":"Port, do not rename","text":"

The pages derive from react.dev. Editing means deleting what is false as much as replacing what is named differently, and the failure mode is a page that reads fluently and is wrong.

Things that do not survive a rename:

  • Hooks that do not exist. ReactPy has twelve hooks. There is no use_id, use_transition, use_deferred_value, use_layout_effect, use_insertion_effect, use_imperative_handle, or use_sync_external_store. Some of the react.dev pages for them are parked as \ud83d\udeab; use_id has no page at all, because the upstream issue was closed as not planned. Never port a page that depends on one as if it worked.
  • Naming rules. Components are snake_case and lowercase \u2014 my_button, not MyButton. React's capital-letter rule does not apply, because ReactPy distinguishes a component from a tag by the call, not the case. Attribute keys stay camelCase (className, onClick), because those pass through to the DOM untouched.
  • JSX mechanics. Children are positional arguments after an optional attribute dict. There is no JSX, and no PSX in this release.
  • StrictMode and memo. Neither exists. React's double-invoke warnings and its \"skip re-rendering unchanged children\" optimisation have no ReactPy equivalent, so port those sections as a warning that ReactPy will not catch the problem for you.
  • && rendering. {cond && <X/>} has no safe Python spelling \u2014 False and X leaks the word False into the page, since only None renders nothing. Rewrite such examples rather than transliterating them.
  • Dependency arrays. use_effect, use_memo, and use_callback infer dependencies from the closure when you omit the argument. React's \"omit the array to run after every render\" is simply wrong here.

Pitfall

A page can pass every automated check while teaching something false, because the checks are syntactic. The ReactJS FIXME that sat in thinking-in-react.md claiming checked was not delivered was wrong \u2014 a checkbox onChange handler reads it back as await event.target.checked. The only defence is running the example against a real layout and reading what the client actually answered.

"},{"location":"about/docs/#page-structure","title":"Page structure","text":"

Every authored page opens the same way: an ## Overview heading, an intro paragraph in a <p class=\"intro\"> block, then a You will learn summary.

## Overview\n\n<p class=\"intro\" markdown>\n\nOne or two paragraphs that say what the page solves and why it matters in ReactPy specifically.\n\n</p>\n\n!!! summary \"You will learn\"\n\n    -   Bullet\n    -   Bullet\n

Then the body as ## and ### sections, closing with ## Recap (bulleted, factual) and ## Related (links with a dash and a phrase explaining each).

Admonitions come from the Material theme plus assets/css/admonition.css. The set in use:

Admonition For !!! summary \"You will learn\" Objectives, once, after the intro !!! abstract \"Note\" Clarification that is not a warning !!! warning \"Pitfall\" A mistake the reader is likely to make !!! info \"Deep Dive\" Deeper mechanics a reader can skip !!! example \"Terminal\" A shell session

ReactJS's <Pitfall>, <DeepDive>, <Recap>, <Challenges>, <Solution>, and <Hint> JSX tags have no MkDocs equivalent. Convert them to admonitions or headings; never copy them through, since an unrecognised tag renders as literal text.

Links are relative .md paths from the current file \u2014 ../reference/use-state.md, ./extracting-state-logic-into-a-reducer.md#the-rule-reactpy-adds \u2014 not site-absolute paths like /learn/..., which break under versioned builds and make --strict unable to verify them.

Code fences: ```python linenums=\"0\" for fragments, bare ```python for full-file tabs, and hl_lines=\"3-4 7\" to mark the lines under discussion. Inline code that is Python gets the #!python prefix (\\#!python use_state``) so it highlights.

"},{"location":"about/docs/#examples-are-the-source-of-truth","title":"Examples are the source of truth","text":"

Snippets are not typed into the markdown. They are real files under docs/examples/<page_slug>/, pulled in by the include plugin:

=== \"app.py\"\n\n    ```python\n\n    @component\n    def my_button():\n        return html.button(\"I'm a button!\")\n\n    ```\n\n=== \":material-play: Run\"\n\n    ```python\n    # TODO\n    ```\n

The rules that make this work:

  • The file is a complete, importable module \u2014 black-formatted at 88 columns, imports at the top.
  • A # start marker on its own line is what the include begins after; # end stops it. Crop at the marker so the marker itself never renders on the page.
  • A file with no marker is included whole.
  • Multiple variants of one idea go in sibling directories (task_state_props/, task_context/, task_wiring/), one tabbed group per example.
  • The :material-play: Run tab is a standing placeholder. Keep it.

Because the file is included, a snippet cannot drift from a runnable example \u2014 but nothing in the build proves the example behaves. Mount it and check the DOM before you write \"this renders a list\". When a page compares several versions, drive all of them through the same click sequence and assert the same observable output; \"it looks the same\" is how two versions of an example quietly disagree.

Also list children of a component with key= when they come from a list \u2014 html.ul([row(item, key=item[\"id\"]) for item in items]). Omitting it renders fine and emits a debug-mode warning, which is exactly the lesson the page about keys is trying to teach.

"},{"location":"about/docs/#verifying-a-page","title":"Verifying a page","text":"

From the repository root:

hatch fmt --check docs/\npyright --pythonpath <your-env>/bin/python docs/\ncd docs && mkdocs build --strict; echo \"exit=$?\"\n

Then run the examples themselves and open the built page in a browser. mkdocs build --strict catches broken internal links and unresolvable includes \u2014 both abort the build with a nonzero exit. Two things it does not catch: a page missing from nav: (an INFO line, exit 0), and a link whose #anchor does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing.

You are the only gate. The single workflow on new-docs runs mkdocs gh-deploy --force on push, and gh-deploy does not pass --strict \u2014 a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live.

Pitfall

Do not trust a component that renders without error. Set REACTPY_DEBUG=1 when probing: with debug off, a component that raised produces {\"tagName\": \"\", \"error\": \"\"} \u2014 a blank region and a 200 response. See Running Tests for the harness traps that make a broken example look green.

"},{"location":"about/docs/#process","title":"Process","text":"

Branch from new-docs, one page per branch, and use the same Conventional Commit subjects as the history there \u2014 docs(learn): author X for ReactPy v2. A commit that removes a \ud83d\udea7 badge from nav: should be the same commit that authored the page, never a separate \"mark done\" commit that could be cherry-picked alone.

No CHANGELOG.md entry: documentation work is not user-facing in the sense the changelog means. See Changelog.

Note

docs/src/dictionary.txt exists for the spellcheck plugin, which is currently commented out in mkdocs.yml. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off.

"},{"location":"about/docs/#recap","title":"Recap","text":"
  • A page is done when it runs and every claim is verified \u2014 then remove its \ud83d\udea7 badge.
  • Port, do not rename: delete JSX, non-existent hooks, StrictMode, memo, && rendering, and React's dependency-array semantics.
  • Fixed structure: Overview, intro paragraph, You will learn, sections, Recap, Related.
  • Relative .md links; admonitions instead of ReactJS's JSX tags.
  • Snippets are includes from docs/examples/, cropped at # start, formatted at 88 columns.
  • --strict checks links and nothing about behaviour. Run the example.
"},{"location":"about/docs/#related","title":"Related","text":"
  • Running Tests \u2014 the exact commands and how to read their failures
  • Contributing Code \u2014 branch and pull request conventions
  • Changelog \u2014 what does and does not deserve an entry
  • Community \u2014 where to ask about a page before rewriting it
"},{"location":"about/licenses/","title":"Licenses","text":""},{"location":"about/licenses/#overview","title":"Overview","text":"

ReactPy's source is MIT-licensed. Much of the prose you are reading is derived from the ReactJS documentation, which carries a separate Creative Commons licence, and the two do not cover the same things. This page states which licence applies to what, and what you owe when you reuse either.

You will learn

  • Which licence covers the code, and which covers these pages
  • Why the distinction matters when you copy from one side
  • Where the third-party components in a ReactPy install come from
"},{"location":"about/licenses/#source-code","title":"Source code","text":"

The ReactPy package is released under the MIT licence, copyright Reactive Python and affiliates. The full text lives in the license file at the repository root and is reproduced below.

MIT is permissive: use, copy, modify, merge, publish, distribute, sublicense, and sell, subject to keeping the copyright notice and licence text in copies of the software. It comes with no warranty, and liability is excluded.

## The MIT License (MIT)\n\n#### Copyright (c) Reactive Python and affiliates.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n
"},{"location":"about/licenses/#documentation","title":"Documentation","text":"

These pages are a different body of work from the code, and they are separately encumbered.

Much of this documentation \u2014 including its layout and much of its content \u2014 was created with heavy influence from react.dev, which is licensed under the Creative Commons Attribution 4.0 International licence. ReactPy's behaviour often mirrors React's closely enough that the documents paraphrase, and in places copy, React's language and examples.

Note

CC BY 4.0 is also permissive, but its condition is attribution rather than keeping a licence block. If you copy substantial prose from these pages into your own documentation, credit ReactPy and, where the text is close to React's original, acknowledge the ReactJS documentation as the further source. Attributing to ReactPy alone is not sufficient for the passages that came from React.

The practical boundary: code you write against ReactPy is yours, unencumbered by either licence. These pages are text under a credit-bearing licence. A snippet in docs/examples/ is code that happens to be inside a document \u2014 treat it as MIT, since its purpose is to be pasted into a project.

"},{"location":"about/licenses/#third-party-components","title":"Third-party components","text":"

A working ReactPy install bundles more than this repository's source: the browser-side client, the vendored JavaScript dependencies, and, for client-side rendering, PyScript. Each keeps its own upstream licence, shipped with the distribution.

Check what a release actually contains rather than trusting a summary \u2014 the package metadata and the static/ directory of the release are authoritative, and the licence files travel inside the wheel.

"},{"location":"about/licenses/#licensing-your-own-contribution","title":"Licensing your own contribution","text":"

The pull request template ends with an agreement that contributions comply with the project's open source licences. It is a checkbox, but it is the whole of the provenance control this project has, so it is worth taking literally:

  • Do not paste code you do not have the right to redistribute. Code copied from a licence-incompatible project, or from an answer on a forum, is the common failure.
  • Do not copy substantial prose from react.dev into these pages without acknowledging the source on the page or in the commit.
  • If you adapted something, say what and from where in the pull request description. It costs you a sentence and saves the maintainer an investigation.
"},{"location":"about/licenses/#recap","title":"Recap","text":"
  • Source code: MIT, in LICENSE.md at the repository root.
  • These pages: derived from the ReactJS docs under CC BY 4.0 \u2014 attribution is the obligation.
  • Snippets in docs/examples/ exist to be pasted; treat them as MIT code.
  • Bundled JavaScript and PyScript keep their upstream licences, shipped with the release.
  • Contributions must be yours to license; say what you adapted and from where.
"},{"location":"about/licenses/#related","title":"Related","text":"
  • Contributing Documentation \u2014 how pages here are authored, and where React-derived text comes from
  • Contributing Code \u2014 the pull request checklist, including the licence agreement
  • Community \u2014 how to reach a maintainer about a licensing question
  • Changelog \u2014 the release history
"},{"location":"about/running-tests/","title":"Running Tests","text":""},{"location":"about/running-tests/#overview","title":"Overview","text":"

There are two test suites in this project, and they live in different places. The Python and JavaScript suites exercise ReactPy itself and run through hatch; the documentation checks exercise the pages you are reading and run through mkdocs. Knowing which one a failing command belongs to is most of debugging it.

You will learn

  • How to run the Python test suite, and why it needs a browser installed
  • How to run the documentation build checks yourself, since no CI runs them for you
  • Which command failures are yours and which are environmental

Note

The ReactPy package and its tests/ directory live on the main branch. This documentation site is authored on the new-docs branch, which contains only docs/ \u2014 running a test command there collects zero tests rather than failing, which reads like a pass and is not one. If hatch test reports no tests ran, you are on the wrong branch, not on a clean tree.

"},{"location":"about/running-tests/#prerequisites","title":"Prerequisites","text":"

The development environment is managed by Hatch, which creates and maintains its own virtual environments. Install Hatch, then clone:

git clone https://github.com/reactive-python/reactpy.git\ncd reactpy\n

You also need Bun for the JavaScript packages. Python must be 3.11 or newer \u2014 requires-python in pyproject.toml is >=3.11, and the test matrix runs 3.11 through 3.14.

Pitfall

Do not install the project's dependencies with pip install -r into your own environment. Hatch owns those environments, and a hand-built one drifts from CI in ways that look like your fault. The one exception is the documentation requirements, which are installed separately below.

"},{"location":"about/running-tests/#python-tests","title":"Python tests","text":"

The suite is pytest-based and runs under Hatch:

hatch test                                    # current Python version\nhatch test --all                              # every version in the matrix\nhatch test -k test_use_connection             # a single test by name\n

CI runs hatch test --parallel --retries 10 across three operating systems and four Python versions, and a separate coverage job runs hatch test --parallel --cover --retries 10.

The suite needs a browser. Each test run installs Playwright's Chromium, builds the JavaScript client, and builds a wheel before it invokes pytest \u2014 that is what the hatch-test environment's run script does. A first run is therefore slow, and a failure inside Chromium installation looks identical to a failure in the test itself. Read the top of the output, not the bottom.

playwright install chromium\nplaywright install-deps chromium   # optional; fails harmlessly on some systems\n

Tests are session-scoped around one browser and one backend server, defined in tests/conftest.py, and that file also turns on REACTPY_DEBUG and REACTPY_ASYNC_RENDERING for the whole session. Two consequences:

  • A test that passes alone and fails in the suite may be reading state a previous test left behind. Reproduce with -k before you chase it.
  • Because debug mode is on in tests and off by default in production, some errors surface only in the suite. Debug-mode checks such as REACTPY_CHECK_VDOM_SPEC are exactly this.

Configuration lives under [tool.pytest.ini_options] in pyproject.toml: --strict-config, --strict-markers, xfail_strict, asyncio_mode = \"auto\", testpaths = \"tests\", and a 120-second per-test timeout. xfail_strict means an expected failure that unexpectedly passes is itself a failure, so an xfail you fixed must be deleted rather than left alone.

"},{"location":"about/running-tests/#what-the-helpers-are-for","title":"What the helpers are for","text":"

reactpy.testing exists because a UI is only correct once it renders in a browser. The acceptance bar is that the real artefact behaves, not that a function returned the expected shape.

  • DisplayFixture \u2014 serves a component and gives you a Playwright page to drive it: await display.show(my_component), then display.page.
  • BackendFixture \u2014 the server side, usually paired with DisplayFixture as in conftest.py.
  • poll(...) \u2014 retries a condition until it holds or the timeout expires. Use it instead of asyncio.sleep, which passes vacuously on a slow machine and flakes on a fast one.
  • HookCatcher and StaticEventHandler \u2014 capture a component's life-cycle hook and an event handler's target, for delivering events without a click.
  • capture_reactpy_logs, assert_reactpy_did_log, assert_reactpy_did_not_log \u2014 assert on server-side log output, which is where ReactPy puts swallowed handler and effect errors.

conftest.py also installs an autouse fixture that fails a test if any ReactPy log record carries an exception. That is deliberate: an error logged server-side while the browser stays quiet is the most common way a ReactPy bug hides. If a new test trips it, the exception is real even when the assertion you wrote passes.

"},{"location":"about/running-tests/#javascript-tests","title":"JavaScript tests","text":"

The client packages are tested with Bun:

hatch run javascript:test     # run the JavaScript tests\nhatch run javascript:check    # lint and format check\nhatch run javascript:fix      # lint and write fixes to disk\nhatch run javascript:build    # rebuild the client bundles\n

Changes under src/js/ need a rebuild before Python tests can see them. The test script does this itself, but a manual run against a stale bundle will not reflect your edit.

"},{"location":"about/running-tests/#linting-and-type-checking","title":"Linting and type checking","text":"
hatch fmt                     # format and lint, writing fixes\nhatch fmt --check             # the CI form: report, do not write\nhatch run python:type_check   # pyright over src/reactpy\n

CI runs hatch fmt src/reactpy --check followed by hatch run python:type_check.

Pitfall

Run hatch fmt --check before you commit, not hatch fmt. Bare hatch fmt rewrites files, so a run that \"passes\" has already edited your working tree \u2014 including files you did not intend to touch. Scope it (hatch fmt docs/) if you only want part of the tree.

Formatting is Ruff at a line length of 88. The rules that bite most in this repository are set in pyproject.toml; docs/**/*.py already ignores E402, S102, T201, TID252, RUF059, and INP001, because example files are teaching snippets rather than importable modules.

"},{"location":"about/running-tests/#documentation-checks","title":"Documentation checks","text":"

Run these from the repository root before you commit.

Pitfall

Nothing checks the documentation for you. The only workflow on new-docs installs the requirements and runs mkdocs gh-deploy --force on push \u2014 which does not pass --strict, so a broken internal link exits 0 and deploys anyway. The commands below are the gate only because you run them; there is no CI to catch a miss, and the live site is where an uncaught one appears.

hatch fmt --check docs/             # formatting and lint\ncd docs && mkdocs build --strict    # build, fail on warnings\n

Plus the type check over the example files, which are real Python:

pyright --pythonpath <your-env>/bin/python docs/\n

mkdocs build --strict is the important one: it turns broken internal links and unresolvable includes into build failures, both of which abort with a nonzero exit. It does not fail on two things you would expect it to: a page missing from nav: (only an INFO line, and the build still exits 0 while serving the orphaned page), and a link whose #anchor does not exist inside an otherwise valid target file (also exit 0). Read that nav: list yourself and check anchors by eye. Nor can it tell you whether a snippet works: the examples in docs/examples/ are the source of truth for every snippet on the site, and a snippet that does not run is a docs bug the build will not catch on its own. That part is on you.

Pitfall

Do not judge a build by its log output alone. A grep-style check on the output hides a nonzero exit:

cd docs && mkdocs build --strict; echo \"exit=$?\"\n

In a bash pipeline, use echo \"${PIPESTATUS[0]}\" instead \u2014 a trailing | grep that finds a match makes the pipeline's exit code meaningless.

"},{"location":"about/running-tests/#running-the-site-locally","title":"Running the site locally","text":"
pip install -r docs/requirements.txt\ncd docs && mkdocs serve\n

Then follow the printed URL. Editing any file under docs/src/ or docs/examples/ reloads the page.

The requirements file pins mkdocs-material==9.7.7. Earlier revisions pinned 9.4.0, which pulled in regex~=2022.4 and resolved to a release shipping wheels only through CPython 3.11 \u2014 on newer Pythons that meant a source build and a hard failure without a C compiler. The current pin dropped the regex dependency altogether, so the install works on 3.12 and up. If you are on an older branch and the docs install dies inside regex, that is the reason: use Python 3.11 or raise the pin.

"},{"location":"about/running-tests/#checking-your-work","title":"Checking your work","text":"

Before calling anything done, run the gates in this order and read every failure:

  1. hatch fmt --check docs/ \u2014 formatting and lint
  2. pyright docs/ \u2014 types in the examples
  3. cd docs && mkdocs build --strict \u2014 links, includes, nav
  4. The relevant tests, for code changes
  5. The real user scenario, by hand \u2014 open the page, click the control

Steps 1\u20134 can all pass while the thing you built does not work. A status is not an acceptance test: run the artefact. For a docs example that means mounting it in a browser and confirming the DOM changed, because a ReactPy component can render, error server-side, and show a blank region while the HTTP response still reads 200.

Deep Dive

Two harness traps have made broken examples look green more than once. First, the decorated function is a constructor: counter itself has no .render() and Layout(counter) raises TypeError: Expected a ReactPy component, so you must pass an instance. The instance does have .render(), but calling it outside a Layout raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? for any component that uses a hook \u2014 which silently skips the behaviour you meant to test. Render through async with Layout(counter()) as layout: await layout.render(). Second, ReactPy formats exception text into the model only when REACTPY_DEBUG is on; with it off, a component that raised renders as {\"tagName\": \"\", \"error\": \"\"} \u2014 an empty region and no message. Set the variable when you are probing failures, and unset it when you are checking what a user would actually see.

"},{"location":"about/running-tests/#recap","title":"Recap","text":"
  • Two suites: hatch test for ReactPy (on main), mkdocs build --strict for the docs.
  • hatch test reporting no tests ran means the suite is not there, not that it passed.
  • Python tests need Playwright's Chromium and build a wheel before pytest starts.
  • conftest.py forces debug mode and fails any test that logs an exception.
  • Run hatch fmt --check, never bare hatch fmt, before committing.
  • Check exit codes, not log text; then verify the real behaviour by hand.
"},{"location":"about/running-tests/#related","title":"Related","text":"
  • Contributing Code \u2014 branches, commits, and the pull request checklist
  • Contributing Documentation \u2014 page conventions and the example-file workflow
  • Changelog \u2014 when a change needs an entry
"},{"location":"learn/add-react-to-an-existing-project/","title":"Add React to an Existing Project","text":""},{"location":"learn/add-react-to-an-existing-project/#overview","title":"Overview","text":"

If you want to add some interactivity to your existing project, you don't have to rewrite it in React. Add React to your existing stack, and render interactive React components anywhere.

"},{"location":"learn/add-react-to-an-existing-project/#using-react-for-an-entire-subroute-of-your-existing-website","title":"Using React for an entire subroute of your existing website","text":"

Let's say you have an existing web app at example.com built with another server technology (like Rails), and you want to implement all routes starting with example.com/some-app/ fully with React.

"},{"location":"learn/add-react-to-an-existing-project/#using-an-asgi-subroute","title":"Using an ASGI subroute","text":"

Here's how we recommend to set it up:

  1. Build the React part of your app using one of the ReactPy executors.
  2. Specify /some-app/ as the base path in your executor's kwargs (path_prefix=\"/some-app/\"). The trailing slash is load-bearing: executors/asgi/middleware.py validates the value and a missing slash raises ValueError from the constructor \u2014 Invalid path_prefix of \"/some-app\". URL path must end with a forward slash. \u2014 so the app never starts.
  3. Configure your server or a proxy so that all requests under /some-app/ are handled by your React app.

This ensures the React part of your app can benefit from the best practices baked into those frameworks.

"},{"location":"learn/add-react-to-an-existing-project/#using-static-site-generation-ssg","title":"Using static site generation (SSG)","text":"

Support for SSG is coming in a future version.

"},{"location":"learn/add-react-to-an-existing-project/#using-react-for-a-part-of-your-existing-page","title":"Using React for a part of your existing page","text":"

Let's say you have an existing page built with another Python web technology (ASGI or WSGI), and you want to render interactive React components somewhere on that page.

The exact approach depends on your existing page setup, so let's walk through some details.

"},{"location":"learn/add-react-to-an-existing-project/#using-asgi-middleware","title":"Using ASGI Middleware","text":"

ReactPy supports running as middleware for any existing ASGI application. ReactPy components are embedded into your existing HTML templates using Jinja2. You can use any ASGI framework, however for demonstration purposes we have selected Starlette for the example below.

First, install ReactPy, Starlette, and your preferred ASGI webserver.

Terminal

pip install reactpy[asgi,jinja] starlette uvicorn[standard]\n

Next, configure your ASGI framework to use ReactPy's Jinja2 template tag. The method for doing this will vary depending on the ASGI framework you are using. Below is an example that follow's Starlette's documentation:

Note

The ReactPyJinja extension enables a handful of template tags that allow you to render ReactPy components in your templates. The component tag is used to render a ReactPy SSR component, while the pyscript_setup and pyscript_component tags can be used together to render CSR components.

from jinja2 import Environment, FileSystemLoader\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\nfrom starlette.templating import Jinja2Templates\n\nfrom reactpy.templatetags import ReactPyJinja\n\njinja_templates = Jinja2Templates(\n    env=Environment(\n        loader=FileSystemLoader(\"path/to/my_templates\"),\n        extensions=[ReactPyJinja],\n        autoescape=True,\n    )\n)\n\n\nasync def example_webpage(request):\n    return jinja_templates.TemplateResponse(request, \"my_template.html\")\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", example_webpage)])\n

Now you will need to wrap your existing ASGI application with ReactPy's middleware, define the dotted path to your root components, and render your components in your existing HTML templates.

main.pymy_components.pymy_template.html
from jinja2 import Environment, FileSystemLoader\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\nfrom starlette.templating import Jinja2Templates\n\nfrom reactpy.executors.asgi import ReactPyMiddleware\nfrom reactpy.templatetags import ReactPyJinja\n\njinja_templates = Jinja2Templates(\n    env=Environment(\n        loader=FileSystemLoader(\"path/to/my_templates\"),\n        extensions=[ReactPyJinja],\n        autoescape=True,\n    )\n)\n\n\nasync def example_webpage(request):\n    return jinja_templates.TemplateResponse(request, \"my_template.html\")\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", example_webpage)])\nreactpy_app = ReactPyMiddleware(starlette_app, [\"my_components.hello_world\"])\n
from reactpy import component, html\n\n\n@component\ndef hello_world():\n    return html.div(\"Hello World\")\n
<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <title>ReactPy in Django</title>\n</head>\n\n<body>\n  {% component \"my_components.hello_world\" %}\n</body>\n\n</html>\n

Finally, use your webserver of choice to start ReactPy:

Terminal

uvicorn main:reactpy_app\n
"},{"location":"learn/add-react-to-an-existing-project/#using-wsgi-middleware","title":"Using WSGI Middleware","text":"

Support for WSGI executors is coming in a future version.

"},{"location":"learn/add-react-to-an-existing-project/#external-executors","title":"External Executors","text":"

Note

External executors exist outside ReactPy's core library and have significantly different installation and configuration instructions.

Make sure to follow the documentation for setting up your chosen external executor.

"},{"location":"learn/add-react-to-an-existing-project/#django","title":"Django","text":"

Django is a full-featured web framework that provides a batteries-included approach to web development.

Due to it's batteries-included approach, ReactPy has unique features only available to this executor.

To learn how to configure Django for ReactPy, see the ReactPy-Django documentation.

"},{"location":"learn/choosing-the-state-structure/","title":"Choosing the State Structure","text":""},{"location":"learn/choosing-the-state-structure/#overview","title":"Overview","text":"

Structuring state well is the difference between a component that is pleasant to modify and one that is a constant source of bugs. ReactPy makes these guidelines matter more than they do in React, because it detects state changes with == rather than by identity, and because it re-renders every child of a component that re-renders. Each principle below comes with the failure it prevents, measured.

You will learn

  • When to group state into one object, and what that trade actually costs
  • Why contradictions, redundancy, and duplication each buy you a bug
  • How ReactPy's == comparison changes the \"never mutate state\" rule
  • What a flat state shape does to the cost of an update
"},{"location":"learn/choosing-the-state-structure/#principles-for-structuring-state","title":"Principles for structuring state","text":"

When you write a component that holds state, you choose how many state variables to use and what shape their data has. A poor shape still runs correctly; it just makes mistakes easier to make. Five principles point the other way:

  1. Group related state. If you always update two or more state variables together, merge them into one.
  2. Avoid contradictions in state. If two pieces of state can disagree, you have left room for a bug.
  3. Avoid redundant state. If you can calculate a value from props or existing state during render, do not put it in state.
  4. Avoid duplication in state. Data stored in two places must be kept in sync in two places.
  5. Avoid deeply nested state. To change one leaf you must rebuild every ancestor.

The goal is to make state easy to update without introducing mistakes \u2014 the same reason a database engineer normalizes a schema. To paraphrase Einstein: make your state as simple as it can be, but no simpler.

One fact about ReactPy changes how all five pay off, so it comes first.

"},{"location":"learn/choosing-the-state-structure/#reactpy-compares-state-with","title":"ReactPy compares state with ==","text":"

use_state compares the value you set against the current one with strictly_equal, which falls through to ==. If the two compare equal, nothing re-renders. Verified against a real Layout, one press each:

You set over Renders {\"items\": [1, 2]} \u2014 a brand-new dict {\"items\": [1, 2]} 0 [1, 2, 3] \u2014 a brand-new list [1, 2, 3] 0 [] [] 0 {\"a\": {\"b\": 2}} {\"a\": {\"b\": 1}} 1 [1, 2] (1, 2) 1

The first three rows are the ones to internalise. In React, setting a fresh object always re-renders, because React compares by identity. In ReactPy, a new object that reads the same as the old one is discarded.

That makes the \"never mutate state\" rule stricter, not looser:

rows[\"items\"].append(\"x\")\nset_rows(rows)  # same object, and == equal -> no render at all\n

Verified: three presses of that handler leave the rendered count at n=0 with the component body having run once. The mutation did happen \u2014 the list really grew \u2014 but nothing re-rendered to show it, so the screen and the data now disagree. Build a new object instead ({**rows, \"items\": [*rows[\"items\"], \"x\"]}) and the update becomes visible.

It also means a tuple and a list are different states: [1, 2] over (1, 2) renders, because strictly_equal checks type(x) is type(y) before anything else. Pick one container type per field. See Lifecycle of Reactive Effects for the full comparison rules.

"},{"location":"learn/choosing-the-state-structure/#group-related-state","title":"Group related state","text":"

Should you use several state variables or one object?

Technically either works. But if two state variables always change together, put them in one state variable, so there is no second setter call to forget. Here the two coordinates are separate, and one handler forgets the second call:

app.py Run
@component\ndef marker():\n    x, set_x = use_state(20)\n    y, set_y = use_state(20)\n\n    async def move_diagonally(event):\n        # The classic mistake: this handler remembers x and forgets y.\n        set_x(x + STEP)\n\n    async def move_correctly(event):\n        set_x(x + STEP)\n        set_y(y + STEP)\n\n    async def reset(event):\n        set_x(20)\n        set_y(20)\n\n    return html.div(\n        html.button({\"onClick\": move_diagonally}, \"Move (forgot y)\"),\n        html.button({\"onClick\": move_correctly}, \"Move (both)\"),\n        html.button({\"onClick\": reset}, \"Reset\"),\n        html.div(\n            STYLE,\n            html.div(\n                {\n                    \"style\": {\n                        \"position\": \"absolute\",\n                        \"left\": f\"{x}px\",\n                        \"top\": f\"{y}px\",\n                        \"width\": \"18px\",\n                        \"height\": \"18px\",\n                        \"borderRadius\": \"50%\",\n                        \"background\": \"#d40000\",\n                    }\n                }\n            ),\n        ),\n        html.p({\"id\": \"coords\"}, f\"x={x}  y={y}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Two state variables\"), marker())\n
# TODO\n

Press Move (forgot y): the dot travels horizontally and the readout confirms the split \u2014 x=44 y=20. Every later move compounds it, because each handler builds on the state it already has.

With one object, the mistake has nowhere to live:

app.py Run
@component\ndef marker():\n    position, set_position = use_state({\"x\": 20, \"y\": 20})\n\n    async def move_diagonally(event):\n        # One setter, one object: there is no second call left to forget.\n        set_position({\"x\": position[\"x\"] + STEP, \"y\": position[\"y\"] + STEP})\n\n    async def reset(event):\n        set_position({\"x\": 20, \"y\": 20})\n\n    return html.div(\n        html.button({\"onClick\": move_diagonally}, \"Move diagonally\"),\n        html.button({\"onClick\": reset}, \"Reset\"),\n        html.div(\n            STYLE,\n            html.div(\n                {\n                    \"style\": {\n                        \"position\": \"absolute\",\n                        \"left\": f\"{position['x']}px\",\n                        \"top\": f\"{position['y']}px\",\n                        \"width\": \"18px\",\n                        \"height\": \"18px\",\n                        \"borderRadius\": \"50%\",\n                        \"background\": \"#d40000\",\n                    }\n                }\n            ),\n        ),\n        html.p({\"id\": \"coords\"}, f\"x={position['x']}  y={position['y']}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"One state variable\"), marker())\n
# TODO\n

Two presses of Move diagonally give x=68 y=68. There is no second setter to omit: one set_position call carries both axes, and the dict you build has to say something about each one.

The trade is worth naming, because it is not free. A single object means every update rebuilds the whole object, and position[\"x\"] is noisier than x. ReactPy does not sweeten it with fewer renders: verified, two presses of \"move both\" cost two renders in the separate version and two in the grouped version, and moving only x costs one render in both. Group related state to remove a class of bug, not to save work.

"},{"location":"learn/choosing-the-state-structure/#avoid-contradictions-in-state","title":"Avoid contradictions in state","text":"

A feedback form tracks its send with two booleans:

app.py Run
@component\ndef feedback():\n    text, set_text = use_state(\"\")\n    is_sending, set_is_sending = use_state(False)\n    is_sent, set_is_sent = use_state(False)\n\n    async def submit(event):\n        set_is_sending(True)\n        await asyncio.sleep(DELAY)\n        # Forget either line below and the two flags disagree forever.\n        set_is_sending(False)\n        set_is_sent(True)\n\n    async def submit_with_bug(event):\n        set_is_sending(True)\n        await asyncio.sleep(DELAY)\n        set_is_sent(True)  # set_is_sending(False) was never called\n\n    if is_sent:\n        return html.div(\n            html.h3(\"Thanks for your feedback!\"),\n            html.p({\"id\": \"flags\"}, f\"is_sending={is_sending}  is_sent={is_sent}\"),\n        )\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.p(\"How was your stay at The Prancing Pony?\"),\n        html.textarea(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"disabled\": is_sending,\n            }\n        ),\n        html.br(),\n        html.button({\"onClick\": submit, \"disabled\": is_sending}, \"Send\"),\n        html.button(\n            {\"onClick\": submit_with_bug, \"disabled\": is_sending}, \"Send (buggy)\"\n        ),\n        (html.p(\"Sending...\") if is_sending else None),\n        html.p({\"id\": \"flags\"}, f\"is_sending={is_sending}  is_sent={is_sent}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Two flags\"), feedback())\n
# TODO\n

The code works \u2014 and it permits a state that should be impossible. Press Send (buggy), which sets is_sent without clearing is_sending, and the readout reports:

is_sending=True  is_sent=True\n

That is not hypothetical. Nothing in the data structure prevents those two flags being true together, so the only thing protecting you is remembering every setter in every handler. The readout exists so you can see the disagreement; in a real component you would instead see a spinner that never stops.

Since the flags can never both be true, replace them with one variable holding one of three values:

app.py Run
@component\ndef feedback(speed=\"slow\"):\n    text, set_text = use_state(\"\")\n    status, set_status = use_state(\"typing\")\n\n    async def submit(event):\n        set_status(\"sending\")\n        await asyncio.sleep(DELAYS[speed])\n        set_status(\"sent\")\n\n    # Plain names, not state: they cannot drift apart because they are derived.\n    is_sending = status == \"sending\"\n    is_sent = status == \"sent\"\n\n    if is_sent:\n        return html.div(html.h3(\"Thanks for your feedback!\"))\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.p(\"How was your stay at The Prancing Pony?\"),\n        html.textarea(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"disabled\": is_sending,\n            }\n        ),\n        html.br(),\n        html.button({\"onClick\": submit, \"disabled\": is_sending}, \"Send\"),\n        (html.p(\"Sending...\") if is_sending else None),\n        html.p({\"id\": \"flags\"}, f\"status={status}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"One status\"), feedback())\n
# TODO\n

status passes through \"sending\" and ends at \"sent\", which swaps the form for a thank-you. There is no way to be in two states at once, because there is only one variable.

You can still write the readable names:

is_sending = status == \"sending\"\nis_sent = status == \"sent\"\n

Those are plain local variables computed during render, not state. They cannot drift, which is exactly why they should not be state.

"},{"location":"learn/choosing-the-state-structure/#avoid-redundant-state","title":"Avoid redundant state","text":"

A check-in form shows a full name built from two fields:

app.py Run
@component\ndef check_in():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n    full_name, set_full_name = use_state(\"Ada Lovelace\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n        set_full_name(await event.target.value + \" \" + last_name)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n        set_full_name(first_name + \" \" + await event.target.value)\n\n    async def rename_only_first(event):\n        # The bug the extra state variable invites: one writer, one update.\n        set_first_name(\"Grace\")\n\n    async def rename_only_last(event):\n        # This one does not touch full_name either.\n        set_last_name(\"Hopper\")\n\n    return html.div(\n        html.h2(\"Let's check you in\"),\n        html.label(\n            {},\n            \"First name: \",\n            html.input({\"value\": first_name, \"onChange\": handle_first_name_change}),\n        ),\n        html.br(),\n        html.label(\n            {},\n            \"Last name: \",\n            html.input({\"value\": last_name, \"onChange\": handle_last_name_change}),\n        ),\n        html.br(),\n        html.button({\"onClick\": rename_only_first}, \"Rename first\"),\n        html.button({\"onClick\": rename_only_last}, \"Rename last\"),\n        html.p({\"id\": \"ticket\"}, \"Your ticket will be issued to: \", html.b(full_name)),\n    )\n\n\n@component\ndef app():\n    return check_in()\n
# TODO\n

It has three state variables, and one is redundant \u2014 full_name is derivable from the other two at any moment. That is not merely wasteful; it is a second place every writer must remember. Press Rename first: first_name becomes \"Grace\", and the ticket still reads Ada Lovelace.

Remove the state variable and calculate it:

app.py Run
@component\ndef check_in():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n\n    # Not state. Every render recomputes it from whatever the two fields hold.\n    full_name = f\"{first_name} {last_name}\"\n\n    async def rename_only_first(event):\n        set_first_name(\"Grace\")\n\n    async def rename_only_last(event):\n        set_last_name(\"Hopper\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.h2(\"Let's check you in\"),\n        html.label(\n            \"First name: \",\n            html.input(\n                {\n                    \"value\": first_name,\n                    \"onChange\": handle_first_name_change,\n                }\n            ),\n        ),\n        html.br(),\n        html.label(\n            \"Last name: \",\n            html.input(\n                {\n                    \"value\": last_name,\n                    \"onChange\": handle_last_name_change,\n                }\n            ),\n        ),\n        html.br(),\n        html.button({\"onClick\": rename_only_first}, \"Rename first\"),\n        html.button({\"onClick\": rename_only_last}, \"Rename last\"),\n        html.p({\"id\": \"ticket\"}, \"Your ticket will be issued to: \", html.b(full_name)),\n    )\n\n\n@component\ndef app():\n    return check_in()\n
# TODO\n

Now Rename first followed by Rename last gives Grace Hopper. The same two buttons cannot go wrong, because there is only one copy of the name.

The difference is not the render count \u2014 both versions render once per press, verified. It is that the derived version has no code path that produces a stale ticket.

Note

Deriving is the default, but not free when a calculation is genuinely expensive. The order is: derive first, then measure. If a render is too slow, cache the value with use_memo rather than promoting it to state \u2014 a cache adds no render, a state variable does. See You Might Not Need an Effect.

"},{"location":"learn/choosing-the-state-structure/#avoid-duplication-in-state","title":"Avoid duplication in state","text":"

A snack menu lets you choose one item and edit the titles. This version stores the selected item as an object:

app.py Run
@component\ndef menu():\n    items, set_items = use_state(INITIAL_ITEMS)\n    selected_item, set_selected_item = use_state(INITIAL_ITEMS[0])\n\n    async def handle_item_change(item_id, event):\n        new_title = await event.target.value\n        set_items(\n            [\n                {**item, \"title\": new_title} if item[\"id\"] == item_id else item\n                for item in items\n            ]\n        )\n\n    def make_change(item):\n        async def change(event):\n            await handle_item_change(item[\"id\"], event)\n\n        return change\n\n    def make_choose(item):\n        async def choose(event):\n            set_selected_item(item)\n\n        return choose\n\n    return html.div(\n        html.h2(\"What's your travel snack?\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": item[\"id\"]},\n                    html.input(\n                        {\n                            \"value\": item[\"title\"],\n                            \"onChange\": make_change(item),\n                        }\n                    ),\n                    html.button(\n                        {\"onClick\": make_choose(item)},\n                        \"Choose\",\n                    ),\n                )\n                for item in items\n            ]\n        ),\n        html.p({\"id\": \"picked\"}, f\"You picked {selected_item['title']}.\"),\n    )\n\n\n@component\ndef app():\n    return menu()\n
# TODO\n

Press Choose on pretzels, then edit that input to chips. The input updates; the line below it does not:

You picked pretzels.\n

selected_item is a second copy of data that also lives inside items. set_items updated one copy and nothing updated the other \u2014 the same bug as the stale full name, one level deeper.

Hold the id instead and look the item up during render:

app.py Run
@component\ndef menu():\n    items, set_items = use_state(INITIAL_ITEMS)\n    selected_id, set_selected_id = use_state(0)\n\n    # Derived: there is exactly one copy of the title, and it lives in items.\n    selected_item = next(item for item in items if item[\"id\"] == selected_id)\n\n    async def handle_item_change(item_id, event):\n        new_title = await event.target.value\n        set_items(\n            [\n                {**item, \"title\": new_title} if item[\"id\"] == item_id else item\n                for item in items\n            ]\n        )\n\n    def make_change(item_id):\n        async def change(event):\n            await handle_item_change(item_id, event)\n\n        return change\n\n    def make_choose(item_id):\n        async def choose(event):\n            set_selected_id(item_id)\n\n        return choose\n\n    return html.div(\n        html.h2(\"What's your travel snack?\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": item[\"id\"]},\n                    html.input(\n                        {\n                            \"value\": item[\"title\"],\n                            \"onChange\": make_change(item[\"id\"]),\n                        }\n                    ),\n                    html.button(\n                        {\"onClick\": make_choose(item[\"id\"])},\n                        \"Choose\",\n                    ),\n                )\n                for item in items\n            ]\n        ),\n        html.p({\"id\": \"picked\"}, f\"You picked {selected_item['title']}.\"),\n    )\n\n\n@component\ndef app():\n    return menu()\n
# TODO\n

Editing the selected row now updates the line below it, because selected_item is a next(...) over items, re-evaluated on every render. The state went from:

  • items = [{\"id\": 0, \"title\": \"pretzels\"}, ...]
  • selected_item = {\"id\": 0, \"title\": \"pretzels\"}

to:

  • items = [{\"id\": 0, \"title\": \"pretzels\"}, ...]
  • selected_id = 0

Only the id is essential; the rest was a copy. The same reasoning turns a selected object into an id, a filter string into its own fields, and a sorted list into an unsorted list plus a sort.

Pitfall

\"Store the id, derive the object\" meets ReactPy's == comparison in one place worth knowing about. A derived object handed to use_effect as a dependency compares equal every render, so the dependency holds and the effect never restarts \u2014 usually what you want. But that also means a dependency cannot detect a change that == cannot see. Verified with a plain class instance held as a dependency and mutated in place: across three presses the component re-rendered each time and the screen showed n=3, while the effect ran once and kept reporting n=0. Use dict and list, or an immutable @dataclass(frozen=True), for anything that appears in a dependency array. See Removing Effect Dependencies.

"},{"location":"learn/choosing-the-state-structure/#avoid-deeply-nested-state","title":"Avoid deeply nested state","text":"

A travel plan is planets, then continents, then countries. The tempting shape mirrors the picture. Two files make the example \u2014 app.py renders it, data.py holds the tree:

app.pydata.py Run
def mark_visited(node, target_id):\n    \"\"\"Copy this node, and recurse \u2014 the whole tree is rebuilt top-down.\"\"\"\n    if node[\"id\"] == target_id:\n        return {**node, \"visited\": True}\n    return {\n        **node,\n        \"childPlaces\": [\n            mark_visited(child, target_id) for child in node[\"childPlaces\"]\n        ],\n    }\n\n\n@component\ndef place_tree(place, on_visit):\n    children = place[\"childPlaces\"]\n    return html.li(\n        {\"key\": place[\"id\"]},\n        place[\"title\"],\n        html.button(\n            {\"onClick\": on_visit}, \"Visited\" if place.get(\"visited\") else \"Visit\"\n        ),\n        (\n            html.ol([place_tree(child, on_visit) for child in children])\n            if children\n            else None\n        ),\n    )\n\n\n@component\ndef travel_plan():\n    plan, set_plan = use_state(INITIAL_PLAN)\n\n    def make_visit(place_id):\n        # A factory, because a handler has to be a coroutine and a coroutine\n        # cannot be a lambda \u2014 this is how the id gets bound instead.\n        async def visit(event):\n            set_plan(mark_visited(plan, place_id))\n\n        return visit\n\n    return html.div(\n        html.h2(\"Places to visit\"),\n        html.ol(\n            [\n                place_tree(place, make_visit(place[\"id\"]))\n                for place in plan[\"childPlaces\"]\n            ]\n        ),\n    )\n\n\n@component\ndef app():\n    return travel_plan()\n
\"\"\"The same tree as nested objects, the shape this section argues against.\"\"\"\n\nINITIAL_PLAN = {\n    \"id\": 0,\n    \"title\": \"(Root)\",\n    \"childPlaces\": [\n        {\n            \"id\": 1,\n            \"title\": \"Earth\",\n            \"childPlaces\": [\n                {\n                    \"id\": 2,\n                    \"title\": \"Africa\",\n                    \"childPlaces\": [\n                        {\"id\": 3, \"title\": \"Botswana\", \"childPlaces\": []},\n                        {\"id\": 4, \"title\": \"Egypt\", \"childPlaces\": []},\n                        {\"id\": 5, \"title\": \"Kenya\", \"childPlaces\": []},\n                    ],\n                },\n                {\n                    \"id\": 10,\n                    \"title\": \"Americas\",\n                    \"childPlaces\": [\n                        {\"id\": 11, \"title\": \"Argentina\", \"childPlaces\": []},\n                        {\"id\": 12, \"title\": \"Brazil\", \"childPlaces\": []},\n                        {\"id\": 13, \"title\": \"Barbados\", \"childPlaces\": []},\n                    ],\n                },\n                {\n                    \"id\": 21,\n                    \"title\": \"Asia\",\n                    \"childPlaces\": [\n                        {\"id\": 22, \"title\": \"Japan\", \"childPlaces\": []},\n                        {\"id\": 23, \"title\": \"Nepal\", \"childPlaces\": []},\n                    ],\n                },\n            ],\n        }\n    ],\n}\n
# TODO\n

Marking one country visited calls mark_visited, which copies the root, copies Earth, copies the continent, and copies every sibling along the way \u2014 a new object for every node between the root and the leaf. Correct, and also easy to get wrong: miss one {**node, ...} and you have mutated shared state, which in ReactPy means the update may compare equal and never render.

Flatten the tree into one dict keyed by id, with child lists of ids:

app.pydata.py Run
def bind_visit(on_visit, place_id):\n    # Handlers are called with the event alone, so the id is bound by a closure\n    # rather than a lambda default \u2014 a handler has to be a coroutine, and a\n    # coroutine cannot be a lambda. The default stays overridable so a parent's\n    # bound handler can be re-bound for a deeper child.\n    async def visit(event, target_id=place_id):\n        await on_visit(event, target_id)\n\n    return visit\n\n\n@component\ndef place_tree(place_id, on_visit, visited):\n    place = PLACES[place_id]\n    children = place[\"childIds\"]\n    return html.li(\n        {\"key\": place_id},\n        place[\"title\"],\n        html.button(\n            {\"onClick\": on_visit}, \"Visited\" if place_id in visited else \"Visit\"\n        ),\n        (\n            html.ol(\n                [\n                    place_tree(child_id, bind_visit(on_visit, child_id), visited)\n                    for child_id in children\n                ]\n            )\n            if children\n            else None\n        ),\n    )\n\n\n@component\ndef travel_plan():\n    # One dict keyed by id, plus one set of visited ids. Marking a place visited\n    # touches exactly one entry \u2014 there is no enclosing object to rebuild.\n    visited, set_visited = use_state(set())\n\n    async def toggle(event, place_id=0):\n        if place_id not in visited:\n            set_visited({*visited, place_id})\n\n    return html.div(\n        html.h2(\"Places to visit\"),\n        html.ol(\n            [\n                place_tree(place_id, bind_visit(toggle, place_id), visited)\n                for place_id in PLACES[0][\"childIds\"]\n            ]\n        ),\n    )\n\n\n@component\ndef app():\n    return travel_plan()\n
\"\"\"The same tree flattened into one dict keyed by id.\"\"\"\n\nPLACES = {\n    0: {\"id\": 0, \"title\": \"(Root)\", \"childIds\": [1]},\n    1: {\"id\": 1, \"title\": \"Earth\", \"childIds\": [2, 10, 21]},\n    2: {\"id\": 2, \"title\": \"Africa\", \"childIds\": [3, 4, 5]},\n    3: {\"id\": 3, \"title\": \"Botswana\", \"childIds\": []},\n    4: {\"id\": 4, \"title\": \"Egypt\", \"childIds\": []},\n    5: {\"id\": 5, \"title\": \"Kenya\", \"childIds\": []},\n    10: {\"id\": 10, \"title\": \"Americas\", \"childIds\": [11, 12, 13]},\n    11: {\"id\": 11, \"title\": \"Argentina\", \"childIds\": []},\n    12: {\"id\": 12, \"title\": \"Brazil\", \"childIds\": []},\n    13: {\"id\": 13, \"title\": \"Barbados\", \"childIds\": []},\n    21: {\"id\": 21, \"title\": \"Asia\", \"childIds\": [22, 23]},\n    22: {\"id\": 22, \"title\": \"Japan\", \"childIds\": []},\n    23: {\"id\": 23, \"title\": \"Nepal\", \"childIds\": []},\n}\n
# TODO\n

The same update now adds one entry to one set, and finding a place is a dict lookup rather than a recursion. The rendering component still walks the tree by id, so the display is unchanged \u2014 only the update path got shorter.

A useful test for whether nesting has gone too far: count the objects an update has to rebuild to change one field. If the answer is \"everything above it\", flatten. If the hierarchy is genuinely part of the data and updates are rare, nesting is fine \u2014 the cost is in writes, not reads.

Deep Dive

Flattening is what databases do, and ReactPy's == comparison gives you a concrete second reason \u2014 though it rewards less than you would expect. A state update is detected by handing the new value to == (core/hooks.py, strictly_equal, falls through to == for a dict). dict.__eq__ first compares lengths, which is free, then walks entries in insertion order and stops at the first difference. So the cost depends on where the changed entry sits, not on how big the object is.

Measured on dicts of 100,000 keys, comparing a copy against its original after changing one entry: about 0.00 ms when the changed key sorts first, about 1.3 ms when it sorts last, and about 3 ms when the two dicts hold identical content. Nested dicts recurse, so a rebuilt branch is compared down to its leaves while untouched sibling branches are still compared entry by entry too \u2014 sharing the sub-dict objects does not make the comparison skip them.

The honest conclusion: comparison cost is real but small and easy to over-engineer around, so flatten for the update-code reasons above rather than for this one. Where the shapes matter, measure instead of guessing \u2014 ReactPy ships no render profiler, so count renders with a use_ref counter in the component body, as the examples on this page do.

"},{"location":"learn/choosing-the-state-structure/#recap","title":"Recap","text":"
  • ReactPy's use_state compares with ==: setting a new object that reads equal to the old one renders nothing. Copy objects rather than mutating them \u2014 verified, mutating leaves the screen and the data disagreeing.
  • type(x) is not type(y) makes (1, 2) and [1, 2] different states, so pick one container type per field.
  • Group state that always changes together. It removes the \"forgot the second setter\" bug; it does not reduce renders (verified: same count both ways).
  • Replace mutually exclusive booleans with one variable holding one of several values, so the impossible state is unrepresentable.
  • Derive anything you can calculate during render. Both versions of the name form render the same number of times; only one of them can show a stale ticket.
  • Store ids, not copies. Edit the selected row and the derived label follows while the stored one does not.
  • Count how many objects an update must rebuild. If nesting forces you to rebuild the path to every leaf, flatten and key by id.
"},{"location":"learn/choosing-the-state-structure/#related","title":"Related","text":"
  • You Might Not Need an Effect \u2014 the same derive-don't-store rule applied to Effects
  • Lifecycle of Reactive Effects \u2014 strictly_equal in full, and why dependency arrays use ==
  • Updating Objects in State and Updating Arrays in State \u2014 the copy idioms this page assumes
  • Queueing a Series of State Updates \u2014 functional updaters, for when two handlers race
  • Extracting State Logic into a Reducer \u2014 the next step once one component holds too many fields
  • Use State \u2014 parameter-level reference
"},{"location":"learn/communicate-data-between-server-and-client/","title":"Communicating Data Between Server and Client","text":""},{"location":"learn/communicate-data-between-server-and-client/#overview","title":"Overview","text":"

ReactPy connects a Python process and a browser tab with one websocket. Everything crossing it is JSON, and nothing is pushed: the server sends rendered models, the client sends events, and a handler which wants to know something about the browser has to ask for it one property at a time. That constraint decides how every kind of data moves \u2014 values the server knows flow down as models, values the user produces flow up as events, and anything else needs a deliberate detour. This page is a map of those routes \u2014 what crosses, what cannot, and the honest workaround for each.

You will learn

  • Which channels actually exist, and the one direction that is not supported
  • How to push a server-side value into a view without the client asking
  • What an event actually gives your handler \u2014 including why file uploads do not work the way people expect
  • How to get real file bytes across, and how to keep blocking code off the loop
"},{"location":"learn/communicate-data-between-server-and-client/#the-channels-that-exist","title":"The channels that exist","text":"Direction Mechanism Your control server \u2192 client the rendered model, sent as layout-update patches total \u2014 return it from the component client \u2192 server an event handler, receiving layout-event total \u2014 read event server \u2192 client, on demand event-proxy-request / event-proxy-response, driven by await on an event indirect \u2014 you ask by awaiting, ReactPy frames the message server \u2192 client, arbitrary message none not supported shared mutable state none the two sides hold different objects

The third row is new, and it is narrower than it looks: the request row exists because a handler pulled, on the back of an event the client chose to send. A read carries no application data and starts no conversation \u2014 see Protocol Structure. Calling a method is the exception which proves the rule: await event.getCoalescedEvents() ships your arguments down as JSON (_event_proxy.py, _begin), which is data travelling the direction the table says does not exist. Reads are the default; a call is a door you have to choose to open.

The server-to-client push row is the one people look for first, and its absence is a design choice, not an omission. A component's Connection has no send, no deliver, no close (User Connection), and the backend is the only code allowed to write to the socket. That keeps the model the single source of truth: if a component could scribble on the wire, the client's tree and the server's model would drift apart with nothing to reconcile them.

A hand-written message is dropped by the client, silently

A custom backend can write anything to the socket, so it is tempting to invent a message type. Verified against a real browser: a {\"type\": \"my-custom\", ...} frame arrives, and the client logs

[ReactPy] Received message without handler Object\n

and discards it. The client dispatches on message.type against a fixed registry \u2014 \"layout-update\" is the only one registered by the mount path \u2014 and an unregistered type warns and returns. Nothing throws, the socket stays up, and the UI never changes. The page's only entry point is mountReactPy, and nothing in that path lets you register a handler of your own: onMessage exists on the client classes, but a page built by mountReactPy never hands you the instance (The client object). Do not build on this path; use one of the routes below.

"},{"location":"learn/communicate-data-between-server-and-client/#down-push-a-value-the-client-never-asked-for","title":"Down: push a value the client never asked for","text":"

Anything you can await on the server becomes a render, and a render reaches the client. An use_async_effect that loops is the whole pattern:

app.py Run
async def fetch_price(symbol):\n    \"\"\"Stand-in for a request to an exchange, a sensor, a queue.\"\"\"\n    await asyncio.sleep(1)\n    return f\"{symbol}: 42.10\"\n\n\n@component\ndef ticker(symbol):\n    price, set_price = use_state(\"loading...\")\n\n    async def poll():\n        while True:\n            try:\n                set_price(await fetch_price(symbol))\n            except Exception as error:\n                set_price(f\"error: {error}\")\n            try:\n                await asyncio.sleep(1)\n            except asyncio.CancelledError:\n                break\n\n    use_async_effect(poll)\n\n    return html.div(html.h1(price))\n
# TODO\n

Verified end to end: the first patch carried [\"loading...\"], and about a second later a second patch arrived with [\"BTC: 42.10\"] \u2014 no client event in between. The client applied it like any other update.

Three things make or break this:

  • Use use_async_effect, not use_effect. An async def passed to use_effect raises TypeError: \\use_effect` does not support async functions. Use `use_async_effect` instead.The effect takes **zero arguments** \u2014 the#!python async def effect(stop)shape from 1.x no longer works. Note where that error surfaces:#!python use_effectraises during the **render**, not inside the effect task, so you get a blank region \u2014 the model is#!python {\"tagName\": \"\", \"error\": \"\"}with debug off, and carries theTypeErrortext with#!python REACTPY_DEBUGon \u2014 and noError in effect` log line at all. That distinguishes it from a raising effect body, which is silent everywhere until unmount (Lifecycle of Reactive Effects).
  • Loop and honour cancellation. A long-lived effect is while True: with except asyncio.CancelledError: break. ReactPy cancels the task on unmount; shield=True opts out if the work must finish.
  • The dependency array is not React's. Omitted means inferred from the closure's free variables, dependencies=[] starts once, dependencies=None restarts every render. Never omit the array expecting \"run after every render\". See Use Async Effect.

Polling is fine for a demo and wrong at scale. For real work, prefer a server-sent queue that the effect awaits \u2014 the effect suspends on await queue.get() and the render happens the moment an item lands, with no timer at all.

"},{"location":"learn/communicate-data-between-server-and-client/#up-read-what-the-client-sends","title":"Up: read what the client sends","text":"

An event handler receives a live proxy onto the browser's event, not a copy of it. The client keeps the object and hands the server a handle; each await on a property is a question asked of the page at that moment (event-proxy.ts, serializeValue). Nothing about the DOM event arrives unless you ask for it, which is the whole point: an <input> used to arrive as the entire HTMLInputElement \u2014 a measured onChange payload carried roughly ninety keys, accept, formAction, validity, and the rest \u2014 of which a handler reads value.

What a read gives back is decided by the client. Plain data comes across as itself. A method comes back as a reference, so await event.target.focus() is a call the client makes rather than a function you receive. An object too deep, too large, or recursive comes back as a reference too, and you reach further with more awaits. Blocked members \u2014 window, document, parentNode, eval, setTimeout \u2014 are refused outright, so a read cannot be used to walk out of the event and into the page.

On submit, the target is the form, and the thing you want is one level down. HTMLFormControlsCollection \u2014 what form.elements holds \u2014 is iterable, and the client hands over anything iterable as a list (serializeValue), so await event.target.elements answers with one entry per control, named or not, each a plain dict of a hundred properties. The form itself is iterable too, so await event.target gives you the same controls; .elements is just the name which says what you mean:

async def handle_submit(event):\n    event.preventDefault()\n    controls = await event.target.elements  # list of control dicts\n    payload = next(\n        (c[\"value\"] for c in controls if c.get(\"name\") == \"payload\"), \"\"\n    )\n

Note what makes that readable: the list arrives as data, so c[\"value\"] is a plain subscript on the answer. The proxy itself is not subscriptable, but the JSON the client sent back is an ordinary list of dicts. The nested back-references inside each control (c[\"form\"], which points at the form again) come through as references rather than copies. The full working example is in Files below, and the wire format is in Protocol Structure.

Because each read is a round trip, how you write them costs time. Awaiting two properties one after another is two trips; await event.read(\"target.value\", \"key\") or an asyncio.gather of them is one. A read you never make costs nothing at all.

"},{"location":"learn/communicate-data-between-server-and-client/#files-what-actually-crosses-the-wire","title":"Files: what actually crosses the wire","text":"

A file input does not send file contents. It sends file metadata. Re-verified in Chrome against a real <input type=\"file\"> \u2014 this is the entire files entry that arrived for a 12-byte upload.txt:

[\n    {\n        \"name\": \"upload.txt\",\n        \"lastModified\": 1792302335371,\n        \"lastModifiedDate\": \"2026-10-18T05:45:35.371Z\",\n        \"webkitRelativePath\": \"\",\n        \"size\": 12,\n        \"type\": \"text/plain\",\n    }\n]\n

No bytes. File exposes its content through methods, and the client hands methods over as references rather than values, so there is nothing to read. There is no FormData in the client and no chunked upload path. If you expected (await event.target.files)[0][\"content\"], it does not exist and never will from this mechanism.

"},{"location":"learn/communicate-data-between-server-and-client/#reading-real-bytes","title":"Reading real bytes","text":"

The route is: read the file in the browser with JavaScript, put the text into a normal form field, and let an ordinary submit carry it. This works \u2014 verified in Chrome, with a 12-character file arriving server-side as 'hello world\\n':

app.py Run
READ_FILE = (\n    \"const file = event.target.files[0];\"\n    \"if (file) {\"\n    \"    file.text().then((text) => {\"\n    \"        document.getElementById('payload').value = text;\"\n    \"    });\"\n    \"}\"\n)\n\n\n@component\ndef text_upload():\n    received, set_received = use_state(\"Choose a file, then send.\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        # Ask for the collection which exists for this purpose: an\n        # HTMLFormControlsCollection is iterable, so the client hands it\n        # over as a list - one dict per control, named or not.\n        controls = await event.target.elements\n        payload = next(\n            (c[\"value\"] for c in controls if c.get(\"name\") == \"payload\"),\n            \"\",\n        )\n        set_received(f\"{len(payload)} characters received\")\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.input({\"type\": \"file\", \"onChange\": READ_FILE}),\n        html.input({\"type\": \"hidden\", \"id\": \"payload\", \"name\": \"payload\", \"value\": \"\"}),\n        html.button({\"type\": \"submit\"}, \"Send\"),\n        html.pre(received),\n    )\n
# TODO\n

The mechanics, each of which matters:

  • The onChange value is a string, which routes it to inlineJavaScript instead of eventHandlers \u2014 that is what makes it run in the page (Creating VDOM Event Handlers).
  • file.text() returns a promise, so the assignment happens in a callback \u2014 which is why a submit button is required. Sending immediately from the change handler races the promise and ships an empty string.
  • The hidden input needs both id (for the JavaScript to find it) and name (for the form walk to include it).
  • event.preventDefault() must appear literally in the handler's body. ReactPy detects it by bytecode inspection; calling a helper that calls it is not detected, and the page navigates away. See the bound-method caveat in Creating VDOM Event Handlers.

Untrusted content and eval

The inline-JavaScript string is eval'd. Build it from constants only \u2014 f\"...{user_input}...\" is an injection point. And anything you read off a user's file is untrusted data: cap the size before you buffer it, and never render it back without escaping.

Binary uploads work the same way via file.arrayBuffer() plus base64, but base64 costs a third of your bandwidth on top of a JSON string over a socket. For anything large, upload to a normal HTTP endpoint and pass the resulting id through state instead.

"},{"location":"learn/communicate-data-between-server-and-client/#blocking-code-and-other-threads","title":"Blocking code and other threads","text":"

Everything a component does runs on the connection's event loop. A synchronous, CPU-bound call inside a render or an effect freezes that connection \u2014 and the handlers on it. Move blocking work with asyncio.to_thread:

app.py Run
def blocking_render(width):\n    \"\"\"A synchronous, CPU-bound library call. Never run this on the loop.\"\"\"\n    time.sleep(0.4)  # stands in for image encoding, model inference, ...\n    return f\"rendered {width}px\"\n\n\n@component\ndef preview(width):\n    result, set_result = use_state(\"rendering...\")\n\n    async def render():\n        # to_thread keeps the loop free; the setter runs back on the loop.\n        set_result(await asyncio.to_thread(blocking_render, width))\n\n    use_async_effect(render, dependencies=[width])\n\n    return html.h1(result)\n
# TODO\n

Verified: the first patch says rendering..., the next says rendered 300px, and the loop stayed responsive across the 0.4 s call. Note that the setter is called from inside the effect, which is already on the loop.

Calling a state setter from another thread loses the update

Handing set_value to a raw threading.Thread is the natural mistake, and it fails with no user-visible symptom. Verified \u2014 the setter raises on the worker thread, and no render ever arrives:

Failed to schedule render via <function _make_life_cycle_state.<locals>.<lambda> ...>\nRuntimeError: no running event loop\nRuntimeWarning: coroutine 'Layout._create_layout_update' was never awaited\n

Nothing appears in the browser console; the component simply never updates. The fix is to capture the loop while you are on it and marshal back:

loop = asyncio.get_running_loop()  # inside the component, on the loop\n\ndef deliver_from_thread(value):\n    loop.call_soon_threadsafe(set_value, value)\n

Verified working \u2014 the update arrived as a normal patch. Prefer asyncio.to_thread when you can, since it removes the bookkeeping entirely.

Do not hang state on the component function

my_component.started = True as a run-once guard looks harmless and is a cross-user leak: the function object is module-scope, so the first visitor flips it for everyone. Give the component a prop, key the child, or guard with use_ref. The general version of this is in VDOM Mutations.

"},{"location":"learn/communicate-data-between-server-and-client/#client-only-data","title":"Client-only data","text":"

The mirror problem: something in the browser that Python cannot see. The proxy narrowed this considerably \u2014 anything reachable from the event object is now readable, so await event.target.scrollTop, await event.target.className, or a value a canvas put on its own element are ordinary awaits. What is still out of reach is anything the page will not hand over from that starting point: computed style of an element you never got an event from, localStorage, geolocation, the clipboard. The client refuses the paths which would lead there anyway (window, document, localStorage), so a handler cannot walk off the event and into the page \u2014 see Protocol Structure.

Your options, in order of preference:

  1. Move the computation server-side. If the value depends only on data you already have, compute it in Python. Server-side rendering is the default for a reason.
  2. Copy it into a form field and submit, exactly as the file example does. Clunky, and it works.
  3. Use the browser API in inline JavaScript and let it act locally \u2014 copy to clipboard, request fullscreen, scroll. Keep it to one call, and never interpolate user input into the string.
  4. Render the data yourself. A chart is usually better as a server-built <svg> or a generated image (reactpy.widgets.image) than as JavaScript in the page.

There is no use_ref-style handle to a DOM node, and {\"ref\": ...} either vanishes or throws in the browser \u2014 see Manipulating the DOM with Refs.

"},{"location":"learn/communicate-data-between-server-and-client/#summary","title":"Summary","text":"
  • Only two channels exist: the model down, events up. An arbitrary server\u2192client message is logged as Received message without handler and dropped.
  • Server push means use_async_effect plus a state setter \u2014 loop, handle CancelledError, and remember the dependency array infers by default.
  • Events are live proxies, not snapshots: each await is a round trip to the browser, a read you never make costs nothing, and submit hands back the form as a list of controls.
  • File inputs send metadata only, never bytes. Read the file in the browser, put the text in a named hidden input, and submit.
  • Use asyncio.to_thread for blocking work. Calling a setter from a raw thread fails with no running event loop and no visible symptom \u2014 marshal with call_soon_threadsafe.
  • State never lives on the component function object; that is a leak across connections.
"},{"location":"learn/communicate-data-between-server-and-client/#related","title":"Related","text":"
  • Protocol Structure \u2014 the message types, target, and seq
  • Use Async Effect and Use Effect \u2014 the push mechanism, and its traps
  • Creating VDOM Event Handlers \u2014 payload shape, inline JavaScript, bytecode detection
  • User Connection \u2014 the read-only connection view
  • Manipulating the DOM with Refs \u2014 client-only capabilities
  • Creating Backends \u2014 where the socket actually lives
"},{"location":"learn/conditional-rendering/","title":"Conditional Rendering","text":""},{"location":"learn/conditional-rendering/#overview","title":"Overview","text":"

Your components will often need to display different things depending on different conditions. In ReactPy, you can conditionally render markup using Python syntax like if statements and conditional expressions.

You will learn

  • How to return different markup depending on a condition
  • How to conditionally include or exclude a piece of markup
  • Conditional syntax shortcuts you'll encounter in ReactPy codebases
"},{"location":"learn/conditional-rendering/#conditionally-returning-markup","title":"Conditionally returning markup","text":"

Let's say you have a packing_list() component rendering several item()s, which can be marked as packed or not. Notice that some of the item() calls have their is_packed prop set to True instead of False. You want to add a checkmark (\u2714) to packed items if is_packed is True.

You can write this as an if/else statement like so:

if is_packed:\n    return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\nreturn html.li({\"className\": \"item\"}, name)\n

If the is_packed prop is True, this code returns a different VDOM tree. With this change, some of the items get a checkmark at the end:

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    if is_packed:\n        return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\n    return html.li({\"className\": \"item\"}, name)\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=True, name=\"Space suit\"),\n            item(is_packed=True, name=\"Helmet with a golden leaf\"),\n            item(is_packed=False, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

Try editing what gets returned in either case, and see how the result changes!

Notice how you're creating branching logic with Python's if and return statements. In ReactPy, control flow (like conditions) is handled by Python.

"},{"location":"learn/conditional-rendering/#conditionally-returning-nothing-with-none","title":"Conditionally returning nothing with None","text":"

In some situations, you won't want to render anything at all. For example, say you don't want to show packed items at all. A component must return something \u2014 in ReactPy, that something is None:

if is_packed:\n    return None\nreturn html.li({\"className\": \"item\"}, name)\n

If is_packed is true, the component will return nothing, None. Otherwise, it will return markup to render.

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    if is_packed:\n        return None\n    return html.li({\"className\": \"item\"}, name)\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=False, name=\"Space suit\"),\n            item(is_packed=False, name=\"Helmet with a golden leaf\"),\n            item(is_packed=True, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

In practice, returning None from a component isn't common because it might surprise a developer trying to render it. More often, you would conditionally include or exclude the component in the parent component. Here's how to do that!

"},{"location":"learn/conditional-rendering/#conditionally-including-markup","title":"Conditionally including markup","text":"

In the previous example, you controlled which (if any!) VDOM tree would be returned by the component. You may already have noticed some duplication in the render output:

html.li({\"className\": \"item\"}, f\"{name} \u2714\")\n

is very similar to

html.li({\"className\": \"item\"}, name)\n

Both of the conditional branches return html.li({\"className\": \"item\"}, ...):

if is_packed:\n    return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\nreturn html.li({\"className\": \"item\"}, name)\n

While this duplication isn't harmful, it could make your code harder to maintain. What if you want to change the className? You'd have to do it in two places in your code! In such a situation, you could conditionally include a little markup to make your code more DRY.

"},{"location":"learn/conditional-rendering/#conditional-ternary-expression","title":"Conditional (ternary) expression","text":"

Python has a compact syntax for writing a conditional expression \u2014 the conditional expression or \"ternary operator\".

Instead of this:

if is_packed:\n    return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\nreturn html.li({\"className\": \"item\"}, name)\n

You can write this:

return html.li({\"className\": \"item\"}, f\"{name} \u2714\" if is_packed else name)\n

You can read it as \"if is_packed is true, then render f\"{name} \u2714\", otherwise render name\".

Now let's say you want to wrap the completed item's text into another HTML tag, like <del> to strike it out. You can add even more newlines and parentheses so that it's easier to nest more markup in each of the cases:

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    return html.li(\n        {\"className\": \"item\"},\n        html.del_(f\"{name} \u2714\") if is_packed else name,\n    )\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=True, name=\"Space suit\"),\n            item(is_packed=True, name=\"Helmet with a golden leaf\"),\n            item(is_packed=False, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

This style works well for simple conditions, but use it in moderation. If your components get messy with too much nested conditional markup, consider extracting child components to clean things up. In ReactPy, markup is a part of your code, so you can use tools like variables and functions to tidy up complex expressions.

Are these two examples fully equivalent?

If you're coming from an object-oriented programming background, you might assume that the two examples above are subtly different because one of them may create two different \"instances\" of html.li. But VDOM dictionaries aren't \"instances\" because they don't hold any internal state and aren't real DOM nodes. They're lightweight descriptions, like blueprints. So these two examples, in fact, are completely equivalent. Preserving and Resetting State goes into detail about how this works.

"},{"location":"learn/conditional-rendering/#excluding-a-child-with-a-conditional","title":"Excluding a child with a conditional","text":"

In JavaScript React you'll often see the logical AND operator used to render a child only when a condition holds:

<li className=\"item\">{name} {isPacked && \"\u2714\"}</li>\n

There is no equivalent of this idiom in ReactPy. Python's and returns one of its operands rather than a renderable-agnostic False, and ReactPy renders False as the literal text \"False\" instead of skipping it:

# WRONG: renders \"Photo of Tam False\" when is_packed is False\nreturn html.li({\"className\": \"item\"}, name, is_packed and \"\u2714\")\n

Reach for a conditional expression that yields an empty list, and unpack it:

return html.li(\n    {\"className\": \"item\"},\n    name,\n    *([\"\u2714\"] if is_packed else []),\n)\n

Or build the children list up front, which reads better as the number of optional children grows:

@component\ndef item(name, is_packed):\n    content = [name]\n    if is_packed:\n        content.append(\"\u2714\")\n    return html.li({\"className\": \"item\"}, *content)\n

Don't put a number on the left side of a conditional expression

The classic messageCount && <p>New messages</p> bug has an exact Python twin. and returns the left operand when it is falsy, and ReactPy renders numbers as text \u2014 so count and html.p(\"New messages\") renders 0 rather than nothing when count is 0. Make the left side a genuine boolean test:

html.p(\"New messages\") if count > 0 else None\n
"},{"location":"learn/conditional-rendering/#conditionally-assigning-markup-to-a-variable","title":"Conditionally assigning markup to a variable","text":"

When the shortcuts get in the way of writing plain code, try using an if statement and a variable. Start by providing the default content you want to display, the name:

item_content = name\n

Use an if statement to reassign a VDOM expression to item_content if is_packed is True:

if is_packed:\n    item_content = html.del_(f\"{name} \u2714\")\n

Embed the variable in the returned VDOM tree, nesting the previously calculated expression inside the parent element:

html.li({\"className\": \"item\"}, item_content)\n

This style is the most verbose, but it's also the most flexible:

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    item_content = name\n    if is_packed:\n        item_content = html.del_(f\"{name} \u2714\")\n    return html.li({\"className\": \"item\"}, item_content)\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=True, name=\"Space suit\"),\n            item(is_packed=True, name=\"Helmet with a golden leaf\"),\n            item(is_packed=False, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

Like before, this works not only for text, but for arbitrary markup too.

Tags that collide with Python keywords

del is a Python keyword, so the <del> tag is spelled with a trailing underscore: html.del_. The underscore is stripped before the tag name is emitted, so it still renders <del>. The same rule covers html.import_, html.object, and friends \u2014 and dashes become underscores, so <data-table> is html.data_table.

If you're not familiar with Python, this variety of styles might seem overwhelming at first. However, learning them will help you read and write any Python code \u2014 and not just ReactPy components! Pick the one you prefer for a start, and then consult this reference again if you forget how the other ones work.

"},{"location":"learn/conditional-rendering/#recap","title":"Recap","text":"
  • To conditionally return different markup from a component, use if statements and return.
  • To render nothing, return None from the component.
  • To tidy up duplicated markup, use a conditional expression: x if condition else y.
  • Don't use and to include a child \u2014 ReactPy renders False as the text \"False\". Unpack an empty list instead.
  • For anything more complex, assign children to variables with if statements.
"},{"location":"learn/convert-between-vdom-and-html/","title":"Convert Between VDOM and HTML","text":""},{"location":"learn/convert-between-vdom-and-html/#overview","title":"Overview","text":"

ReactPy's UI is a plain dictionary. Two functions move it across the boundary to and from ordinary HTML text: reactpy_to_string() renders a VDOM dictionary or a component to an HTML string, and string_to_reactpy() parses an HTML string into a VDOM dictionary. Both live at the top of the package, and both are load-bearing inside ReactPy itself \u2014 the executors build the initial document and the PyScript head with them.

You will learn

  • How to turn VDOM into an HTML string, and what that conversion throws away
  • How to parse HTML text into a VDOM dictionary, and the renames it performs on the way in
  • Why className survives parsing but not serialization
  • When to reach for string_to_reactpy() instead of writing html.* calls
"},{"location":"learn/convert-between-vdom-and-html/#the-two-functions","title":"The two functions","text":"Function In Out Raises on bad input reactpy_to_string(root) VdomDict or a called Component str of HTML TypeError on a callable attribute string_to_reactpy(html, *transforms, strict=True, intercept_links=True) str of HTML VdomDict HTMLParseError, ValueError, TypeError

Both are exported from the package root:

from reactpy import reactpy_to_string, string_to_reactpy\n

Neither is a serializer for the wire. The socket protocol has its own shape, described in Protocol Structure; these two functions exist so you can hand ReactPy's output to something that speaks HTML, and hand HTML from somewhere else to ReactPy.

"},{"location":"learn/convert-between-vdom-and-html/#vdom-to-html-reactpy_to_string","title":"VDOM to HTML: reactpy_to_string()","text":"

Give it an element, or a component that has already been called:

app.py Run
todo_list = html.div(\n    html.h1(\"My Todo List\"),\n    html.ul(\n        html.li(\"Build a cool new app\"),\n        html.li(\"Share it with the world!\"),\n    ),\n)\n\nprint(reactpy_to_string(todo_list))\n# <div><h1>My Todo List</h1><ul><li>Build a cool new app</li><li>Share it with the world!</li></ul></div>\n
# TODO\n

There is no formatting option, no indent, no doctype. You get the tags and the attributes and nothing else.

"},{"location":"learn/convert-between-vdom-and-html/#what-it-renders","title":"What it renders","text":"

reactpy_to_string() walks tagName, attributes, and children (utils.py, _add_vdom_to_etree). Everything else in the dictionary is ignored:

  • Event handlers disappear. A {\"onClick\": handler} produces no attribute at all. The value is a Python function; there is nothing sensible to write into HTML text.
  • Inline JavaScript disappears too. inlineJavaScript is not consulted, so reactpy_to_string(html.button({\"onClick\": \"this.innerText = 1\"}, \"x\")) returns <button>x</button> with the script gone.
  • An importSource disappears. A web-module component renders as its bare tag name, which no browser knows what to do with.
  • A component child is rendered. _add_vdom_to_etree calls component_to_vdom() on any child with a .render attribute, and component_to_vdom() recurses, so nested components come out as HTML \u2014 verified two levels down, html.section(mid()) becomes <section><div><p>deep</p></div></section>. The limit is hooks, not depth.

That list is why this function cannot render your average app. It has no Layout, so there is no hook stack: use_state, use_ref, use_effect, use_location all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? Only hook-free components and plain html.* elements convert. This is the same path ReactPy(prepend_body=...) uses, which is why that argument takes an element and not a stateful component.

Passing an uncalled component raises AttributeError

reactpy_to_string(my_component) fails with AttributeError: 'function' object has no attribute 'render' \u2014 the decorator has not run, so there is nothing to render. Call it: reactpy_to_string(my_component()).

"},{"location":"learn/convert-between-vdom-and-html/#attribute-names-are-lowercased","title":"Attribute names are lowercased","text":"

This is the sharpest edge on the page, and it is silent. On the way out, every attribute name goes through .lower() (utils.py, _react_attribute_to_html), with the exception of data-* and aria-*, which are left exactly as you wrote them:

app.py Run
# HTML in, ReactPy out: camelCase comes back.\nprint(string_to_reactpy('<label for=\"email\">Email</label>'))\n# {'attributes': {'htmlFor': 'email'}, 'children': ['Email'], 'tagName': 'label'}\n\n# ReactPy out: every attribute name is lowercased on the way out.\nprint(reactpy_to_string(html.label({\"htmlFor\": \"email\"}, \"Email\")))\n# <label htmlfor=\"email\">Email</label>\n\nprint(reactpy_to_string(html.label({\"for\": \"email\"}, \"Email\")))\n# <label for=\"email\">Email</label>\n\nprint(reactpy_to_string(html.div({\"className\": \"card\"}, \"x\")))\n# <div classname=\"card\">x</div>\n
# TODO\n

htmlFor becomes htmlfor and className becomes classname \u2014 neither of which is a real HTML attribute, so the browser ignores both. The label stops labelling and the class stops styling, with no error anywhere.

Write the HTML name when the string is the destination. {\"for\": \"email\"} and {\"class\": \"card\"} produce correct markup; {\"htmlFor\": ...} and {\"className\": ...} do not. There is no single spelling that satisfies both this and live rendering \u2014 see Common Props for why className is what the browser-side path wants.

The one attribute ReactPy does translate is style, which it flattens from a dict into a key:value; string and kebab-cases on the way:

reactpy_to_string(html.div({\"style\": {\"fontSize\": \"20px\", \"zIndex\": 3}}, \"s\"))\n# <div style=\"font-size:20px;z-index:3\">s</div>\n

Note what did not happen to the 3: no unit is inferred. z-index:3 is fine because it takes no unit, but font-size:30 would be invalid CSS where the browser-side renderer would have applied it as 30px. Always put the unit in the string.

Some boolean attributes obey lxml, not HTML

ReactPy renders the string through lxml, which carries its own list of known boolean attributes. For a name on that list the value is discarded and only presence matters \u2014 so False still emits the attribute:

app.py Run
# lxml knows a fixed list of boolean attributes. For those, the value is\n# dropped and presence is all that survives -- False included.\nprint(reactpy_to_string(html.input({\"type\": \"checkbox\", \"checked\": False})))\n# <input type=\"checkbox\" checked>\n\n# For everything else the value is str()-ed, so False leaks into the page.\nprint(reactpy_to_string(html.div({\"hidden\": False}, \"still hidden\")))\n# <div hidden=\"False\">still hidden</div>\n
# TODO\n

checked renders as <input type=\"checkbox\" checked> even though you said False. Names outside that list get str(value) instead, which leaks the word False into your markup as in <div hidden=\"False\">. Which names fall in which group depends on your lxml build, so test the attribute you care about. The safe form for a server-rendered string is to omit the key entirely rather than set it to False.

"},{"location":"learn/convert-between-vdom-and-html/#html-to-vdom-string_to_reactpy","title":"HTML to VDOM: string_to_reactpy()","text":"app.py Run
model = string_to_reactpy('<div class=\"card\"><h1>My Todo List</h1></div>')\n\nprint(model[\"tagName\"])  # div\nprint(model[\"attributes\"])  # {'className': 'card'}\nprint(model[\"children\"])  # [{'tagName': 'h1', 'children': ['My Todo List']}]\n\n# Nested children are full models too, which is easiest to see with pprint:\n# pprint(model, sort_dicts=False)\n# {'children': [{'children': ['My Todo List'], 'tagName': 'h1'}],\n#  'attributes': {'className': 'card'},\n#  'tagName': 'div'}\n
# TODO\n

Parsing is done by lxml.html, then each node is rebuilt through the normal html.* constructors (utils.py, _etree_to_vdom), which means what comes back is a genuine VdomDict you can render, patch, and send over the socket.

"},{"location":"learn/convert-between-vdom-and-html/#what-parsing-renames-and-rewrites","title":"What parsing renames and rewrites","text":"

The parser does not give you a mirror of the input. It runs a table of renames and a handful of node rewrites (transforms.py, attributes_to_reactjs and RequiredTransforms):

In the HTML In the VDOM Why class=\"card\" {\"className\": \"card\"} React-style prop name for=\"email\" {\"htmlFor\": \"email\"} React-style prop name checked {\"defaultChecked\": ...} Controlled-vs-uncontrolled accept-charset {\"acceptCharset\": ...} React-style prop name http-equiv {\"httpEquiv\": ...} React-style prop name style=\"font-size:20px\" {\"style\": {\"fontSize\": \"20px\"}} Style strings become dicts <input value=\"ab\"> {\"defaultValue\": \"ab\"} A value prop makes an input controlled <textarea>hi</textarea> {\"defaultValue\": \"hi\"}, no children Same reason <option selected> defaultValue on the parent <select> React puts the selection on the parent <a href=\"/x\"> an onClick handler with prevent_default Keeps a client router in charge <div id=\"uniq\"> {\"id\": \"uniq\", \"key\": \"uniq\"} A key is inferred from id, then name

The last two deserve a second look, because they change behaviour rather than spelling.

Every anchor gets a click handler. By default intercept_links=True installs a preventDefault-only onClick on every <a> (transforms.py, intercept_link_clicks). The link looks dead: clicking it does nothing, because ReactPy has no router to hand the navigation to. Pass intercept_links=False to keep real links.

An id becomes a key. infer_key_from_attributes copies id \u2014 or name on <input>, <select>, <textarea> \u2014 into attributes[\"key\"]. That is usually what you want, since a stable id is a stable identity, but it also means two elements parsed with the same id collide: the layout rejects them with ValueError: Duplicate keys [...].

Whitespace you did not ask for

lxml is configured to drop comments, processing instructions, and blank text \u2014 but remove_blank_text only removes whitespace that is purely formatting. The newline and indentation between tags in a pretty-printed document survive as text children:

string_to_reactpy(\"<ul>\\n  <li>a</li>\\n  <li>b</li>\\n</ul>\")[\"children\"]\n# [{'tagName': 'li', 'children': ['a']}, '\\n  ', {'tagName': 'li', 'children': ['b']}, '\\n']\n

Those strings render as visible whitespace. If you parse an indented template literal, expect stray spaces and newlines in the layout \u2014 write the markup flat, or strip the text children in a transform.

"},{"location":"learn/convert-between-vdom-and-html/#errors-and-when-to-turn-strictness-off","title":"Errors, and when to turn strictness off","text":"

string_to_reactpy() raises in four distinct ways:

string_to_reactpy(12)          # TypeError: Expected html to be a string, not int\nstring_to_reactpy(\"no tags here\")  # ValueError: Expected html string to contain HTML tags, but no tags were found.\nstring_to_reactpy(\"<div <p>x</p>\")  # HTMLParseError (a subclass of lxml.LxmlSyntaxError)\nstring_to_reactpy(\"<div <p>x</p>\", strict=False)  # parses; '<p' becomes an attribute name\n

strict=True reaches lxml as recover=False, so a document lxml refuses to parse cleanly becomes an HTMLParseError whose message tells you to turn strictness off. Do that deliberately: recovery invents attribute names out of the malformed text rather than complaining, so you trade a loud failure for a quietly wrong model.

A few things you might expect strict to catch, and it does not:

  • Unclosed or mismatched tags \u2014 <div><span>x</div> parses fine and the span is closed for you.
  • Orphan fragments \u2014 <td>orphan</td> and <li>x</li> parse into bare td/li models, which then render somewhere HTML forbids them.
  • Unknown entities \u2014 &notreal; becomes the literal text \u00acreal;.
"},{"location":"learn/convert-between-vdom-and-html/#wrapping-empty-input-and-multiple-roots","title":"Wrapping, empty input, and multiple roots","text":"

lxml wants a single well-formed root, so it adds one for you:

  • <p>a</p><p>b</p> comes back wrapped in a div.
  • <body>hi</body> comes back as a span, not a body \u2014 lxml.html's document scaffolding leaks through.
  • <meta http-equiv=\"refresh\"> is likewise pulled into an html/head pair.
  • Empty or whitespace-only input returns {\"tagName\": \"\"} \u2014 a fragment, not an error.

Do not write code that indexes children by position on parsed input; the wrapper is lxml's decision, not yours.

"},{"location":"learn/convert-between-vdom-and-html/#transforming-every-node-the-transforms-argument","title":"Transforming every node: the *transforms argument","text":"

string_to_reactpy() takes any number of one-argument callables and applies each to every node after parsing (utils.py, _etree_to_vdom). This is the supported way to post-process the model rather than editing it after the fact:

def add_card_class(vdom):\n    if vdom[\"tagName\"] == \"div\":\n        vdom.setdefault(\"attributes\", {})[\"className\"] = \"card\"\n    return vdom\n\n\nstring_to_reactpy(\"<div>x</div>\", add_card_class)\n# {'tagName': 'div', 'attributes': {'className': 'card'}, 'children': ['x']}\n

Mutate and return the same dictionary; the return value replaces the node, assigned unconditionally (utils.py, _etree_to_vdom). A transform that returns None deletes the node \u2014 for a child it leaves None in children, for the root it makes string_to_reactpy() itself return None. Always return the model.

Transforms run after the built-in ones, so a style value is already a dict by the time yours sees it, and an <a> already has its onClick. Use this hook to strip attributes you do not trust from third-party HTML \u2014 but note that removing an attribute is not the same as making untrusted HTML safe:

Never parse untrusted HTML without filtering

string_to_reactpy() does not sanitize. An <img src=x onerror=\"alert(1)\"> comes back with the payload in inlineJavaScript, not in attributes \u2014 verified: {'attributes': {'src': 'x'}, 'inlineJavaScript': {'onError': 'alert(1)'}, 'tagName': 'img'} \u2014 and that is the more dangerous of the two outcomes: the client turns each inlineJavaScript entry into an event-handler prop whose body is eval(...) (static/index.js, the inline-JavaScript wrapper), so the code executes when the event fires. An <img src=x> fails to load, onError fires, and the payload runs. An <script> comes back as a script element with its content intact. ReactPy's own html.script refuses event handlers and injects an inferred key, but it does not inspect the JavaScript inside, and neither guard applies to what string_to_reactpy() produces. If the string came from a user, run it through a sanitizer first and pass intercept_links=False only once you have decided links are wanted.

"},{"location":"learn/convert-between-vdom-and-html/#using-the-result-in-a-component","title":"Using the result in a component","text":"

A parsed model is an ordinary element, so a component can return it directly:

app.py Run
@component\ndef card():\n    return string_to_reactpy('<div class=\"card\"><b>rendered from a string</b></div>')\n\n\nprint(reactpy_to_string(card()))\n# <div class=\"card\"><b>rendered from a string</b></div>\n
# TODO\n

That is exactly how ReactPy builds its own document head: ReactPy(pyscript_setup=True) parses a block of PyScript markup with string_to_reactpy() and splices it into html_head (executors/asgi/standalone.py).

Do not parse inside the render body

The parse is a full lxml document build, and it runs on the server on every render. Parse once at module level and return the stored model, or memoize it with use_memo. Re-parsing per render is pure waste, and if you put the model in state you also hand yourself the mutation hazard covered in VDOM Mutations.

"},{"location":"learn/convert-between-vdom-and-html/#choosing-a-direction","title":"Choosing a direction","text":"

Reach for these when the HTML exists somewhere other than in your Python code:

  • string_to_reactpy() when markup arrives from a CMS, a markdown renderer, an email body, or a template you do not control. It is also the shortest path to pasting a snippet of HTML from a design mock into a component.
  • reactpy_to_string() when you must hand ReactPy's output to something that is not the ReactPy client: server-rendered <head> content, an <noscript> fallback, an email or PDF generator, a test assertion.

If the markup is yours to write, write html.* calls. The constructors validate at the call site, keep their arguments JSON-serializable, and support handlers \u2014 none of which the string path gives you.

"},{"location":"learn/convert-between-vdom-and-html/#summary","title":"Summary","text":"
  • reactpy_to_string() takes a VdomDict or a called component and returns HTML text; it drops event handlers, inline JavaScript, and import sources, and can only render components that use no hooks.
  • It lowercases every attribute name except data-* and aria-*, so write \"class\" and \"for\" when the string is the destination.
  • string_to_reactpy() parses HTML into a real VdomDict, renaming to React-style prop names, converting style strings to dicts, and moving value/<textarea> content to defaultValue.
  • It intercepts link clicks and infers a key from id unless you tell it not to.
  • strict=False turns a loud HTMLParseError into a guessed model; lxml never complained about unclosed tags anyway.
  • Neither function is the wire format \u2014 that is Protocol Structure.
"},{"location":"learn/convert-between-vdom-and-html/#related","title":"Related","text":"
  • Creating HTML Tags \u2014 building the same models with html and Vdom
  • VDOM Mutations \u2014 what you may and may not do to a model after you get one
  • Creating VDOM Event Handlers \u2014 the eventHandlers slot these functions ignore
  • Common Props \u2014 attribute naming on the live rendering path
  • Protocol Structure \u2014 what actually goes over the socket
"},{"location":"learn/creating-a-react-app/","title":"Creating a Standalone React App","text":""},{"location":"learn/creating-a-react-app/#overview","title":"Overview","text":"

If you want to build a new app or website with React, we recommend starting with a standalone executor.

If your app has constraints not well-served by existing web frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, you can use ReactPy in standalone mode.

"},{"location":"learn/creating-a-react-app/#using-reactpy-for-full-stack","title":"Using ReactPy for full-stack","text":"

ReactPy is a component library that helps you build a full-stack web application. For convenience, ReactPy is also bundled with several different standalone executors.

These standalone executors are the easiest way to get started with ReactPy, as they require no additional setup or configuration.

Note

Standalone ReactPy requires a server

In order to serve the initial HTML page, you will need to run a server. The ASGI examples below use Uvicorn, but you can use any ASGI server.

Executors on this page can either support client-side rendering (CSR) or server-side rendering (SSR).

"},{"location":"learn/creating-a-react-app/#running-via-asgi-ssr","title":"Running via ASGI SSR","text":"

ReactPy can run in server-side standalone mode, where both page loading and component rendering occurs on an ASGI server.

This executor is the most commonly used, as it provides maximum extensibility.

First, install ReactPy and your preferred ASGI webserver.

Terminal

pip install reactpy[asgi] uvicorn[standard]\n

Next, create a new file called main.py containing the ASGI application:

main.py
from reactpy import component, html\nfrom reactpy.executors.asgi import ReactPy\n\n\n@component\ndef hello_world():\n    return html.div(\"Hello World\")\n\n\nmy_app = ReactPy(hello_world)\n

Finally, use your webserver of choice to start ReactPy:

Terminal

uvicorn main:my_app\n
"},{"location":"learn/creating-a-react-app/#running-via-asgi-csr","title":"Running via ASGI CSR","text":"

ReactPy can run in client-side standalone mode, where the initial page is served using the ASGI protocol. This is configuration allows direct execution of Javascript, but requires special considerations since all ReactPy component code is run on the browser via WebAssembly.

First, install ReactPy and your preferred ASGI webserver.

Terminal

pip install reactpy[asgi] uvicorn[standard]\n

Next, create a new file called main.py containing the ASGI application, and a root.py file containing the root component:

main.pyroot.py
from pathlib import Path\n\nfrom reactpy.executors.asgi import ReactPyCsr\n\nmy_app = ReactPyCsr(Path(__file__).parent / \"root.py\", initial=\"Loading...\")\n
from reactpy import component, html\n\n\n@component\ndef root():\n    return html.div(\"Hello World\")\n

Finally, use your webserver of choice to start ReactPy:

Terminal

uvicorn main:my_app\n
"},{"location":"learn/creating-a-react-app/#running-via-wsgi-ssr","title":"Running via WSGI SSR","text":"

Support for WSGI executors is coming in a future version.

"},{"location":"learn/creating-a-react-app/#running-via-wsgi-csr","title":"Running via WSGI CSR","text":"

Support for WSGI executors is coming in a future version.

"},{"location":"learn/creating-a-react-app/#running-as-a-native-app","title":"Running as a native app","text":"

Support for native apps (iO, Android, Windows, etc.) is coming in a future version.

"},{"location":"learn/creating-backends/","title":"Creating Backends","text":""},{"location":"learn/creating-backends/#overview","title":"Overview","text":"

A ReactPy backend is the piece that owns the HTTP routes, serves the client bundle, accepts the websocket, and drives a Layout against it. ReactPy and ReactPyMiddleware are two backends. This page is about writing the third: what the framework actually does for you, which parts you have to reproduce, and where the quiet traps are. It is also the fastest way to understand the request lifecycle, even if you never ship a custom backend.

You will learn

  • The four jobs a backend performs, in the order a browser makes them necessary
  • The three ASGI routes ReactPy needs, and the exact shapes of the messages on each
  • How to run a Layout by hand with serve_layout()
  • Why Connection is yours to construct, and what carrier means
"},{"location":"learn/creating-backends/#what-a-backend-owes-you","title":"What a backend owes you","text":"

Strip away the executors and there are exactly four responsibilities. Everything else \u2014 hot reloading, config validation, HTML assembly \u2014 is convenience on top.

  1. Serve the client bundle over HTTP, so the page can import it.
  2. Serve web modules over HTTP, so the client can import any importSource you emitted.
  3. Return a document containing a mount element and a mountReactPy(...) call.
  4. Accept a websocket and run one Layout against it, pumping messages in both directions.

The protocol is four message types. A rendered update goes down as {\"type\": \"layout-update\", \"path\": ..., \"model\": ...}; an event comes up as {\"type\": \"layout-event\", \"target\": ..., \"data\": [...]}; and when a handler reads a property of that event, the request goes down as {\"type\": \"event-proxy-request\", ...} and the browser's answer comes up as {\"type\": \"event-proxy-response\", ...}. Protocol Structure documents them in full \u2014 this page is about plumbing, not the schema.

The good news about the last pair: serve_layout wires both directions for you. It binds your send as the proxy sender, and Layout.deliver recognises a response and routes it to the proxy backend instead of treating it as an event. A backend which pumps every received frame into deliver and every message out through send supports live event access without knowing it exists. A backend that filters frames before deliver \u2014 the way the ASGI middleware does, matching on \"layout-event\" first \u2014 must route the responses itself or every await on an event stalls until it times out.

"},{"location":"learn/creating-backends/#the-job-in-one-file","title":"The job, in one file","text":"

This is a complete, working backend: no framework, no executor. It was run, and the counter incremented in a real browser.

main.py Run
\"\"\"A ReactPy backend with no framework: routing, static files, and Layout.\"\"\"\n\nimport asyncio\nfrom pathlib import Path\n\nimport orjson\nfrom asgi_tools import ResponseHTML\nfrom servestatic import ServeStaticASGI\n\nimport reactpy\nfrom reactpy import component, html, use_state\nfrom reactpy.config import REACTPY_WEB_MODULES_DIR\nfrom reactpy.core.hooks import ConnectionContext\nfrom reactpy.core.layout import Layout\nfrom reactpy.core.serve import serve_layout\nfrom reactpy.types import Connection, Location\n\nPATH_PREFIX = \"/reactpy/\"\nSTATIC_PATH = f\"{PATH_PREFIX}static/\"\nMODULES_PATH = f\"{PATH_PREFIX}modules/\"\n\nSTATIC_DIR = Path(reactpy.__file__).parent / \"static\"\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"Count: {count}\"),\n        html.button({\"onClick\": handle_count_click}, \"Increment\"),\n    )\n\n\ndef index_html() -> str:\n    mount = (\n        \"mountReactPy({mountElement: document.getElementById('app'),\"\n        \"  pathPrefix:'\" + PATH_PREFIX + \"', componentPath:'counter/'});\"\n    )\n    return (\n        \"<!doctype html><html><head><meta charset='utf-8'><title>Custom backend</title>\"\n        \"<script type='module'>\"\n        f\"import {{ mountReactPy }} from '{STATIC_PATH}index.js';\"\n        + mount\n        + \"</script></head><body><div id='app'>Loading...</div></body></html>\"\n    )\n\n\nclass Backend:\n    def __init__(self) -> None:\n        self.components = {\"counter\": counter}\n        self.static_app = ServeStaticASGI(\n            self.not_found, root=STATIC_DIR, prefix=STATIC_PATH\n        )\n        self.modules_app = ServeStaticASGI(\n            self.not_found, root=REACTPY_WEB_MODULES_DIR.current, prefix=MODULES_PATH\n        )\n\n    async def not_found(self, scope, receive, send) -> None:\n        await ResponseHTML(\"Resource not found on this server.\", status_code=404)(\n            scope, receive, send\n        )\n\n    async def __call__(self, scope, receive, send) -> None:\n        path = scope[\"path\"]\n        if scope[\"type\"] == \"http\" and path.startswith(STATIC_PATH):\n            return await self.static_app(scope, receive, send)\n        if scope[\"type\"] == \"http\" and path.startswith(MODULES_PATH):\n            return await self.modules_app(scope, receive, send)\n        if scope[\"type\"] == \"http\" and path == \"/\":\n            return await ResponseHTML(index_html())(scope, receive, send)\n        if scope[\"type\"] == \"websocket\" and path.startswith(PATH_PREFIX):\n            return await self.serve_component(scope, receive, send)\n        return await self.not_found(scope, receive, send)\n\n    async def serve_component(self, scope, receive, send) -> None:\n        name = scope[\"path\"][len(PATH_PREFIX) :].strip(\"/\")\n        if name not in self.components:\n            raise RuntimeError(f\"Attempting to use an unregistered root {name}.\")\n\n        await send({\"type\": \"websocket.accept\"})\n        queue: asyncio.Queue = asyncio.Queue(1000)\n\n        async def pump() -> None:\n            while True:\n                event = await receive()\n                if event[\"type\"] == \"websocket.disconnect\":\n                    return\n                if event[\"type\"] == \"websocket.connect\":\n                    continue\n                await queue.put(orjson.loads(event[\"text\"]))\n\n        async def recv():\n            return await queue.get()\n\n        async def send_model(message):\n            await send(\n                {\"type\": \"websocket.send\", \"text\": orjson.dumps(message).decode()}\n            )\n\n        task = asyncio.create_task(pump())\n        connection = Connection(\n            scope=scope,\n            location=Location(path=\"/\", query_string=\"\"),\n            carrier=None,\n        )\n        try:\n            await serve_layout(\n                Layout(ConnectionContext(self.components[name](), value=connection)),\n                send_model,\n                recv,\n            )\n        finally:\n            task.cancel()\n\n\napp = Backend()\n
# TODO\n

Run it with anything that speaks ASGI:

uvicorn main:app --port 8000\n

Read it top to bottom and the four jobs map onto four spots: static_app and modules_app are jobs 1 and 2, index_html() is job 3, and serve_component() is job 4. The rest is __call__ dispatching on scope[\"type\"].

"},{"location":"learn/creating-backends/#the-routes","title":"The routes","text":"Route Serves Supplied by {path_prefix}static/ index.js and its chunks reactpy/static/ inside the wheel {path_prefix}modules/ authored JavaScript modules config.REACTPY_WEB_MODULES_DIR {path_prefix}<name>/ (websocket) the layout for <name> your components mapping

The trailing slash on the websocket path is not decoration. The stock executors match ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$, and the client builds that URL by concatenating pathPrefix and componentPath. A custom backend can match anything \u2014 but then componentPath has to agree with it, and pathPrefix must start and end with /. See The URL is built by concatenation for why a missing slash fails loudly in development and silently in production.

"},{"location":"learn/creating-backends/#serving-the-bundle","title":"Serving the bundle","text":"

Two directories, both plain static files:

import reactpy\nfrom pathlib import Path\n\nfrom servestatic import ServeStaticASGI\n\nfrom reactpy.config import REACTPY_WEB_MODULES_DIR\n\nstatic_app = ServeStaticASGI(\n    not_found, root=Path(reactpy.__file__).parent / \"static\", prefix=\"/reactpy/static/\"\n)\nmodules_app = ServeStaticASGI(\n    not_found, root=REACTPY_WEB_MODULES_DIR.current, prefix=\"/reactpy/modules/\"\n)\n

servestatic is what the executors use, and it is a normal ASGI app \u2014 swap in your own static handler if you have one. Two details matter more than the choice of library.

Resolve REACTPY_WEB_MODULES_DIR.current at request time or shortly after, never at import time. It is an Option, and the executors repoint it. Reading it too early hands you a directory that later ceases to be the live one.

The <script type=\"module\"> must be a module. The bundle uses static import for its two sibling chunks, so an ordinary <script> fails before any of your code runs, usually with a bare SyntaxError: Cannot use import statement outside a module in the console.

Serve the bundle from your own path at your peril

The client imports its chunks by a path derived from where index.js was loaded. Serve it under a prefix that does not also serve static/, and the chunk requests 404 while index.js itself loaded fine \u2014 which reads as a partial success and is easy to misdiagnose. Keep static/ a child of your mount prefix, exactly as the executors do.

"},{"location":"learn/creating-backends/#driving-a-layout","title":"Driving a layout","text":"

This is the part that is genuinely yours. Everything else is file serving.

The two primitives are Layout (core/layout.py) and serve_layout (core/serve.py). The latter is about twenty lines: it opens the layout, then runs one loop sending await layout.render() out and one loop calling await layout.deliver(event) on each received message \u2014 with the deliver not awaited to completion, so a slow handler never blocks the next event.

You supply send and recv coroutines:

async def send_model(message):\n    await send({\"type\": \"websocket.send\", \"text\": orjson.dumps(message).decode()})\n\n\nasync def recv():\n    return await queue.get()\n

Four things go wrong here, and all four are worth knowing before you hit them.

"},{"location":"learn/creating-backends/#1-the-first-receive-is-websocketconnect","title":"1. The first receive() is websocket.connect","text":"

await send({\"type\": \"websocket.accept\"}) completes the handshake, but the websocket.connect event is still sitting in the incoming stream. A pump loop that treats every event as a message will orjson.loads() a dict with no text key and die, typically with TypeError or KeyError: 'text', right at connection time. Skip it:

async def pump():\n    while True:\n        event = await receive()\n        if event[\"type\"] == \"websocket.disconnect\":\n            return\n        if event[\"type\"] == \"websocket.connect\":\n            continue\n        await queue.put(orjson.loads(event[\"text\"]))\n

This is the single most common way a hand-written backend fails, because everything looks right until the client connects: the page serves, the bundle loads, and the socket opens and closes immediately. The console shows [ReactPy] Disconnected! followed by an endless reconnect loop.

"},{"location":"learn/creating-backends/#2-recv-must-not-return-none","title":"2. recv() must not return None","text":"

If your recv returns None \u2014 which is what an unguarded await receive() does on disconnect \u2014 serve_layout calls layout.deliver(None) and raises inside a task group, surfacing as an ExceptionGroup that names neither your code nor the cause. Return a valid message or raise; never None.

"},{"location":"learn/creating-backends/#3-responsewebsocket-has-no-send_text-and-receive-needs-rawtrue","title":"3. ResponseWebSocket has no send_text, and receive() needs raw=True","text":"

asgi_tools.ResponseWebSocket is what the built-in executors use \u2014 ReactPyWebsocket subclasses it \u2014 and it works fine as a backend's socket. Two API details differ from raw ASGI, and getting either wrong fails inside an anyio task group with an ExceptionGroup that names neither your code nor the cause:

async with ResponseWebSocket(scope=scope, receive=receive, send=send) as ws:\n\n    async def pump():\n        while True:\n            event = await ws.receive(raw=True)  # the ASGI dict, not the payload\n            if event[\"type\"] == \"websocket.disconnect\":\n                return\n            if event[\"type\"] != \"websocket.receive\":\n                continue\n            await queue.put(orjson.loads(event[\"text\"]))\n\n    async def send_model(message):\n        await ws.send(orjson.dumps(message).decode())  # not send_text(...)\n
  • There is no send_text. On asgi_tools.response.ResponseWebSocket the methods are send(msg) \u2014 which wraps a str as {\"type\": \"websocket.send\", \"text\": ...} and bytes as \"bytes\": ... \u2014 plus send_json(data). Send text frames. The stock send_json emits {\"type\": \"websocket.send\", \"bytes\": json_dumps(data)}, i.e. a binary frame, and that is precisely why the executors do not use it: executors/asgi/middleware.py, ReactPyWebsocket.send_json overrides it to self._send({\"type\": \"websocket.send\", \"text\": orjson.dumps(data).decode()}). The client never sets socket.binaryType (static/index.js), so a binary frame arrives as a Blob, and JSON.parse(blob) throws SyntaxError: ... \"[object Blob]\" is not valid JSON \u2014 verified in headless Chrome. Reusing a stock asgi_tools websocket object instead of overriding send_json therefore produces a socket that connects, accepts, and then parses nothing.
  • receive() returns the payload by default, not the ASGI event. Without raw=True you get a str, and event[\"type\"] then raises TypeError: string indices must be integers. Pass raw=True.
  • Under async with, the handshake is already done \u2014 do not send websocket.accept yourself. That is why the websocket.connect skip above tests for != \"websocket.receive\" rather than naming connect.

Raw send/receive from the ASGI callable, as the main example does, is the interface guaranteed to be present on any host; that is the reason it is the default here.

"},{"location":"learn/creating-backends/#4-one-layout-per-connection-and-never-share-one","title":"4. One layout per connection, and never share one","text":"

A Layout owns the hook state for a single client. Creating it before the request, or caching it by component name, means every browser tab shares one set of use_state values \u2014 one user's click changes another's screen. Build the Layout inside the connection handler, as above, and let it be garbage-collected with the socket.

The mirror-image bug is at the component level: a module-level element rendered by every layout is shared too, and mutation by one client leaks to all of them. That is covered in VDOM Mutations.

"},{"location":"learn/creating-backends/#connection-and-carrier","title":"Connection and carrier","text":"

Wrap the component in ConnectionContext so the connection hooks work:

from reactpy.core.hooks import ConnectionContext\nfrom reactpy.types import Connection, Location\n\nconnection = Connection(\n    scope=scope,\n    location=Location(path=\"/\", query_string=\"\"),\n    carrier=None,\n)\nlayout = Layout(ConnectionContext(my_component(), value=connection))\n

Without that wrapper, use_connection(), use_location(), and use_scope() all raise RuntimeError: No backend established a connection. \u2014 even inside a perfectly healthy socket.

Connection is a three-field dataclass, and as the backend author you decide what goes in it:

  • scope \u2014 pass the websocket scope through verbatim. use_scope() returns it, and users expect headers and query strings to be there.
  • location \u2014 the stock ASGI executors do not use scope[\"path\"]. The browser reports where it is, and the executor parses it out of the websocket query string: ?path=%2Fdocs%2F&qs=a%3D1 becomes Location(path=\"/docs/\", query_string=\"a=1\"). If you hard-code path=\"/\", every use_location() on your app reports the root regardless of the URL in the address bar. Parse it the same way if your app routes on it.
  • carrier \u2014 whatever mediates the connection. Nothing in ReactPy reads it; the executors put their websocket object there so your components can reach it. Put your own in, and document what it is, because User Connection tells people to check its type rather than assume. None is fine if nothing needs it.

Connection is a read-only view by design

There is no deliver, no send, no close on it. Components talk to the client through state, effects, and event handlers \u2014 not by pushing messages. Your backend is the only code allowed to touch the socket, precisely because letting a component write to the wire bypasses the protocol and desynchronizes the client. If you find yourself wanting a push channel, read Communicating Data Between Server and Client first.

"},{"location":"learn/creating-backends/#when-to-write-one","title":"When to write one","text":"

Almost never, and it is worth naming the honest cases:

  • Embedding ReactPy in an ASGI app you cannot wrap. ReactPyMiddleware already covers this \u2014 it takes your existing app as its first argument. Reach for a custom backend only when the middleware's routing gets in your way, since it owns {path_prefix}* and nothing else.
  • A non-ASGI host. ReactPy's core is host-agnostic: Layout and serve_layout need only two coroutines, so a socket server on another loop is a routing problem, not a rewrite. This is the case the design genuinely supports.
  • Owning the document. If you generate HTML yourself and want ReactPy components inside it, you need the mount markup and the import map. You usually want server_side_component_html for that rather than a new backend \u2014 see Serving a component from your own route.
  • Understanding it. Writing the backend once is the clearest way to learn where the boundary between layout and transport is. It is a good exercise even when the result is deleted.

What it does not buy you: performance. serve_layout in your own code is the same loop. And you take on the pieces the executors handle quietly \u2014 config validation, the import-map injection, pyscript_setup, static caching, reconnect options, and the error paths in the four sections above.

"},{"location":"learn/creating-backends/#summary","title":"Summary","text":"
  • A backend serves the bundle, serves web modules, returns a mount document, and runs one Layout per websocket.
  • The message shapes are fixed: layout-update and event-proxy-request down, layout-event and event-proxy-response up.
  • Feed every received frame to layout.deliver and serve_layout routes event-proxy responses for you; filter frames yourself and you must route them yourself.
  • Skip the websocket.connect event in your pump; it is the most common cause of an instantly-dying socket.
  • Speak raw ASGI on the socket; framework websocket wrappers do not have the methods serve_layout needs.
  • Build the Layout per connection, and wrap the component in ConnectionContext or the connection hooks raise.
  • You populate Connection; the stock executors parse location from the query string, not the scope path.
  • Components get a read-only view of the connection by design \u2014 that is the protocol's integrity, not an omission.
"},{"location":"learn/creating-backends/#related","title":"Related","text":"
  • Protocol Structure \u2014 all four message types, target, seq, and the event proxy round trip
  • Client API \u2014 what mountReactPy expects from your URL layout
  • ReactPy and ReactPyMiddleware \u2014 the two built-in backends
  • User Connection, User Scope, User Location \u2014 what your Connection feeds
  • Communicating Data Between Server and Client \u2014 the supported alternatives to pushing
"},{"location":"learn/creating-html-tags/","title":"Creating HTML Tags","text":""},{"location":"learn/creating-html-tags/#overview","title":"Overview","text":"

Every element you write in ReactPy is a function call that returns a dictionary. html.div(...) is not a keyword, a macro, or a parser \u2014 it is an attribute lookup that manufactures a constructor on first use, and a __call__ that assembles the model. Knowing how that works explains why there is no allow-list to fight, why html.del_ means <del>, and where the handful of hard errors come from.

You will learn

  • How html turns an attribute name into a tag name
  • The rule that decides whether your first argument is attributes or a child
  • Which elements reject children, and why html.script is stricter still
  • How to build fragments, SVG, and tag names that are not valid Python identifiers
"},{"location":"learn/creating-html-tags/#html-manufactures-tags-on-demand","title":"html manufactures tags on demand","text":"

html is an instance of HtmlConstructor with a single __getattr__ (_html.py). Ask it for anything and you get a Vdom constructor back, cached by name:

app.py Run
# The tag name is whatever you ask for -- there is no allow-list to consult.\nprint(html.div({\"id\": \"greeting\"}, \"Hello\"))\n# {'tagName': 'div', 'attributes': {'id': 'greeting'}, 'children': ['Hello']}\n\nprint(html.definitely_not_a_real_tag(\"still renders\"))\n# {'tagName': 'definitely-not-a-real-tag', 'children': ['still renders']}\n
# TODO\n

There is no allow-list. ReactPy does not know or care whether <definitely-not-a-real-tag> exists; the string travels to the browser and the browser decides. The names spelled out on HtmlConstructor exist only so your editor can autocomplete them \u2014 the comment in the source says as much. Two practical consequences:

  • A misspelled tag renders as an unknown element with your text inside it, silently. Nothing in ReactPy will tell you that html.hdeader is wrong.
  • Custom elements work with no ceremony: html.my_widget(...) produces {\"tagName\": \"my-widget\"} and the browser hands it to whatever registered that name.
"},{"location":"learn/creating-html-tags/#the-underscore-rules","title":"The underscore rules","text":"

__getattr__ applies exactly two transformations \u2014 value.rstrip(\"_\").replace(\"_\", \"-\") \u2014 which is how you write names Python will not let you spell:

app.py Run
# Underscores become dashes, and a trailing underscore is stripped -- the name\n# is translated, not looked up.\nprint(html.del_(\"removed\")[\"tagName\"])  # del\nprint(html.data_table()[\"tagName\"])  # data-table\nprint(html.my_widget()[\"tagName\"])  # my-widget\n\n# The same constructor comes back every time, so identity checks are cheap.\nprint(html.my_widget is html.my_widget)  # True\n
# TODO\n

Read the rules literally, because they are applied blindly:

You write Tag name Rule html.del_ del trailing _ stripped (a keyword) html.data_table data-table every _ becomes - html.my_widget my-widget same html.h1 h1 no underscore, nothing changes html._private -private leading underscore is not special html.a__b a--b doubles are not collapsed

Casing is passed through untouched, so html.myWidget gives you myWidget. Because translation happens before the cache lookup, html.data_table is html.data_table holds \u2014 but two spellings that translate to the same tag are the same object, and two that do not are different elements.

If a name cannot be written as an attribute at all \u2014 a leading digit, or one you are building from a variable \u2014 construct it directly:

from reactpy import Vdom\n\ntag = Vdom(\"2fa-code\")\ntag(\"123456\")  # {'tagName': '2fa-code', 'children': ['123456']}\n

Vdom(tag_name, /, allow_children=True, custom_constructor=None, import_source=None) is the same class every html.* name is built from, so you get identical behaviour. It is exported from the package root. Reach for it only for generated or unspellable names; anything else should go through html so it reads consistently.

html is not a namespace of plain functions

html.div is a Vdom object, not a function \u2014 html.div(\"x\") works because Vdom.__call__ is the constructor. So html.div.__name__ is the tag name rather than a qualified name, and any name you ask for is fair game: html.allow_children(\"x\") returns a perfectly valid {\"tagName\": \"allow-children\"} element rather than tripping over the Vdom attribute of the same name.

"},{"location":"learn/creating-html-tags/#the-two-call-shapes","title":"The two call shapes","text":"

Vdom.__call__ accepts either children only, or an attribute dictionary followed by children (core/vdom.py, Vdom.__call__):

app.py Run
# Two call shapes: children only, or an attribute dict first.\nprint(html.button(\"Click Me\"))\n# {'tagName': 'button', 'children': ['Click Me']}\n\nprint(html.button({\"id\": \"save\", \"disabled\": True}, \"Save\"))\n# {'tagName': 'button', 'attributes': {'id': 'save', 'disabled': True}, 'children': ['Save']}\n\n# Only an *exact* dict counts as attributes -- a dict subclass is a child.\nprint(html.div(VdomDict(tagName=\"span\")))\n# {'tagName': 'div', 'children': [{'tagName': 'span'}]}\n
# TODO\n

The discriminator is exact-type, not isinstance: type(values[0]) is dict (core/vdom.py, separate_attributes_and_children). Two consequences that bite:

  • A VdomDict \u2014 which is a dict subclass \u2014 in first position is treated as a child, which is what you want when nesting models you built earlier.
  • Any other dict subclass in first position is also a child, so a custom mapping you meant as attributes becomes a rendered object instead. Pass a plain dict literal for attributes.

A dict in any later position is always a child. There is one slot for attributes and it is the first.

"},{"location":"learn/creating-html-tags/#attribute-values-are-forwarded-verbatim","title":"Attribute values are forwarded verbatim","text":"

ReactPy renames nothing. className, class, htmlFor, and for all reach the DOM as whichever string you typed, and unknown names like data-* and aria-* pass straight through. Nothing validates names against a list, so a typo is a silent no-op \u2014 {\"class_name\": \"card\"} renders <div class_name=\"card\">, an attribute nobody reads.

There are no exceptions to this rule on the live path. class_name is not a thing. See Common Props for the full picture, including the one case where numeric style values behave differently between executors.

Attributes must be JSON-serializable, but you may not be told

Everything in the attribute dict is JSON-encoded on its way to the browser. ReactPy checks that with json.dumps only when REACTPY_CHECK_JSON_ATTRS is on \u2014 and that option's parent is REACTPY_DEBUG, so turning debug on turns this on too:

from reactpy import config\n\nconfig.REACTPY_DEBUG.current = True\n

With the check on, html.div({\"data\": object()}) raises TypeError: Object of type object is not JSON serializable at the tag call, pointing at your line. With it off, the model is accepted and the failure surfaces much later, inside the websocket send, as an orjson error in a server log. If a page renders nothing and the browser console is empty, this is why.

"},{"location":"learn/creating-html-tags/#fragments","title":"Fragments","text":"

html.fragment(...) and the shorter html(...) build a child list with no element of its own \u2014 tagName is the empty string, and the client renders it as nothing:

app.py Run
# Fragments carry no tag of their own: tagName is the empty string.\nprint(html.fragment(html.b(\"bold\"), html.i(\"italic\")))\n# {'tagName': '', 'children': [{'tagName': 'b', ...}, {'tagName': 'i', ...}]}\n\n# html(...) is html itself: HtmlConstructor.__call__ is the fragment call.\nprint(html(html.b(\"bold\"))[\"tagName\"])  # ''\nprint(html()[\"tagName\"])  # '' -- an empty fragment\n\n# A fragment may carry a key -- and nothing else.\nprint(html.fragment({\"key\": \"row-1\"}, html.b(\"x\"))[\"attributes\"])  # {'key': 'row-1'}\n\ntry:\n    html.fragment({\"className\": \"wrap\"}, html.b(\"x\"))\nexcept TypeError as error:\n    print(error)  # Fragments cannot have attributes besides 'key'\n
# TODO\n

A fragment accepts key and nothing else: a className, a style, or an event handler all raise TypeError: Fragments cannot have attributes besides 'key'. There is no node to put them on, so ReactPy refuses rather than dropping them quietly.

Use a fragment when a component must return siblings and a wrapper <div> would break the layout or the CSS. When the children are already in a list, pass the list \u2014 you do not need a fragment for that, though you do need keys, covered in Rendering Lists.

"},{"location":"learn/creating-html-tags/#void-elements","title":"Void elements","text":"

html.br(\"x\") is a hard error, not a warning that scrolls past:

app.py Run
# Void elements reject children -- loudly, at the call site.\ntry:\n    html.br(\"oops\")\nexcept TypeError as error:\n    print(error)  # 'br' nodes cannot have children.\n\nprint(html.br())  # {'tagName': 'br'}\n\n# script is stricter still: one string child, no handlers, an inferred key.\nprint(html.script(\"var answered = 42;\"))\n# {'tagName': 'script', 'children': ['var answered = 42;'],\n#  'attributes': {'key': 'var answered = 42;'}}\n\nprint(html.script({\"src\": \"/chart.js\"}))\n# {'tagName': 'script', 'attributes': {'src': '/chart.js', 'key': '/chart.js'}}\n\n\nasync def handle_load(event):\n    pass\n\n\ntry:\n    html.script({\"onLoad\": handle_load}, \"var x = 1;\")\nexcept ValueError as error:\n    print(error)  # 'script' elements do not support event handlers\n
# TODO\n

The rejecting set is NO_CHILDREN_ALLOWED_HTML_BODY (_html.py): area, base, br, col, command, embed, hr, img, input, iframe, keygen, link, meta, param, portal, source, track, wbr. The check happens after the model is built, so the message names the tag rather than your mistake \u2014 read the name in the error.

iframe is on that list, which surprises people who put fallback content inside one. ReactPy will not let you, and the browser-side renderer would not have shown it anyway.

"},{"location":"learn/creating-html-tags/#htmlscript-is-its-own-thing","title":"html.script is its own thing","text":"

html.script uses a custom constructor (_html.py, _script) with three rules and a side effect:

  • At most one child, and it must be a str \u2014 ValueError: 'script' nodes may have, at most, one child. and The child of a 'script' must be a string.
  • No event handlers at all \u2014 ValueError: 'script' elements do not support event handlers. Attach behaviour to a sibling element instead, or use the inline-JavaScript handler form on a normal tag (Creating VDOM Event Handlers).
  • A key is injected from the script's content, or from its src if it has no content. Upstream documents the purpose as allowing the script \"to be run multiple times if its key changes\", browser permitting. Pass your own key to control re-execution yourself.

Scripts are an injection surface

The child string reaches the browser as JavaScript. Never interpolate untrusted text into it \u2014 ReactPy's own docstring on _script says so, and there is no escaping step to save you. Build the string from your own constants, or pass a src you control.

"},{"location":"learn/creating-html-tags/#svg-is-a-second-constructor","title":"SVG is a second constructor","text":"

html.svg is a separate SvgConstructor, so SVG element names live in their own namespace and cannot collide with HTML names. Children come off it:

app.py Run
# SVG lives on a second constructor, so html.svg.circle() does not collide\n# with a hypothetical html.circle().\nprint(\n    html.svg(\n        {\"width\": \"100\", \"height\": \"100\", \"viewBox\": \"0 0 100 100\"},\n        html.svg.circle({\"cx\": \"50\", \"cy\": \"50\", \"r\": \"40\", \"fill\": \"tomato\"}),\n    )\n)\n# {'tagName': 'svg', 'attributes': {...},\n#  'children': [{'tagName': 'circle', 'attributes': {...}}]}\n
# TODO\n

It has its own void set (NO_CHILDREN_ALLOWED_SVG), and that set is broader and less intuitive than the HTML one \u2014 it includes text, tspan, title, desc, path, use, and foreignObject. So the element you most need children inside is exactly the one that rejects them:

html.svg.text(\"hello\")  # TypeError: 'text' nodes cannot have children.\n

Build those nodes with Vdom, which shares all the behaviour minus the void check:

from reactpy import Vdom\n\nVdom(\"text\")(\"hello\")  # {'tagName': 'text', 'children': ['hello']}\n

The bundled client creates every element through document.createElementNS(namespace, ...), picking the namespace from the tag name: \"svg\" gets http://www.w3.org/2000/svg, \"math\" gets the MathML URI, everything else the XHTML one. That logic lives in the preact runtime ReactPy bundles into its client chunks, not in ReactPy's own @reactpy/client source \u2014 so grep there for createElementNS and you will find nothing. Inside the SVG namespace attribute names are forwarded verbatim rather than lower-cased, so viewBox keeps its capital B \u2014 write it that way, since viewbox is a different attribute to the DOM.

"},{"location":"learn/creating-html-tags/#choosing-children-over-attributes","title":"Choosing children over attributes","text":"

html.div({\"children\": [\"a\", \"b\"]}) does not do what it looks like. The dict is attributes, so you get an attribute literally named children and no children at all. Children are always positional arguments after the attribute dict \u2014 that is the only way to supply them:

html.div({\"id\": \"list\"}, html.li(\"one\"), html.li(\"two\"))   # correct\nhtml.ul({\"children\": [html.li(\"one\")]})                     # an attribute named children\n

Children are flattened fully, not one level at a time, for any iterable that is not a string or a mapping \u2014 core/vdom.py, _flatten_children recurses, and verified html.div([[[\"a\"], \"b\"]]) yields children: ['a', 'b']. A list or tuple comprehension therefore works at any nesting depth, and None is skipped by the layout. Two rules from there are worth internalizing now, because both failures are quiet:

  • Elements inside a list need keys, in debug mode, or you get Key not specified for child in list ....
  • A generator in place of a list skips that check entirely, with Did not verify key-path integrity of children in generator .... Pass a list. The deeper hazard \u2014 storing an iterator in state \u2014 is covered in Rendering Lists.
"},{"location":"learn/creating-html-tags/#summary","title":"Summary","text":"
  • html invents a tag for any attribute name; the two rules are trailing _ stripped and every _ turned into -. There is no allow-list and no warning for a typo.
  • The first argument is attributes only if it is exactly a dict; everything else, including VdomDict and dict subclasses, is a child.
  • Attribute names are forwarded verbatim \u2014 use the DOM's spelling. Turn on REACTPY_DEBUG to catch un-serializable attribute values at the call site.
  • html.fragment and html() take only a key.
  • The void elements in NO_CHILDREN_ALLOWED_HTML_BODY reject children with a TypeError; html.script additionally allows one string child, forbids handlers, and infers its own key.
  • html.svg.* uses a broader void set that includes text; build those with Vdom.
  • Vdom(tag_name) is the class behind every html.* name \u2014 use it for tag names you cannot spell as an attribute, and for the SVG nodes html.svg rejects.
"},{"location":"learn/creating-html-tags/#related","title":"Related","text":"
  • Common Props \u2014 attribute naming, style, and the executor differences
  • Creating VDOM Event Handlers \u2014 the eventHandlers half of the model
  • VDOM Mutations \u2014 editing a model after you built it
  • Conditional Rendering and Rendering Lists \u2014 the child rules with their failure modes
  • Convert Between VDOM and HTML \u2014 the same models as text, and what that loses
"},{"location":"learn/creating-vdom-event-handlers/","title":"Creating VDOM Event Handlers","text":""},{"location":"learn/creating-vdom-event-handlers/#overview","title":"Overview","text":"

An event handler in ReactPy is a coroutine you put in an element's attribute dict. ReactPy inspects the value, not the name, decides what it is, and files it under one of three keys in the model \u2014 refusing it outright if it cannot be awaited. Most of the surprises on this page come from that one decision point: what counts as a handler, what counts as JavaScript, and what happens to the options you thought you set.

You will learn

  • The three buckets an attribute value lands in, and the rule that sorts them
  • Why a handler must be async def, and what that rules out
  • Why preventDefault() sometimes reaches the browser and sometimes does not
  • The difference in arguments between a plain callable and an EventHandler
  • How to set debounce and throttle, and what values are rejected
"},{"location":"learn/creating-vdom-event-handlers/#the-three-buckets","title":"The three buckets","text":"

When you call html.button({...}, ...), ReactPy walks the attribute dict once and routes every value by kind \u2014 callable first, then string-under-onXxx, then everything else (core/vdom.py, separate_attributes_handlers_and_inline_javascript):

app.py Run
# ReactPy sorts each value in the attribute dict into one of three buckets\n# by *kind*, not by name (core/vdom.py, separate_attributes_handlers_and_inline_javascript).\nasync def save(event):\n    print(\"saved\", await event.target.value)\n\n\nelement = html.button(\n    {\n        \"id\": \"save\",  # str, not onXxx -> attribute\n        \"onClick\": save,  # callable -> event handler\n        \"onHover\": \"console.log('hover')\",  # str under onXxx -> inline JS\n        \"data-row\": \"3\",  # str, not onXxx -> attribute\n    },\n    \"Save\",\n)\n\nprint(sorted(element))\n# ['attributes', 'children', 'eventHandlers', 'inlineJavaScript', 'tagName']\nprint(sorted(element[\"attributes\"]))  # ['data-row', 'id']\nprint(sorted(element[\"eventHandlers\"]))  # ['onClick']\nprint(sorted(element[\"inlineJavaScript\"]))  # ['onHover']\n
# TODO\n

The rules, exactly:

Value Lands in Note Anything callable eventHandlers under any key name; anything callable which is not a coroutine raises A BaseEventHandler eventHandlers stored as-is, untouched A str under a key matching ^on[A-Z]\\w+ inlineJavaScript eval'd in the page A reactpy.types.InlineJavaScript string inlineJavaScript under any key Everything else attributes forwarded verbatim

Three of those rows have consequences worth spelling out.

callable wins over the key name. {\"notAnHandler\": my_async_function} registers a real listener whose target ends in :notAnHandler. It will never fire, because no DOM event is named that, but it is registered. Conversely {\"onClick\": True} is not a handler \u2014 a bool is not callable, so it becomes an attribute and no listener exists. Neither mistake produces an error. What does raise is a callable ReactPy cannot await: the sort step builds an EventHandler for every callable value, and that is where the coroutine check lives.

Calling instead of passing gives you something that is not a handler. A handler is a coroutine, so {\"onClick\": handle_click()} hands over a coroutine object, which is not callable and so falls through to the attributes: the button renders, looks dead, and the failure surfaces later as TypeError: Type is not JSON serializable: coroutine when the model is encoded for the wire \u2014 plus a RuntimeWarning: coroutine ... was never awaited in the log. Calling something that was never a handler ({\"onClick\": print(\"hi\")}) is the None case, and that one really is silent on both sides. Either way: pass the function, do not call it. The checklist in What to check when a control does nothing covers all three shapes.

A dict is never a handler. The React-style {\"function\": fn, \"preventDefault\": True} becomes an ordinary attribute; no listener is attached. With REACTPY_DEBUG on it fails loudly instead \u2014 TypeError: Object of type function is not JSON serializable \u2014 which is the better outcome. Build handlers with @event or EventHandler, never with a dict.

"},{"location":"learn/creating-vdom-event-handlers/#plain-coroutines-the-shape-you-want-most-of-the-time","title":"Plain coroutines, the shape you want most of the time","text":"

Pass a coroutine and ReactPy wraps it. The browser sends a list of arguments; a plain coroutine receives it unpacked positionally (core/events.py, to_event_handler_function), which for a DOM event means one argument:

async def handle_click(event):\n    print(await event.target.value)\n\n\nhtml.button({\"onClick\": handle_click}, \"Click me\")\n

The parameter name is yours to choose \u2014 the value is positional. What is not optional is async def: EventHandler.__init__ and to_event_handler_function both run the value through check_is_async_handler, and anything that is not awaitable raises there. A coroutine function, a callable object whose __call__ is a coroutine, and a functools.partial or functools.wraps wrapper around either all pass. A plain def and a lambda do not \u2014 the latter has no syntax for await, which is why the old inline-handler idiom is gone.

The value handed over is a reactpy.types.Event: not a dict, but a live proxy onto the browser object, where await event.target.value is the read and the property chain matches JavaScript's. See Responding to Events for the payload surface and Protocol Structure for what crosses the wire.

Declare exactly one parameter

The list is unpacked onto your signature, so a handler declaring two required parameters fails at the moment of the click with TypeError: ... missing 1 required positional argument, logged server-side only \u2014 the browser stays silent and the button just stops working. Extra context has to be bound in Python, and since a lambda is no longer available that means a named handler or a closure:

def delete_handler(item_id):\n    async def handle_click(event):\n        await delete(item_id)\n\n    return handle_click\n\n\nhtml.button({\"onClick\": delete_handler(item.id)}, \"Delete\")\n

functools.partial does the same job, with one catch: the client still hands over the event, so the underlying function must leave exactly one parameter free \u2014 partial(delete, item.id) over async def delete(item_id, event), not over async def delete(item_id). Binding the leading argument also moves the event off the first parameter, which is what the bytecode detector matches, so see Be discovered: bytecode inspection below: a preventDefault() in that handler is not detected, and @event(prevent_default=True) is the fix.

"},{"location":"learn/creating-vdom-event-handlers/#eventhandler-the-pre-built-form-and-its-different-argument","title":"EventHandler: the pre-built form, and its different argument","text":"

EventHandler is what the @event decorator returns, and you can construct it directly. Two things change when you do.

First, ReactPy stores an EventHandler as-is rather than wrapping it, so the same object can serve every element that uses it. It does not follow that they share a target. The target is derived per element: unless the handler carries an explicit target, core/layout.py builds target = f\"{key_path}:{event}\", and @event leaves target as None. Verified with one @event handler on two buttons: the targets came out /0/0:onClick and /0/1:onClick \u2014 one function object, two targets.

Do not try to force the sharing by setting target= yourself. Two elements registered under the same explicit target overwrite each other in the layout's handler table, and the unmount path then tries to del that one key twice \u2014 verified, the second deletion raises KeyError from Layout._unmount_model_states.

Second, and this is the trap: an EventHandler receives the list itself, not the unpacked arguments.

app.py Run
# A plain coroutine gets the browser's argument list UNPACKED.\nasync def plain_handler(event):\n    print(await event.target.value)  # the event itself\n\n\n# An EventHandler gets the LIST ITSELF: an EventArguments, a list subclass.\nasync def list_handler(data):\n    print(await data[0].target.value)  # index first, then read as usual\n\n\nhtml.button({\"onClick\": plain_handler}, \"a\")\nhtml.button({\"onClick\": EventHandler(list_handler, prevent_default=True)}, \"b\")\n\n# Hand the list to a body written for the event and the first read fails:\n# AttributeError: 'EventArguments' object has no attribute 'target'\n# Hand the event to a body written for the list and subscripting it fails:\n# TypeError: 'Event' object is not subscriptable\n
# TODO\n

The failure when you get it backwards differs by direction, and the layout swallows either one into a server-side Failed to execute event handler line \u2014 nothing appears in the browser console, so the UI merely stops responding. Hand the list to a handler written for the event and the first property read raises AttributeError: 'EventArguments' object has no attribute 'target' \u2014 the list is an EventArguments, a list subclass, not the event. Hand the unpacked event to a handler written for the list and you get TypeError: 'Event' object is not subscriptable. Prefer the plain-coroutine form unless you specifically need to hand-build a handler; @event gives you the same options with the friendlier argument shape.

"},{"location":"learn/creating-vdom-event-handlers/#setting-the-five-options","title":"Setting the five options","text":"

Five behaviours run in the browser before an event is sent. They are properties on the handler object, never attributes on the element:

Option Type Effect prevent_default bool calls preventDefault() in the page stop_propagation bool calls stopPropagation() in the page stop_immediate_propagation bool also calls stopImmediatePropagation(), blocking sibling listeners debounce int ms coalesces outgoing events on a trailing edge, so the server sees one event after the user pauses \u2014 applied only to <input>, <select>, and <textarea> throttle int ms rate-limits outgoing events on a leading edge, on any element

There are two ways to set them, and the difference matters.

"},{"location":"learn/creating-vdom-event-handlers/#say-it-event","title":"Say it: @event","text":"
from reactpy import event\n\n\n@event(\n    prevent_default=True,\n    stop_propagation=True,\n    stop_immediate_propagation=True,\n    debounce=250,\n    throttle=100,\n)\nasync def handle_key_down(event_data): ...\n\n\nhtml.input({\"onKeyDown\": handle_key_down})\n

This is explicit and it always works. Reach for it by default.

"},{"location":"learn/creating-vdom-event-handlers/#be-discovered-bytecode-inspection","title":"Be discovered: bytecode inspection","text":"

ReactPy also reads your function's bytecode looking for three specific statements, so you can write them inline instead (core/_event_inspect.py, inspect_event_handler):

app.py Run
# Same behaviour, discovered by reading your bytecode instead of a decorator.\nasync def submit(event):\n    event.preventDefault()\n    event.stopPropagation()\n    event.debounce = 250\n\n\nhandler = html.form({\"onSubmit\": submit}, \"x\")[\"eventHandlers\"][\"onSubmit\"]\nprint(handler.prevent_default)  # True\nprint(handler.stop_propagation)  # True\nprint(handler.debounce)  # 250\n
# TODO\n

This is a source-parsing hack, not a runtime mechanism, and it has limits you should know before relying on it. Detection requires all of the following:

  • The call is written literally in the handler's own body. event.preventDefault() inside a helper the handler calls is invisible to the inspector \u2014 it does not follow calls.
  • The first parameter is the event. The inspector reads co_varnames[0] and matches against it.
  • For debounce, the assigned value must be resolvable at inspect time: an integer literal, a compile-time-folded expression like 100 * 5, a numeric default on a parameter (async def h(event, ms=350)), or a numeric closure cell. It cannot resolve a parameter with no default, a module-level global, or an arbitrary expression \u2014 all three inspect to None. And it stops scanning at the first assignment it can settle \u2014 resolved or not \u2014 so with two assignments to event.debounce the result depends on which one the bytecode reaches first. Verified: if event: event.debounce = 200 / else: event.debounce = len(x) inspects to 200, while the same branches written in the other order inspect to None. Write one assignment and the question does not arise; with more than one, check the value you get.

The second rule produces the sharpest bug, so it deserves its own call-out:

Bound methods are not inspected correctly

For async def on_submit(self, event), the first parameter is self, so the inspector never matches the event variable and reports prevent_default=False. The handler is a coroutine, so it passes the check and still fires \u2014 but your event.preventDefault() call is silently discarded:

app.py Run
# A bound method is a fine handler -- it fires. What is lost is detection of\n# the event.preventDefault() inside it: the bytecode inspector reads the\n# *first* parameter as the event, and for a method that is `self`.\nclass SearchForm:\n    async def on_submit(self, event):\n        event.preventDefault()\n\n\nplain = html.form({\"onSubmit\": SearchForm().on_submit})[\"eventHandlers\"][\"onSubmit\"]\nprint(plain.prevent_default)  # False -- your call was never seen\n\n\n# Say it explicitly with @event and it works.\n@event(prevent_default=True)\nasync def submit(event_data):\n    print(\"submitted\")\n\n\nexplicit = html.form({\"onSubmit\": submit})[\"eventHandlers\"][\"onSubmit\"]\nprint(explicit.prevent_default)  # True\n
# TODO\n

Attach a plain function defined inside the component instead, or pass @event(prevent_default=True) explicitly over the method. A functools.wraps decorator is fine \u2014 the inspector follows __wrapped__ to the function you wrote, first parameter and all. A functools.partial is unwrappable too, but only half-fixed: once the leading argument is bound, the first parameter the inspector reads is yours, not the event's, so the same silent False appears. Same escape hatch: declare the option with @event.

An inline-JavaScript handler cannot be inspected at all

Detection runs on Python bytecode. {\"onClick\": \"this.innerText = 'x'\"} has none, so there is no preventDefault equivalent in the string form. Call it yourself in the JavaScript: \"event.preventDefault(); doThing()\".

"},{"location":"learn/creating-vdom-event-handlers/#inline-javascript","title":"Inline JavaScript","text":"

A str under an onXxx key is stored under inlineJavaScript and eval'd in the page; if it evaluates to a function, that function is called with the event:

html.button({\"onClick\": '(event) => (event.target.innerText = \"Thank you!\")'}, \"Click\")\nhtml.button({\"onClick\": \"this.innerText = 'Thank you!'\"}, \"Click\")\n

This is the way to touch the DOM from ReactPy, and the reason it exists is that the server has no node to hold (Manipulating the DOM with Refs). Keep each snippet to a single call, and never interpolate anything you did not write:

Trusted strings only

The string runs with your application's privileges, in the page. f\"this.innerText = '{user_input}'\" is an injection point. Build strings from constants, or use a Python handler and pass data through state.

Note also that the pattern is ^on[A-Z]\\w+ \u2014 a lowercase {\"onclick\": \"...\"} matches nothing and becomes a plain attribute that never fires.

"},{"location":"learn/creating-vdom-event-handlers/#merging-handlers","title":"Merging handlers","text":"

reactpy.core.events.merge_event_handlers combines several handlers into one that runs them concurrently in a task group. It is public API but nothing in the installed package calls it \u2014 verified by grepping the whole reactpy package, which finds only its own definition \u2014 so you will rarely have a reason to call it. Its failure mode is still worth knowing, because it is a ValueError at build time:

merge_event_handlers([a, b])\n# ValueError: Cannot merge handlers - 'stop_propagation', 'prevent_default',\n# 'debounce', 'throttle', 'target', or 'stop_immediate_propagation' mismatch.\n

Handlers merge only if every option matches. An empty list raises ValueError: No event handlers to merge.

"},{"location":"learn/creating-vdom-event-handlers/#what-to-check-when-a-control-does-nothing","title":"What to check when a control does nothing","text":"

Every failure mode on this page is silent in the browser, so work through them in this order:

  1. Is it declared async def? A def, a lambda, or a state setter raises while the component renders, and the traceback names the function.
  2. Did you pass the function or call it? A handler is a coroutine, so {\"onClick\": handler()} hands over a coroutine object \u2014 not callable, so it falls through to the attributes and dies when the model is encoded: TypeError: Type is not JSON serializable: coroutine from the send path. A handler which returns something instead (None from a helper) ships as an inert attribute and never fires. Either way: pass the function, do not call it.
  3. Is the key spelled the way the DOM spells its event, with a leading on and a capital letter? ReactPy passes the key straight through, so a on_click registers a listener for an event named _click, which nothing ever dispatches. Copy the DOM's camelCase spelling (onClick, onDoubleClick, onKeyDown) rather than guessing.
  4. Does the handler take exactly one parameter? Two required parameters means a server-side TypeError and a dead button.
  5. Is it a bound method? Then preventDefault/stopPropagation/debounce are not detected \u2014 use @event.
  6. Read the server log. Failed to execute event handler is where swallowed handler errors go; the browser console will show nothing.
"},{"location":"learn/creating-vdom-event-handlers/#summary","title":"Summary","text":"
  • Values are sorted by kind, not name: callable \u2192 eventHandlers (and raises there unless it can be awaited), string under onXxx \u2192 inlineJavaScript, everything else \u2192 attributes.
  • None, True, and dict handlers all fail silently as attributes.
  • A handler is a coroutine \u2014 a def or a lambda is refused with a TypeError when the element is built, not when it is clicked.
  • A plain coroutine receives the argument list unpacked (so, the event proxy); an EventHandler receives the list.
  • Set options with @event(prevent_default=..., stop_propagation=..., stop_immediate_propagation=..., debounce=..., throttle=...); the bytecode detector is a convenience with real limits, notably bound methods.
  • Both debounce and throttle rate-limit client\u2192server traffic: debounce coalesces outgoing events on a trailing edge and only on user-input elements, throttle fires on a leading edge on any element. The server's echo of a new value is reconciled by _reactpy_ack_seq, not by debounce. Invalid values are dropped with a console warning, not an error.
  • Handler errors surface in the server log only.
"},{"location":"learn/creating-vdom-event-handlers/#related","title":"Related","text":"
  • Responding to Events \u2014 the tutorial treatment, propagation, and the event object
  • Common Props \u2014 the same surface from the reference side, including ReactPy's own options
  • Protocol Structure \u2014 the wire format, target, and how data is built
  • Creating HTML Tags \u2014 how the model these handlers live in is built
  • Manipulating the DOM with Refs \u2014 the inline-JavaScript escape hatch
"},{"location":"learn/editor-setup/","title":"Editor Setup","text":""},{"location":"learn/editor-setup/#overview","title":"Overview","text":"

A properly configured editor can make code clearer to read and faster to write. It can even help you catch bugs as you write them! If this is your first time setting up an editor or you're looking to tune up your current editor, we have a few recommendations.

You will learn

  • What the most popular editors are
  • How to format your code automatically
"},{"location":"learn/editor-setup/#your-editor","title":"Your editor","text":"

VS Code is one of the most popular editors in use today. It has a large marketplace of extensions and integrates well with popular services like GitHub. Most of the features listed below can be added to VS Code as extensions as well, making it highly configurable!

Other popular text editors used in the React community include:

  • Sublime Text has support for syntax highlighting and autocomplete built in.
  • Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient. It is included as \"vi\" with most UNIX systems and with Apple OS X.
"},{"location":"learn/editor-setup/#recommended-text-editor-features","title":"Recommended text editor features","text":"

Some editors come with these features built in, but others might require adding an extension. Check to see what support your editor of choice provides to be sure!

"},{"location":"learn/editor-setup/#linting","title":"Linting","text":"

Linting is the process of running a program that will analyse code for potential errors. Ruff is Reactive Python's linter of choice due to its speed and configurability. Additionally, we recommend using Pyright as a performant type checker for Python.

Be sure you have Python installed before you proceed!

  • Install Ruff using the VSCode extension or the standalone package.
  • Install Pyright using the VSCode extension or the standalone package.
"},{"location":"learn/editor-setup/#formatting","title":"Formatting","text":"

The last thing you want to do when sharing your code with another contributor is get into an discussion about tabs vs spaces! Fortunately, Ruff will clean up your code by reformatting it to conform to preset, configurable rules. Run Ruff, and all your tabs will be converted to spaces\u2014and your indentation, quotes, etc will also all be changed to conform to the configuration. In the ideal setup, Ruff will run when you save your file, quickly making these edits for you.

You can install the Ruff extension in VSCode by following these steps:

  1. Launch VS Code
  2. Use Quick Open, press Ctrl+P
  3. Paste in ext install charliermarsh.ruff
  4. Press Enter

Formatting on save

Ideally, you should format your code on every save. VS Code has settings for this!

  1. In VS Code, press Ctrl+Shift+P
  2. Type \"workspace settings\"
  3. Hit Enter
  4. In the search bar, type \"Format on save\"
  5. Be sure the \"Format on save\" option is ticked!
"},{"location":"learn/extracting-state-logic-into-a-reducer/","title":"Extracting State Logic into a Reducer","text":""},{"location":"learn/extracting-state-logic-into-a-reducer/#overview","title":"Overview","text":"

A component whose state is updated by many handlers spread across its body gets hard to reason about. A reducer moves every update rule into one pure function, so the handlers say only what the user did. In ReactPy there is one rule to learn on top of React's: the reducer you pass on the first render is the one ReactPy keeps, so a reducer defined inside the component body closes over frozen values.

You will learn

  • What a reducer is, and how dispatching differs from setting
  • Why ReactPy keeps the first reducer, and what that forbids
  • Where a ReactPy reducer actually runs, and what that means for purity
  • When a reducer is worth the extra code
"},{"location":"learn/extracting-state-logic-into-a-reducer/#consolidate-state-logic-with-a-reducer","title":"Consolidate state logic with a reducer","text":"

This component holds a list of tasks and updates it from three handlers:

app.py Run
INITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n    {\"id\": 2, \"text\": \"Camera\", \"done\": False},\n]\n\n\n@component\ndef task_app():\n    next_id, set_next_id = use_state(3)\n    tasks, set_tasks = use_state(INITIAL_TASKS)\n\n    async def handle_add_task(event):\n        set_tasks([*tasks, {\"id\": next_id, \"text\": \"New task\", \"done\": False}])\n        set_next_id(next_id + 1)\n\n    def handle_toggle(task_id):\n        async def handle_click(event):\n            set_tasks(\n                [\n                    (\n                        {**task, \"done\": not task[\"done\"]}\n                        if task[\"id\"] == task_id\n                        else task\n                    )\n                    for task in tasks\n                ]\n            )\n\n        return handle_click\n\n    def handle_delete(task_id):\n        async def handle_click(event):\n            set_tasks([task for task in tasks if task[\"id\"] != task_id])\n\n        return handle_click\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add_task}, \"Add task\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": task[\"id\"]},\n                    html.span(\n                        {\n                            \"style\": {\n                                \"textDecoration\": (\n                                    \"line-through\" if task[\"done\"] else \"none\"\n                                )\n                            }\n                        },\n                        task[\"text\"],\n                    ),\n                    html.button({\"onClick\": handle_toggle(task[\"id\"])}, \"Toggle\"),\n                    html.button({\"onClick\": handle_delete(task[\"id\"])}, \"Delete\"),\n                )\n                for task in tasks\n            ]\n        ),\n        html.p({\"id\": \"summary\"}, f\"{len(tasks)} tasks, next id {next_id}\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Each handler knows how to rebuild the list. That is fine at three handlers and one field. It stops being fine when a fourth handler appears, when a second field has to change alongside tasks, or when you are debugging which of the four produced a wrong list \u2014 and here handle_add_task already has to keep two state variables in step.

Managing state with a reducer inverts it. Instead of telling ReactPy what to do, each handler says what just happened, and one function decides what that means:

app.py Run
INITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n    {\"id\": 2, \"text\": \"Camera\", \"done\": False},\n]\n\n\ndef tasks_reducer(state, action):\n    \"\"\"One place that knows how the task list may change.\"\"\"\n    kind = action[\"type\"]\n    if kind == \"added\":\n        task = {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        return {\n            **state,\n            \"tasks\": [*state[\"tasks\"], task],\n            \"next_id\": state[\"next_id\"] + 1,\n        }\n    if kind == \"toggled\":\n        return {\n            **state,\n            \"tasks\": [\n                (\n                    {**task, \"done\": not task[\"done\"]}\n                    if task[\"id\"] == action[\"id\"]\n                    else task\n                )\n                for task in state[\"tasks\"]\n            ],\n        }\n    if kind == \"deleted\":\n        return {\n            **state,\n            \"tasks\": [task for task in state[\"tasks\"] if task[\"id\"] != action[\"id\"]],\n        }\n    raise ValueError(f\"unknown action type {kind!r}\")\n\n\n@component\ndef task_app():\n    state, dispatch = use_reducer(tasks_reducer, {\"tasks\": INITIAL_TASKS, \"next_id\": 3})\n\n    async def handle_add_task(event):\n        dispatch({\"type\": \"added\"})\n\n    def handle_toggle(task_id):\n        async def handle_click(event):\n            dispatch({\"type\": \"toggled\", \"id\": task_id})\n\n        return handle_click\n\n    def handle_delete(task_id):\n        async def handle_click(event):\n            dispatch({\"type\": \"deleted\", \"id\": task_id})\n\n        return handle_click\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add_task}, \"Add task\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": task[\"id\"]},\n                    html.span(\n                        {\n                            \"style\": {\n                                \"textDecoration\": (\n                                    \"line-through\" if task[\"done\"] else \"none\"\n                                )\n                            }\n                        },\n                        task[\"text\"],\n                    ),\n                    html.button({\"onClick\": handle_toggle(task[\"id\"])}, \"Toggle\"),\n                    html.button({\"onClick\": handle_delete(task[\"id\"])}, \"Delete\"),\n                )\n                for task in state[\"tasks\"]\n            ]\n        ),\n        html.p(\n            {\"id\": \"summary\"},\n            f\"{len(state['tasks'])} tasks, next id {state['next_id']}\",\n        ),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Both examples are verified to behave identically \u2014 add twice, toggle, delete produces the same readouts and the same single flipped row in each. The difference is where the knowledge lives. The handlers now read as a list of user intentions:

  • dispatch({\"type\": \"added\"})
  • dispatch({\"type\": \"toggled\", \"id\": task_id})
  • dispatch({\"type\": \"deleted\", \"id\": task_id})

next_id moved into the same state object as tasks, so added bumps both in one return value. That is the first concrete gain: there is no longer a pair of setters that can be called in the wrong combination.

The reducer ends with raise ValueError(f\"unknown action type {kind!r}\") rather than return state. A fallthrough that silently does nothing is the hardest bug in this pattern to find, because every other symptom looks correct.

Note

use_reducer(reducer, initial_value) takes exactly two arguments. There is no third \"init function\" parameter as in some React versions. If building the initial state is expensive, pass a zero-argument callable as initial_value \u2014 ReactPy calls it once and stores the result, the same way use_state does.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#the-rule-reactpy-adds-the-first-reducer-wins","title":"The rule ReactPy adds: the first reducer wins","text":"

This is the part that differs from React, and it will bite you once. use_reducer builds its dispatcher once and reuses it, so the reducer function from the first render is the one that runs forever \u2014 core/hooks.py, use_reducer.

Swapping the reducer at runtime does nothing at all:

app.py Run
def add_by_one(state, action):\n    return {\"n\": state[\"n\"] + 1, \"by\": \"add_by_one\"}\n\n\ndef add_by_hundred(state, action):\n    return {\"n\": state[\"n\"] + 100, \"by\": \"add_by_hundred\"}\n\n\n@component\ndef counter():\n    version, set_version = use_state(1)\n\n    # The component body picks a different reducer each time version changes,\n    # but use_reducer built its dispatcher on the first render and reuses it.\n    reducer = add_by_one if version == 1 else add_by_hundred\n    state, dispatch = use_reducer(reducer, {\"n\": 0, \"by\": \"-\"})\n\n    async def handle_add(event):\n        dispatch({\"type\": \"add\"})\n\n    async def handle_switch(event):\n        set_version(2)\n\n    return html.div(\n        html.button({\"onClick\": handle_add}, \"Add\"),\n        html.button({\"onClick\": handle_switch}, \"Switch reducer\"),\n        html.p({\"id\": \"readout\"}, f\"n={state['n']} by={state['by']} version={version}\"),\n    )\n\n\n@component\ndef app():\n    return counter()\n
# TODO\n

Press Switch reducer \u2014 the readout shows version=2, so the component body really did pick add_by_hundred \u2014 then press Add: by=add_by_one. Every later action still runs the original function.

The same mechanism is subtler, and much more common, when the reducer is defined inside the component:

app.py Run
INITIAL = {\"count\": 0, \"total\": 0}\n\n\n@component\ndef cart(item_price):\n    # This reducer reads item_price from the enclosing scope. ReactPy keeps the\n    # reducer from the first render, so it keeps the first price too.\n    def reducer(state, action):\n        if action[\"type\"] == \"add\":\n            return {\n                \"count\": state[\"count\"] + 1,\n                \"total\": state[\"total\"] + item_price,\n            }\n        return state\n\n    state, dispatch = use_reducer(reducer, INITIAL)\n\n    async def handle_add(event):\n        dispatch({\"type\": \"add\"})\n\n    return html.div(\n        html.button({\"onClick\": handle_add}, \"Add\"),\n        html.p(\n            {\"id\": \"cart\"},\n            f\"count={state['count']} total={state['total']} price={item_price}\",\n        ),\n    )\n\n\n@component\ndef price_control():\n    price, set_price = use_state(10)\n\n    async def raise_price(event):\n        set_price(50)\n\n    return html.div(\n        html.button({\"onClick\": raise_price}, \"Raise price\"),\n        cart(price),\n        html.p(f\"parent price={price}\"),\n    )\n\n\n@component\ndef app():\n    return price_control()\n
# TODO\n

Press Add once: count=1 total=10. Then Raise price, which takes the displayed price to 50 \u2014 and press Add again. You get total=20, not 60. The line reads price=50 while the arithmetic used 10.

Nothing crashed and nothing was logged. The reducer still holds the closure from the first render, in which item_price was 10. This is the frozen-effect-body problem from Lifecycle of Reactive Effects wearing a different hat, and it is the single most likely way to be quietly wrong with a ReactPy reducer.

The fix is the same instinct as always: keep the reducer pure and self-contained. Define it at module level, and let the action carry every value it needs. Handlers are not frozen \u2014 they are rebuilt each render \u2014 so a handler can read the current prop and hand it over:

app.py Run
INITIAL = {\"count\": 0, \"total\": 0}\n\n\ndef cart_reducer(state, action):\n    \"\"\"Pure and self-contained: every value it needs arrives in the action.\"\"\"\n    if action[\"type\"] == \"add\":\n        return {\n            \"count\": state[\"count\"] + 1,\n            \"total\": state[\"total\"] + action[\"price\"],\n        }\n    return state\n\n\n@component\ndef cart(item_price):\n    # Defined at module level, so there is no enclosing scope to freeze.\n    state, dispatch = use_reducer(cart_reducer, INITIAL)\n\n    async def handle_add(event):\n        # The handler closes over the *current* price, and hands it over.\n        dispatch({\"type\": \"add\", \"price\": item_price})\n\n    return html.div(\n        html.button({\"onClick\": handle_add}, \"Add\"),\n        html.p(\n            {\"id\": \"cart\"},\n            f\"count={state['count']} total={state['total']} price={item_price}\",\n        ),\n    )\n\n\n@component\ndef price_control():\n    price, set_price = use_state(10)\n\n    async def raise_price(event):\n        set_price(50)\n\n    return html.div(\n        html.button({\"onClick\": raise_price}, \"Raise price\"),\n        cart(price),\n        html.p(f\"parent price={price}\"),\n    )\n\n\n@component\ndef app():\n    return price_control()\n
# TODO\n

Same buttons: Add, Raise price, Add now gives count=2 total=60 price=50. cart_reducer sits at module level with no enclosing scope to freeze, and dispatch({\"type\": \"add\", \"price\": item_price}) supplies the live value.

So the ReactPy rule is: a reducer must be a module-level function that reads nothing but its two arguments. That is stricter than React's \"reducers must be pure\", and it costs nothing \u2014 a reducer that closes over component scope is a reducer you cannot unit-test with two plain arguments anyway.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#where-a-reactpy-reducer-runs","title":"Where a ReactPy reducer runs","text":"

React's docs say reducers run during rendering, because React queues actions and calls the reducer at the next render. ReactPy is more direct, and the difference is worth knowing when you reach for a print statement:

handler:enter\nreducer(add)\nhandler:exit\nrender\n

Verified trace of one click. dispatch calls your reducer immediately, inside the handler, then compares the result against the current state with == and schedules a render only if it differs \u2014 core/hooks.py, _create_dispatcher.

Three consequences:

  • A reducer that raises surfaces as a failed event handler, not a reducer error. Verified: dispatching an action whose branch raises ValueError leaves the state untouched and produces zero renders. dispatch ran the reducer inside the handler, so the exception propagates to core/layout.py, Layout._process_event_queue, which catches it and calls logger.exception \u2014 one ERROR: Failed to execute event handler record on the reactpy.core.layout logger, with the reducer's traceback attached. ReactPy itself never reports \"a reducer raised\". The component stays mounted and keeps working for later actions.
  • Multiple dispatches in one handler batch into one render. Verified: three dispatch calls in a single handler produce three reducer calls, applied in order, and one render.
  • Purity is not optional, but for a different reason than in React. Since the result is compared with ==, mutating and returning the same object compares equal and schedules nothing.

That last one is the most common reducer bug in ReactPy, and it is silent:

app.py Run
INITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n]\n\n\ndef mutating_reducer(state, action):\n    \"\"\"Looks fine, edits the list in place, returns it. Nothing will re-render.\"\"\"\n    if action[\"type\"] == \"added\":\n        state[\"tasks\"].append(\n            {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        )\n        return state\n    return state\n\n\n@component\ndef task_app():\n    # A fresh list per component instance: this reducer mutates the list it is\n    # given, so it must not be handed a module-level one.\n    state, dispatch = use_reducer(\n        mutating_reducer, {\"tasks\": [*INITIAL_TASKS], \"next_id\": 2}\n    )\n\n    async def handle_add_task(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.button({\"onClick\": handle_add_task}, \"Add task\"),\n        html.ul(\n            [html.li({\"key\": task[\"id\"]}, task[\"text\"]) for task in state[\"tasks\"]]\n        ),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks rendered\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Press Add task three times. The rendered count stays at 2 tasks rendered and the component body never runs again \u2014 verified at zero renders. The list did grow; the returned state compared equal to the old one, so ReactPy decided nothing had changed. Return new objects ({**state, \"tasks\": [*state[\"tasks\"], task]}), exactly as you would with use_state.

Pitfall

return state after modifying state is a no-op, and so is any branch that falls through to return state. If an action's \"type\" matches no branch \u2014 a typo, a renamed constant \u2014 the dispatch is silently ignored. End the reducer with raise ValueError(...) for unknown types instead of a bare return state, so the mistake surfaces where it is made.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#comparing-use_state-and-use_reducer","title":"Comparing use_state and use_reducer","text":"
  • Code size. use_state needs less code upfront: a reducer and actions is two artefacts, not one. A reducer pays off when many handlers mutate state in similar shapes, or when one transition touches several fields at once \u2014 moving next_id next to tasks above is that case.
  • Readability. With use_state the update logic lives in the handlers, which is where you want it while it is three lines. When handlers grow conditionals, the reducer lets you separate how state changes from what happened.
  • Debugging. With use_state you must find which setter was wrong. With a reducer you can log the two arguments of one function and reconstruct the session \u2014 in ReactPy the log also tells you the order, because dispatch is eager. If each action is right, the bug is in the reducer.
  • Testing. A module-level reducer \u2014 which ReactPy already requires \u2014 is a plain function. tasks_reducer(state, {\"type\": \"deleted\", \"id\": 1}) is an assertion, no rendering involved.
  • Render count. Neither is cheaper. Both compare with == and both skip a render when the value is unchanged; neither bails out of rendering the component, because ReactPy has no memo.

Use a reducer when a component has produced real bugs from the wrong update path, not as a default. Mixing is fine: use_state for a draft input, use_reducer for the form's submitted record, in the same component.

Deep Dive

ReactPy has no immer equivalent that earns its keep here, because {**state, ...} and [*items, item] are already the copy idioms a reducer needs. What is worth doing once a reducer grows past a couple of branches: make the state a frozen @dataclass. Then a mutation raises FrozenInstanceError at the line that did it instead of quietly comparing equal \u2014 verified, and with it the two halves that make this safe: strictly_equal compares dataclass instances field by field, so dispatching an action that changes nothing renders nothing, while one that changes a field renders once. Because the raise happens inside the event handler, though, it is caught like any other handler exception: it appears as a root-logger ERROR, not on screen. Use dataclasses.replace(state, ...), and read the server log. See Lifecycle of Reactive Effects for the full comparison table.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#writing-reducers-well","title":"Writing reducers well","text":"
  • One action per user interaction, not per field. A \"Reset\" button on a five-field form should dispatch {\"type\": \"reset_form\"}, not five set_field actions. If you logged every action, the log should read like a transcript of the session.
  • Actions describe what happened, not what to change. {\"type\": \"toggled\", \"id\": 3} survives a change in how toggling is displayed. {\"type\": \"set_done\", \"value\": True} does not, and is usually shorter written as a setter.
  • Put the data in the action. {\"type\": \"add_item\", \"item\": item, \"price\": price} \u2014 this is also exactly what keeps a ReactPy reducer honest, since the reducer cannot read live values from a closure.
  • Keep transitions in the reducer, not in the handler. A handler that does dispatch(...) and set_something_else(...) has two sources of truth again.
  • Do not dispatch during render. Dispatch is eager, so it updates the pending state while the component body is still running; the render then reflects an update that has already happened. Put it in an effect or a handler.
"},{"location":"learn/extracting-state-logic-into-a-reducer/#recap","title":"Recap","text":"
  • A reducer collects every state transition in one pure function; handlers shrink to a description of what the user did.
  • ReactPy keeps the first reducer it is given. Verified: swapping the reducer argument changes the body, not the behaviour.
  • Therefore define reducers at module level, reading nothing but state and action. A reducer defined in the component body freezes its closure \u2014 verified charging total=20 at a displayed price=50.
  • dispatch runs the reducer immediately, inside the handler, not at the next render. Verified by trace.
  • Mutating the state and returning it compares equal, so nothing renders \u2014 verified: three presses, zero renders, no log line. Return new objects.
  • A raising reducer surfaces as ERROR: Failed to execute event handler from reactpy.core.layout, not as a reducer error; end the reducer with raise ValueError so unknown actions surface.
  • Several dispatches in one handler apply in order and cost one render (verified).
  • use_reducer takes two arguments; pass a callable as initial_value to build it lazily, once.
"},{"location":"learn/extracting-state-logic-into-a-reducer/#related","title":"Related","text":"
  • Choosing the State Structure \u2014 the == comparison and the copy idioms a reducer depends on
  • Queueing a Series of State Updates \u2014 functional updaters, the use_state sibling of dispatch
  • Lifecycle of Reactive Effects \u2014 frozen closures and strictly_equal, the two mechanisms behind the traps on this page
  • Passing Data Deeply with Context \u2014 how to hand dispatch to distant components
  • Use Reducer \u2014 parameter-level reference and troubleshooting
"},{"location":"learn/importing-and-exporting-components/","title":"Importing and Exporting Components","text":""},{"location":"learn/importing-and-exporting-components/#overview","title":"Overview","text":"

The magic of components lies in their reusability: you can create components that are composed of other components. But as you nest more and more components, it often makes sense to start splitting them into different files. This lets you keep your files easy to scan and reuse components in more places.

You will learn

  • What a root component module is
  • How to import a component from another file
  • How ReactPy expresses \"private\" and \"public\" components
  • How to import and export multiple components from one file
  • How to split components into multiple files
"},{"location":"learn/importing-and-exporting-components/#the-root-component-module","title":"The root component module","text":"

In Your First Component, you made a profile component and a gallery component that renders it:

app.pystyles.css
from reactpy import component, html\n\n\n@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n
img {\n    margin: 0 10px 10px 0;\n    height: 90px;\n}\n

These currently live in a single module. Whichever module holds the component you hand to an executor is your root component module \u2014 the ASGI executor in Creating a Standalone React App takes that component directly:

from reactpy.executors.asgi import ReactPy\n\nfrom app import gallery\n\nmy_app = ReactPy(gallery)\n

Which module plays this role depends on your executor. The middleware executor takes dotted paths instead, e.g. [\"my_components.hello_world\"], and the CSR executor takes a path to a file whose root component it mounts.

"},{"location":"learn/importing-and-exporting-components/#exporting-and-importing-a-component","title":"Exporting and importing a component","text":"

What if you want to change the landing screen in the future and put a list of science books there? Or place all the profiles somewhere else? It makes sense to move gallery and profile into their own module. This makes them more modular and reusable in other files. You can move a component in three steps:

  1. Make a new Python file to put the components in.
  2. Define your component functions in that file. Unlike JavaScript, Python has no export keyword \u2014 every name defined at module level is importable.
  3. Import it in the module where you'll use the component.

Here both profile and gallery have moved into a new file called gallery.py. Now app.py imports gallery from it:

app.pygallery.py
from gallery import gallery\n\nfrom reactpy import component\n\n\n@component\ndef app():\n    return gallery()\n
from reactpy import component, html\n\n__all__ = [\"gallery\"]\n\n\n@component\ndef _profile():\n    \"\"\"Used only inside this module, so it stays out of ``__all__``.\"\"\"\n    return html.img({\"src\": \"https://i.imgur.com/QIrZWGIs.jpg\", \"alt\": \"Alan L. Hart\"})\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        _profile(),\n        _profile(),\n        _profile(),\n    )\n

Notice how this example is broken down into two component files now:

  1. gallery.py:
    • Defines the _profile component, which is only used within the same file and is kept private.
    • Makes gallery the public component of the module.
  2. app.py:
    • Imports gallery from gallery.py.
    • Defines the root app component.

Note

ReactPy does not care which module a component is defined in, so unlike ReactJS there is no distinction between \"default\" and \"named\" exports. from gallery import gallery, profile and two separate import statements mean the same thing.

"},{"location":"learn/importing-and-exporting-components/#public-and-private-components","title":"Public and private components","text":"

ReactJS lets you keep a component unexported so that only its own file can use it. Python has no equivalent enforcement, but it has two widely respected conventions that express the same intent.

A leading underscore marks a name as private to its module:

@component\ndef _profile():\n    ...\n

And __all__ lists the names a module publishes. It is what a star import exports, and what linters and type checkers read to decide what is public API:

__all__ = [\"gallery\"]\n

This is why _profile is both underscore-prefixed and absent from __all__ in the example above.

Pitfall

These conventions are advisory, not enforced. from gallery import _profile still works, because Python has no private keyword. Do not reach for underscore-prefixed names from another module, and do not rely on them being unavailable \u2014 treat a broken convention as a bug in the importing code, not as protection.

Deep Dive

"},{"location":"learn/importing-and-exporting-components/#naming-modules-and-components","title":"Naming modules and components","text":"

Because imports name modules rather than files, give both meaningful names. scientists.py defining gallery and _profile reads better than utils.py defining Gallery, and it also keeps you consistent with the rest of the docs, which write components in snake_case.

Components should still be called everywhere they are used, so a component used by two modules must be imported in both of them. There is no global component registry to fall back on.

"},{"location":"learn/importing-and-exporting-components/#exporting-and-importing-multiple-components-from-the-same-file","title":"Exporting and importing multiple components from the same file","text":"

What if you want to show just one profile instead of a gallery? Then app.py needs profile too. Add it to the module's __all__ and import it by name:

app.pygallery.py
from gallery import gallery, profile\n\nfrom reactpy import component, html\n\n\n@component\ndef app():\n    return html.div(profile(), gallery())\n
from reactpy import component, html\n\n__all__ = [\"gallery\", \"profile\"]\n\n\n@component\ndef profile():\n    return html.img({\"src\": \"https://i.imgur.com/QIrZWGIs.jpg\", \"alt\": \"Alan L. Hart\"})\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n

Now gallery.py publishes both components:

  • gallery.py:
    • Lists gallery and profile in __all__.
  • app.py:
    • Imports both gallery and profile from gallery.py.
    • Renders each of them.
"},{"location":"learn/importing-and-exporting-components/#recap","title":"Recap","text":"

You've learned:

  • What a root component module is, and that it depends on your executor
  • How to import a component from another file
  • How ReactPy expresses public and private components with __all__ and a leading underscore
  • How to export multiple components from the same file
"},{"location":"learn/importing-and-exporting-components/#next-steps","title":"Next Steps","text":"

Now that your components can live in separate files, continue to Passing Props to a Component to make them configurable.

"},{"location":"learn/installation/","title":"Installation","text":""},{"location":"learn/installation/#overview","title":"Overview","text":"

React has been designed from the start for gradual adoption. You can use as little or as much React as you need. Whether you want to get a taste of React, add some interactivity to an HTML page, or start a complex React-powered app, this section will help you get started.

You will learn

  • How to start a new React project
  • How to add React to an existing project
  • How to set up your editor
  • How to install React Developer Tools
"},{"location":"learn/installation/#start-a-new-react-project","title":"Start a new React project","text":"

If you want to build an app or a website fully with React, start a new React project.

"},{"location":"learn/installation/#add-react-to-an-existing-project","title":"Add React to an existing project","text":"

If want to try using React in your existing app or a website, add React to an existing project.

"},{"location":"learn/installation/#next-steps","title":"Next steps","text":"

Head to the Quick Start guide for a tour of the most important React concepts you will encounter every day.

"},{"location":"learn/keeping-components-pure/","title":"Keeping Components Pure","text":""},{"location":"learn/keeping-components-pure/#overview","title":"Overview","text":"

Some Python functions are pure. Pure functions only perform a calculation and nothing more. By strictly only writing your components as pure functions, you can avoid an entire class of baffling bugs and unpredictable behavior as your codebase grows. To get these benefits, though, there are a few rules you must follow.

You will learn

  • What purity is and how it helps you avoid bugs
  • How to keep components pure by keeping changes out of the render phase
  • Why ReactPy cannot check purity for you
"},{"location":"learn/keeping-components-pure/#purity-components-as-formulas","title":"Purity: Components as formulas","text":"

In computer science (and especially the world of functional programming), a pure function is a function with the following characteristics:

  • It minds its own business. It does not change any objects or variables that existed before it was called.
  • Same inputs, same output. Given the same inputs, a pure function should always return the same result.

You might already be familiar with one example of pure functions: formulas in math.

Consider this math formula: y = 2 * x.

If x = 2 then y = 4. Always.

If x = 3 then y = 6. Always.

If x = 3, y won't sometimes be 9 or -1 or 2.5 depending on the time of day or the state of the stock market.

If y = 2 * x and x = 3, y will always be 6.

If we made this into a Python function, it would look like this:

def double(number):\n    return 2 * number\n

In the above example, double is a pure function. If you pass it 3, it will return 6. Always.

ReactPy is designed around this concept. ReactPy assumes that every component you write is a pure function. This means that ReactPy components you write must always return the same VDOM given the same inputs:

app.py Run
@component\ndef recipe(drinkers):\n    return html.ol(\n        html.li(f\"Boil {drinkers} cups of water.\"),\n        html.li(f\"Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.\"),\n        html.li(f\"Add {0.5 * drinkers} cups of milk to boil and sugar to taste.\"),\n    )\n\n\n@component\ndef app():\n    return html.section(\n        html.h1(\"Spiced Chai Recipe\"),\n        html.h2(\"For two\"),\n        recipe(2),\n        html.h2(\"For a gathering\"),\n        recipe(4),\n    )\n
# TODO\n

When you pass drinkers=2 to recipe(), it will return VDOM containing 2 cups of water. Always.

If you pass drinkers=4, it will return VDOM containing 4 cups of water. Always.

Just like a math formula.

You could think of your components as recipes: if you follow them and don't introduce new ingredients during the cooking process, you will get the same dish every time. That \"dish\" is the VDOM that the component serves to ReactPy to render.

"},{"location":"learn/keeping-components-pure/#side-effects-unintended-consequences","title":"Side Effects: (un)intended consequences","text":"

ReactPy's rendering process must always be pure. Components should only return their VDOM, and not change any objects or variables that existed before rendering\u2014that would make them impure!

Here is a component that breaks this rule:

app.py Run
guest = 0\n\n\n@component\ndef cup():\n    # Bad: changing a preexisting variable!\n    global guest\n    guest = guest + 1\n    return html.h2(f\"Tea cup for guest #{guest}\")\n\n\n@component\ndef tea_set():\n    return html(cup(), cup(), cup())\n
# TODO\n

This component is reading and writing a guest variable declared outside of it. This means that calling this component multiple times will produce different VDOM! And what's more, if other components read guest, they will produce different VDOM, too, depending on when they were rendered! That's not predictable.

Render this once and you get the numbering you expect:

Tea cup for guest #1\nTea cup for guest #2\nTea cup for guest #3\n

Render it again \u2014 after a hot reload, a reconnect, or simply a second request in the same process \u2014 and the counters have silently moved on:

Tea cup for guest #4\nTea cup for guest #5\nTea cup for guest #6\n

Going back to our formula y = 2 * x, now even if x = 2, we cannot trust that y = 4. Our tests could fail, our users would be baffled, planes would fall out of the sky\u2014you can see how this would lead to confusing bugs!

You can fix this component by passing guest as a prop instead:

app.py Run
@component\ndef cup(guest):\n    return html.h2(f\"Tea cup for guest #{guest}\")\n\n\n@component\ndef tea_set():\n    return html(cup(1), cup(2), cup(3))\n
# TODO\n

Now your component is pure, as the VDOM it returns only depends on the guest prop.

In general, you should not expect your components to be rendered in any particular order. It doesn't matter if you call y = 2 * x before or after y = 5 * x: both formulas will resolve independently of each other. In the same way, each component should only \"think for itself\", and not attempt to coordinate with or depend upon others during rendering. Rendering is like a school exam: each component should calculate VDOM on their own!

ReactPy has no Strict Mode to catch this for you

React ships a \"Strict Mode\" that calls every component function twice in development, which is how the example above gets caught: an impure counter jumps to #2, #4, #6. ReactPy has no equivalent \u2014 there is no StrictMode component and no double-invoke. A single render of an impure component looks completely correct, and the bug only appears later as drifting output or as a failure under a test that renders twice.

Two practical consequences:

  • Add a test that renders the component twice and asserts the two results are equal. That is the closest ReactPy gets to Strict Mode, and it is entirely in your hands.
  • Because there is no double-invoke to hide behind, impurity in ReactPy tends to surface as state that changes between reconnects rather than as an obvious warning. Treat any component that touches module-level state as suspect.
"},{"location":"learn/keeping-components-pure/#local-mutation-your-components-little-secret","title":"Local mutation: Your component's little secret","text":"

In the above example, the problem was that the component changed a preexisting variable while rendering. This is often called a \"mutation\" to make it sound a bit scarier. Pure functions don't mutate variables outside of the function's scope or objects that were created before the call\u2014that makes them impure!

However, it's completely fine to change variables and objects that you've just created while rendering. In this example, you create a [] list, assign it to a cups variable, and then append a dozen cups into it:

app.py Run
@component\ndef cup(guest):\n    return html.h2(f\"Tea cup for guest #{guest}\")\n\n\n@component\ndef tea_gathering():\n    cups = []\n    for index in range(1, 13):\n        cups.append(cup(index, key=index))\n    return html.div(cups)\n
# TODO\n

If the cups variable or the [] list were created outside the tea_gathering() function, this would be a huge problem! You would be changing a preexisting object by appending items to that list.

However, it's fine because you've created them during the same render, inside tea_gathering(). No code outside of tea_gathering() will ever know that this happened. This is called \"local mutation\"\u2014it's like your component's little secret.

"},{"location":"learn/keeping-components-pure/#where-you-can-cause-side-effects","title":"Where you can cause side effects","text":"

While functional programming relies heavily on purity, at some point, somewhere, something has to change. That's kind of the point of programming! These changes\u2014updating the screen, starting an animation, changing the data\u2014are called side effects. They're things that happen \"on the side\", not during rendering.

In ReactPy, side effects usually belong inside event handlers. Event handlers are functions that ReactPy runs when you perform some action\u2014for example, when you click a button. Even though event handlers are defined inside your component, they don't run during rendering! So event handlers don't need to be pure.

app.py Run
@component\ndef tea_counter():\n    sips, set_sips = use_state(0)\n\n    async def take_a_sip(event):\n        # Side effects belong here: this runs on click, not during rendering.\n        set_sips(sips + 1)\n\n    return html.div(\n        html.h1(f\"You have taken {sips} sips.\"),\n        html.button({\"onClick\": take_a_sip}, \"Take a sip\"),\n    )\n
# TODO\n

If you've exhausted all other options and can't find the right event handler for your side effect, you can still attach it to your returned VDOM with a use_effect or use_async_effect call in your component. This tells ReactPy to execute it later, after rendering, when side effects are allowed. However, this approach should be your last resort.

When possible, try to express your logic with rendering alone. You'll be surprised how far this can take you!

Why does ReactPy care about purity?

Writing pure functions takes some habit and discipline. But it also unlocks real opportunities:

  • Your components can run on the server, and be rendered to a string. Components already execute server-side in ReactPy; purity is what makes it safe for one component to serve many requests. The same property lets reactpy_to_string turn a component into HTML \u2014 with one limit worth knowing: it calls component.render() with no Layout behind it, so the component may use no hooks at all. Hand it html.* elements or a hook-free component; anything with use_state raises RuntimeError: No life cycle hook is active. Are you rendering in a layout?. It is a string helper for fragments and tests, not a way to server-render an app. A component that mutated module state would leak one request's output into another's.
  • Rendering can be restarted. ReactPy may need to discard an in-progress render, and purity makes it safe to stop calculating at any time.
  • Reasoning and testing become mechanical. A pure component is an ordinary Python function: call it inside a reactpy.core.layout.Layout, render it twice, and compare the results \u2014 no browser required.

Note that one React benefit is not available to you yet: React skips re-rendering components whose inputs have not changed, which is safe precisely because pure functions are safe to cache. ReactPy has use_memo for values, but no memo for components, so a child re-runs whenever its parent renders. Purity is still required; it simply isn't being used for caching today.

"},{"location":"learn/keeping-components-pure/#recap","title":"Recap","text":"
  • A component must be pure, meaning:

    • It minds its own business. It should not change any objects or variables that existed before rendering.
    • Same inputs, same output. Given the same inputs, a component should always return the same VDOM.
  • Rendering can happen at any time, so components should not depend on each others' rendering sequence.

  • You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, \"set\" state instead of mutating preexisting objects.
  • Strive to express your component's logic in the VDOM you return. When you need to \"change things\", you'll usually want to do it in an event handler. As a last resort, you can use_effect.
  • ReactPy will not double-render your components to find impurity for you; write the render-twice test yourself.
  • Writing pure functions takes a bit of practice, but it unlocks the power of the ReactPy paradigm.
"},{"location":"learn/lifecycle-of-reactive-effects/","title":"Lifecycle of Reactive Effects","text":""},{"location":"learn/lifecycle-of-reactive-effects/#overview","title":"Overview","text":"

An Effect is a piece of synchronization code attached to a render. It runs, it may clean up after itself, and it runs again if its inputs change. Understanding that lifecycle \u2014 not a memorized list of rules \u2014 is what makes Effects predictable in ReactPy.

You will learn

  • How an Effect setup and clean-up pair forms one synchronization
  • Which values an Effect \"reacts\" to, and what happens to the ones it doesn't
  • Why a dependency list, not the Effect's own text, decides re-synchronization
  • How Effects are ordered, and what is not guaranteed
  • Why a broken Effect reports itself nowhere the user can see
"},{"location":"learn/lifecycle-of-reactive-effects/#the-lifecycle-of-an-effect","title":"The lifecycle of an Effect","text":"

Think of an Effect as a synchronization process between a value your component holds and something outside ReactPy. Each Effect has a setup and, sometimes, a clean-up:

app.py Run
@component\ndef room_view(room):\n    def effect():\n        # Setup: ReactPy runs this when the Effect is created.\n        close = server.connect(room)\n\n        # Clean-up: ReactPy runs this before the *next* run of this same\n        # Effect, and again when the component unmounts. Together they make\n        # one synchronization: connect this room, then leave it.\n        def cleanup():\n            close()\n\n        return cleanup\n\n    use_effect(effect, dependencies=[room])\n\n    return html.p(f\"{room} -- open connections: {server.open_count}\")\n\n\n@component\ndef app():\n    room, set_room = use_state(\"lobby\")\n\n    async def handle_room_click(event):\n        set_room(\"dev\" if room == \"lobby\" else \"lobby\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_room_click},\n            f\"Leave {room}\",\n        ),\n        html.p(\"(server log is printed on the server console)\"),\n        room_view(room),\n    )\n
# TODO\n

Verified against the server's own log: mount produces connect lobby; pressing Leave lobby produces close lobby then connect dev; and the last connection closes on unmount. The open-connection count never climbs above 1.

Read the pair as a single unit \u2014 \"be connected to this room\". Setup and clean-up are not two independent callbacks: ReactPy runs the clean-up of a run right before that run's successor sets up, using the same captured state. So switching rooms means leave the old room, join the new one, in that order, and the process is complete no matter how many times the inputs change.

"},{"location":"learn/lifecycle-of-reactive-effects/#why-synchronization-may-need-to-happen-more-than-once","title":"Why synchronization may need to happen more than once","text":"

The value being synchronized can change while the component is on screen: room above, a search query, an auth token. Every such change means the previous synchronization is describing the past, so ReactPy tears it down and starts a new one.

That is why the clean-up is not optional boilerplate. In a browser, forgetting it leaks one subscription until the tab closes. On a ReactPy server it leaks until the process exits \u2014 and the leaked callback still holds your component, still fires, and keeps a connection or file handle alive for a user who left hours ago.

"},{"location":"learn/lifecycle-of-reactive-effects/#how-reactpy-re-synchronizes-an-effect","title":"How ReactPy re-synchronizes an Effect","text":"

When a dependency changes, ReactPy runs this sequence:

  1. The component renders and registers the Effect again.
  2. A new Effect task starts, and its first act is to run the previous run's clean-up \u2014 core/hooks.py, use_effect calls run_effect_cleanup before invoking your function.
  3. Your setup runs and returns the next clean-up.

Two consequences worth internalizing:

  • The clean-up runs on the new task, not the old one. It sees the previous run's captured values, which is exactly what a teardown needs.
  • The old task is not cancelled. A sync Effect's task parks on stop.wait() and is only reaped when the component unmounts. Measured: three dependency changes leave 4 live Effect tasks where there was 1, and all 4 disappear at unmount. The clean-ups still ran in the right order; the parked tasks are simply waiting for a signal that will come at unmount. For a long-lived component re-synchronizing constantly, that is memory you are paying for.
"},{"location":"learn/lifecycle-of-reactive-effects/#thinking-from-the-effects-perspective","title":"Thinking from the Effect's perspective","text":"

When you read an Effect, read it as the outside world would. The code above says: \"while this component is on screen and room is X, stay connected to X.\" Everything in that sentence is checkable:

Sentence part Where it comes from \"while this component is on screen\" the component's mount/unmount \"and room is X\" the dependency list \"stay connected to X\" setup (leaving) the returned clean-up

If part of that sentence is a lie, the Effect is wrong \u2014 not the dependency list, not the linting setup. dependencies is how you spell \"and room is X\"; it is not a performance knob.

"},{"location":"learn/lifecycle-of-reactive-effects/#each-effect-represents-a-separate-synchronization-process","title":"Each Effect represents a separate synchronization process","text":"

Two Effects in one component are two processes, not one with two steps. Keep them apart when their synchronization targets differ, and let each own its own clean-up:

use_effect(connect_to_room, dependencies=[room])\nuse_effect(subscribe_to_feed, dependencies=[user_id])\n

Do not merge them because they live in the same component: merging means a room change tears down the feed subscription too. Two Effects in the same component run in declaration order, so ordering within a component is available to you when one genuinely depends on the other's setup having happened.

"},{"location":"learn/lifecycle-of-reactive-effects/#effects-react-to-reactive-values","title":"Effects \"react\" to reactive values","text":"

Every value in the component body \u2014 props, state, anything computed from them \u2014 belongs to one render. An Effect created by that render reads that render's values and never sees a later one. The dependency list is how you say which values should trigger a new Effect.

Here an Effect reports an error flag but leaves it out of the dependency list:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    show_error, set_show_error = use_state(True)\n    reports, set_reports = use_state([])\n\n    def effect():\n        # Reads `show_error`, but dependencies=[count] never mentions it. So\n        # flipping show_error does not re-synchronize, and this keeps\n        # reporting whatever show_error was when the Effect was created.\n        line = f\"count={count} reported_error={show_error}\"\n        log.append(line)\n        set_reports(lambda previous: [*previous, line])\n\n    use_effect(effect, dependencies=[count])\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_show_error_click(event):\n        set_show_error(not show_error)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button(\n            {\"onClick\": handle_show_error_click},\n            f\"Toggle error (currently {show_error})\",\n        ),\n        html.p(\"Effect reports:\"),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(reports)]),\n    )\n
# TODO\n

Verified: pressing Toggle error re-renders the component and runs the Effect zero times \u2014 the list says dependencies=[count], so nothing happens. Press Add 1 and the Effect runs, and this time it reports the new value of the flag, because a brand-new Effect was created by that render.

That is the precise meaning of \"stale\": the Effect reports values from the render that created it. It does not freeze at mount, and it does not read the latest \u2014 it reads whichever render it was born in. Adding the missing value to the list is the fix.

A local function as a dependency never counts as changed

This one bites hard, and it contradicts the docstrings. ReactPy compares dependencies with core/hooks.py, strictly_equal \u2014 not by id. For two locally defined functions it compares their code: same co_* attributes means same dependency. So a def re-created on every render is never detected as changed:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    label, set_label = use_state(\"count\")\n    runs, set_runs = use_state([])\n\n    def describe():\n        # A fresh function object on every render -- but ReactPy compares two\n        # such local functions by their *code*, not by identity\n        # (core/hooks.py, strictly_equal). So `describe` never counts as\n        # changed, and the Effect below keeps the closure it captured first.\n        return f\"{label}={count}\"\n\n    def effect():\n        line = f\"effect ran, describe() -> {describe()}\"\n        log.append(line)\n        set_runs(lambda previous: [*previous, line])\n\n    use_effect(effect, dependencies=[describe])\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_label_click(event):\n        set_label(label + \"!\")\n\n    return html.div(\n        html.h1(f\"{label}: {count}\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button({\"onClick\": handle_label_click}, \"Rename\"),\n        html.p(\"Effect runs:\"),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(runs)]),\n    )\n
# TODO\n

Verified: press Add 1 twice and Rename once \u2014 the heading shows count!!: 2, yet the Effect has run exactly once and describe() still returns 'count=0'. The Effect is permanently frozen on its first closure, and nothing warns you.

The same rule bites the other way: a fresh [1, 2] or {\"a\": 1} that compares equal is also \"unchanged\", so rebuilding an options dict on every render will not re-trigger an Effect that lists it. Depend on the scalar values you actually use, not on containers or local functions.

"},{"location":"learn/lifecycle-of-reactive-effects/#what-an-effect-with-empty-dependencies-means","title":"What an Effect with empty dependencies means","text":"

dependencies=[] does not mean \"run once, like a componentDidMount\". It means create this Effect and never replace it \u2014 including its body:

def effect():\n    print(f\"body sees label={label}\")\n\nuse_effect(effect, dependencies=[])\n

Verified: change label three times and the component re-renders three times; the Effect never runs again, and when something else finally forces it, it prints the first render's label. An [] Effect is sealed at mount. Reach for it only when the Effect genuinely reads nothing but stable values (module constants, a setter, a ref).

The four settings, restated with their lifecycle meaning:

Value Meaning dependencies=[a, b] Re-synchronize when a or b changes. dependencies=[] Synchronize once, at mount; the body is never replaced. dependencies=None Re-synchronize after every render. omitted Infer the list from the closure's free variables \u2014 then compare with ==, so a rebuilt-but-equal value will not re-trigger."},{"location":"learn/lifecycle-of-reactive-effects/#all-variables-declared-in-the-component-body-are-reactive","title":"All variables declared in the component body are reactive","text":"

Props, state, and anything derived from them are per-render values, so they belong in the dependency list whenever the Effect reads them. Module-level constants and imported singletons do not: they are the same object for every render and every connection.

Careful with the middle case. A dict built at module level looks like a constant and is not safe to mutate \u2014 every connection shares it (VDOM Mutations). And a mutable default argument is shared too. Neither is a dependency, because neither changes; both are bugs if you write to them.

Hooks are unusable inside an effect body

def effect():\n    theme = use_context(theme_context)   # RuntimeError\n

Effects run in their own task, after rendering, with no life cycle hook on the stack. use_context, use_state, and every other hook raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 and that traceback reaches only the server log. Read what you need during render and let the closure carry it:

theme = use_context(theme_context)\n\ndef effect():\n    apply(theme)\n\nuse_effect(effect, dependencies=[theme])\n
"},{"location":"learn/lifecycle-of-reactive-effects/#what-to-do-when-you-dont-want-to-re-synchronize","title":"What to do when you don't want to re-synchronize","text":"

Three legitimate reasons, three different answers.

The value is genuinely constant. Import it, define it at module level, or hoist it out of the component. Then it is not a dependency because it cannot change:

POLL_INTERVAL = 5\n\n\n@component\ndef poller(topic):\n    async def effect():\n        ...\n\n    use_async_effect(effect, dependencies=[topic])\n

You want the latest value without restarting the process. Keep it in a ref and read .current \u2014 the box persists across renders, so a long-lived Effect always reads the newest assignment (Synchronizing with Effects).

You want to switch the process off. Do not delete the hook call \u2014 that changes the hook order and corrupts every hook after it. Pass a flag and branch inside the Effect:

app.py Run
@component\ndef feed(topic, enabled):\n    def effect():\n        if not enabled:\n            # Nothing to set up, and nothing to clean up. The hook call below\n            # still happens, so the hook order never changes.\n            return None\n\n        print(f\"subscribe {topic}\")\n\n        def cleanup():\n            print(f\"unsubscribe {topic}\")\n\n        return cleanup\n\n    use_effect(effect, dependencies=[topic, enabled])\n\n    return html.p(f\"{topic}\")\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n    enabled, set_enabled = use_state(True)\n\n    async def handle_enabled_click(event):\n        set_enabled(not enabled)\n\n    async def handle_topic_click(event):\n        set_topic(\"sports\" if topic == \"news\" else \"news\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_enabled_click},\n            \"Stop\" if enabled else \"Start\",\n        ),\n        html.button(\n            {\"onClick\": handle_topic_click},\n            \"Switch topic\",\n        ),\n        feed(topic, enabled),\n    )\n
# TODO\n

Verified: Stop runs unsubscribe news; while stopped, switching topics logs nothing (correct \u2014 there is nothing to synchronize); and Start then runs subscribe sports for the new topic. The hook call always happens; only the setup is conditional. Note the dependency list includes enabled \u2014 leaving it out would mean toggling did nothing at all.

Do not omit a dependency to dodge a loop

An Effect that re-triggers itself is telling you something. Measured: an Effect that sets a piece of state it also lists as a dependency rendered 86 times before the probe stopped. Deleting the dependency silences the symptom and leaves you with an Effect that reports the past. Fix the loop \u2014 usually by moving the value into the dependency list and making the setup idempotent, or by deriving the value during render instead (You Might Not Need an Effect).

"},{"location":"learn/lifecycle-of-reactive-effects/#reactpy-will-not-verify-your-dependencies","title":"ReactPy will not verify your dependencies","text":"

React's side of this is eslint-plugin-react-hooks, which reads the Effect and its dependency list and warns when they disagree. ReactPy's equivalent is reactpy-flake8 \u2014 a real plugin, pinned in this repo's pyproject.toml \u2014 but it is far narrower than people expect. Every row below was produced by running the installed reactpy_flake8.run.run_checks over the snippet in question.

The rules-of-hooks half, always on:

What you wrote Reported? a hook called inside an if, for, while, try, match, or a conditional expression yes \u2014 REACTPY102 a hook used after an early return yes \u2014 REACTPY103 a hook used outside any component or hook yes \u2014 REACTPY101 a hook defined as a closure inside another function yes \u2014 REACTPY100

The dependency half, which needs exhaustive_hook_deps=True and is otherwise silent:

What you wrote Reported? an inline lambda Effect, use_memo, or use_callback missing a variable that is assigned at the top of the component body yes \u2014 REACTPY202 the same, but the missing variable is a component parameter no \u2014 parameters are not in the set of names it compares against a named def Effect missing a variable no a @use_effect-decorated function missing a variable no a dependency written as obj.attr rather than a bare name yes \u2014 REACTPY200 a dependency list that is a variable or call rather than a literal list, tuple, or None yes \u2014 REACTPY201

So the linter catches every call-site violation, and catches a missing dependency only for an inline lambda over a body-assigned variable \u2014 one shape among the several people actually write. The stale-value class of bug is largely on you.

Two habits replace the missing dependency check:

  1. Read the Effect and list the names. Every free variable in the body should appear in dependencies, or be provably module-level. This is mechanical \u2014 do it once per Effect at write time.
  2. Test the re-synchronization, not just the mount. Render, change the input, assert the outside world changed:

    async def test_switching_room_reconnects():\n    async with Layout(app()) as layout:\n        await layout.render()\n        ...  # deliver the click that changes the room\n        assert server.log == [\"connect lobby\", \"close lobby\", \"connect dev\"]\n

    A reactpy.core.layout.Layout in pytest is enough \u2014 no browser, and it catches exactly the bugs the linter cannot see.

"},{"location":"learn/lifecycle-of-reactive-effects/#the-order-effects-run-in","title":"The order Effects run in","text":"

Verified output for a parent with two children, each declaring two Effects:

beta: first effect\nbeta: second effect\nalpha: first effect\nalpha: second effect\npanel: my own effect\n
app.py Run
@component\ndef leaf(name):\n    @use_effect\n    def first():\n        print(f\"{name}: first effect\")\n\n    @use_effect\n    def second():\n        print(f\"{name}: second effect\")\n\n    return html.p(name)\n\n\n@component\ndef panel():\n    @use_effect\n    def parent_effect():\n        print(\"panel: my own effect\")\n\n    # Two children: the SECOND one's effects run first.\n    return html.div(leaf(\"alpha\"), leaf(\"beta\"))\n\n\n@component\ndef app():\n    return html.div(\n        html.h1(\"Watch the server console\"),\n        panel(),\n    )\n
# TODO\n

Three rules, and nothing else:

  • Children before parent. A parent's own Effects run after everything below it has set up.
  • Siblings in reverse source order. beta before alpha. This surprises everyone and is worth knowing if two siblings write to the same external resource.
  • Within one component, declaration order. This one you can rely on.

Do not build anything on the first two. They are behaviour, not contract \u2014 the reverse-sibling rule in particular is an artifact of how effects are collected, and code that depends on a particular cross-component order is code that will break. Within a component, order Effects deliberately; across components, make each Effect self-contained.

"},{"location":"learn/lifecycle-of-reactive-effects/#async-effects-cancellation-is-not-instant","title":"Async Effects: cancellation is not instant","text":"

use_async_effect wraps your coroutine in a task. On re-synchronization the old task is cancelled, which means it stops at its next await \u2014 not before, and not necessarily in time.

Two things follow, both measured.

Two runs can be alive at once. Switch topics three times quickly, before any 0.4 s poll resolves, and the log is:

start polling news\ncancelled news\nstart polling sports\nstart polling news          # the older run is still awaiting\ncancelled news\nstart polling sports\nfinished polling sports\nfinished polling sports\n

Verified, and identical across three repeats: two runs are simultaneously awaiting. Cancellation happens when the successor task starts, so a run that has already passed its awaits cannot be recalled \u2014 and notice the two finished lines belong to the same topic.

A superseded run can still write state. With a 0.4 s source and four rapid switches (a \u2192 b \u2192 c \u2192 d), the runs that actually applied their result were ['DATA FOR b', 'DATA FOR d'] \u2014 a and c were cancelled, b and d completed and called their setters. Reproducible across runs.

So cancellation is a bias toward the newest value, not a guarantee. If a stale write would be damaging, do not rely on being cancelled \u2014 tag the data with what produced it and check on write:

loaded, set_loaded = use_state({\"name\": None, \"data\": {}})\n\nasync def effect():\n    data = await fetch(name)\n    if name == expected_name.current:   # still the value we care about?\n        set_loaded({\"name\": name, \"data\": data})\n

Put teardown that must run in the cancellation branch, and re-raise:

async def effect():\n    handle = open_connection()\n    try:\n        while True:\n            await handle.poll()\n    except asyncio.CancelledError:\n        handle.close()\n        raise\n

Swallowing CancelledError leaves the task in a state asyncio did not expect.

A sync Effect blocks the whole connection

use_effect runs on the connection's event loop. Measured: an Effect calling time.sleep(0.4) made the next click take 0.40 s to be served, and every other handler on that connection waited behind it. Only the browser's own local typing stays responsive. Long or blocking work belongs in use_async_effect, with blocking calls wrapped in asyncio.to_thread (Communicating Data Between Server and Client).

"},{"location":"learn/lifecycle-of-reactive-effects/#when-an-effect-fails-nothing-reports-it","title":"When an Effect fails, nothing reports it","text":"

This is the section that saves the most hours. An Effect body that raises is invisible in three places at once:

  • the page looks completely fine,
  • the browser console is clean,
  • and \u2014 the part that misleads people \u2014 the server log stays silent too.
app.py Run
@component\ndef dashboard():\n    refreshes, set_refreshes = use_state(0)\n\n    def effect():\n        # Suppose this reads a metric from a database and the query fails.\n        #\n        # The page does not change, the browser console stays clean, and no\n        # error reaches the model. The ONLY trace is a traceback on the\n        # server -- and, as written here, it does not even appear until the\n        # component unmounts. Test your Effects; the UI will not report them.\n        msg = \"metric query failed\"\n        raise RuntimeError(msg)\n\n    use_effect(effect, dependencies=[refreshes])\n\n    async def handle_refreshes_click(event):\n        set_refreshes(refreshes + 1)\n\n    return html.div(\n        html.h1(\"Dashboard looks fine\"),\n        html.button({\"onClick\": handle_refreshes_click}, \"Refresh\"),\n    )\n
# TODO\n

Measured with a log handler attached to the reactpy logger: after mount, after three dependency changes, and after half a second idle, zero log records. The single ERROR Error in effect record appears only when the component unmounts \u2014 because core/_life_cycle_hook.py, affect_component_will_unmount is where the Effect tasks are finally awaited and their exceptions gathered (core/hooks.py parks each task on stop.wait(), so nobody looks at its result before then).

Consequences to design around:

  • Never diagnose an Effect from the page. Read the server log, and read it after navigating away \u2014 that is when failures surface.
  • Assert the external world in tests. A render assertion passes happily while your Effect is throwing; assert that the connection happened, the row was written, the file exists.
  • Catch inside the Effect and set state when a failure should be visible to the user. ReactPy will not do it for you:

    def effect():\n    try:\n        set_metrics(fetch_metrics())\n    except Exception as error:\n        set_error(str(error))   # now the user can see it\n

Contrast with the failure modes that are reported, so you can tell which one you have:

Symptom Cause Blank region, error in the model (debug on) or a server traceback the component body raised \u2014 e.g. an async def passed to use_effect ValueError: Cell is empty in the model an Effect registered above the state it reads Page looks fine, log silent until unmount the Effect body raised Page looks fine, log clean forever, Effect never runs wrong dependencies"},{"location":"learn/lifecycle-of-reactive-effects/#recap","title":"Recap","text":"
  • An Effect is a synchronization process: setup, clean-up, and a dependency list that says when to redo it.
  • The clean-up of a run executes on its successor's task, before that successor's setup \u2014 so a re-synchronization is leave-then-join, in order.
  • An Effect reads the values of the render that created it. Not the latest, not necessarily the first.
  • The dependency list, not the Effect's own text, decides re-synchronization. dependencies=[] seals the body at mount.
  • Dependencies compare with == via strictly_equal, so local functions and rebuilt-but-equal containers never count as changed \u2014 and a frozen Effect is silent.
  • ReactPy will not check your dependency list: reactpy-flake8 reports hooks in branches, and missing dependencies only for inline lambdas. Write the list yourself and test the re-synchronization.
  • Children's Effects run before the parent's, siblings in reverse, and within a component in declaration order. Only the last is safe to depend on.
  • Async cancellation lands at the next await: runs can overlap and a superseded run can still write. Guard the write; don't trust the cancel.
  • A raising Effect shows nothing in the page, nothing in the browser, and nothing in the log until unmount. Verify Effects by asserting their effect on the world.
"},{"location":"learn/lifecycle-of-reactive-effects/#related","title":"Related","text":"
  • Synchronizing with Effects \u2014 the four-way dependency table, clean-ups, and the common patterns
  • You Might Not Need an Effect \u2014 the cases that belong in the render
  • Separating Events from Effects \u2014 reading fresh values without restarting a process
  • Removing Effect Dependencies \u2014 the legitimate ways to drop a dependency
  • Use Effect and Use Async Effect \u2014 parameter-level reference
"},{"location":"learn/manipulating-the-dom-with-refs/","title":"Manipulating the DOM with Refs","text":""},{"location":"learn/manipulating-the-dom-with-refs/#overview","title":"Overview","text":"

Sometimes you need to do something a rendering model cannot express: focus an input, scroll an element into view, call a browser API ReactPy does not expose. In React you reach for a ref to hold the DOM node. ReactPy runs on the server and sends description-of-UI over a socket, so it has no DOM node to hand you \u2014 and the consequences shape everything on this page.

You will learn

  • Why there is no <div ref={myRef}> equivalent in ReactPy
  • What happens if you pass a \"ref\" attribute anyway
  • How to focus, scroll, and restyle declaratively, and when you can't
  • Why you must not mutate the DOM ReactPy renders
"},{"location":"learn/manipulating-the-dom-with-refs/#there-is-no-ref-to-the-node","title":"There is no ref to the node","text":"

In React, you declare a ref, pass it to a tag, and React fills it in:

# This is React, not ReactPy. It does not work.\n<div ref={myRef}>\n

myRef.current then holds a real DOM node you can call focus() or scrollIntoView() on. That works because React runs in the browser, next to the DOM.

ReactPy runs in your Python process. It builds a VDOM dictionary, serialises it to JSON, and sends it over a websocket; the browser-side client turns that JSON into DOM nodes. By the time a node exists, the code that described it has finished running, and there is no channel for the browser to send a node back. The \"node\" is on the other side of a network boundary.

"},{"location":"learn/manipulating-the-dom-with-refs/#what-ref-actually-does","title":"What \"ref\" actually does","text":"

It is tempting to copy the React shape and put \"ref\" in the attribute dict. Don't \u2014 both forms fail, and one of them fails quietly.

Passing a dict that looks like a ref is silently dropped:

html.div({\"ref\": {\"current\": None}}, \"...\")\n

The key travels to the browser as an ordinary attribute. Even if the client writes a node into that object, it writes into its own throwaway copy decoded from the JSON \u2014 nothing can travel back up the socket into your Python dict. Measured: the element renders normally, as_dict[\"current\"] stays None forever, and there is no error and no warning. A \"focus the input\" button built this way simply never focuses anything.

Passing a string is worse, because it throws in the browser:

html.div({\"ref\": \"my_ref\"}, \"...\")\n

preact tries to assign to the string and the page dies with an uncaught:

TypeError: Cannot create property 'current' on string 'my_ref'\n

The server logs nothing. Check the browser console.

"},{"location":"learn/manipulating-the-dom-with-refs/#focusing-an-input","title":"Focusing an input","text":"

If the input should be focused when it first appears, that is declarative \u2014 use the browser's own attribute and let ReactPy render it:

app.py Run
@component\ndef search_form():\n    return html.form(\n        html.label({\"htmlFor\": \"search\"}, \"Search\"),\n        html.input({\"id\": \"search\", \"type\": \"search\", \"autoFocus\": True}),\n    )\n
# TODO\n

No ref, no effect, no JavaScript. The attribute dict is the DOM.

To focus later, in response to a click, there is no server-side way to reach a node \u2014 but ReactPy has an escape hatch for exactly this. When the value of an on* key is a string, it runs as JavaScript in the browser (see Responding to Events). Give the element an \"id\" and reach it by id:

app.py Run
@component\ndef search_form():\n    return html.form(\n        html.input({\"id\": \"search\", \"type\": \"search\"}),\n        # A string in a handler slot runs in the browser (see Responding to\n        # Events), which is the only way to reach an element by id.\n        html.button(\n            {\n                \"onClick\": \"document.getElementById('search').focus()\",\n            },\n            \"Focus the input\",\n        ),\n    )\n
# TODO\n

This is the honest equivalent of myRef.current.focus(). The trade-off is that the code doing the focusing now lives in a string, on the server, unchecked by your linter and invisible to your type checker. Keep it to one-line calls.

Re-rendering does not re-focus

A common wish is \"re-fire autoFocus when the user clicks again\". Changing the element's \"key\" really does remount it \u2014 ReactPy destroys and recreates the node, and a child's state resets \u2014 but autoFocus does not re-trigger on that remount, because the browser only applies autofocus on initial page load. Measured: after re-keying, focus stayed on the button that was clicked. Use the inline-JS form above instead.

"},{"location":"learn/manipulating-the-dom-with-refs/#scrolling-to-an-element","title":"Scrolling to an element","text":"

The React version of this example keeps three refs, one per image, and calls scrollIntoView() on each. In ReactPy you give each item an id and let the browser find it:

app.pydata.pystyles.css Run
@component\ndef cat_friends():\n    return html(\n        html.div(\n            {\"id\": \"carousel\", \"className\": \"carousel\"},\n            [\n                html.li(\n                    {\"key\": person[\"id\"], \"id\": f\"friend-{person['id']}\"},\n                    html.img({\"src\": get_image_url(person), \"alt\": person[\"name\"]}),\n                )\n                for person in friends\n            ],\n        ),\n        html.div(\n            {\"className\": \"buttons\"},\n            [\n                html.button(\n                    {\n                        \"key\": person[\"id\"],\n                        \"onClick\": f\"document.getElementById('friend-{person['id']}')\"\n                        \".scrollIntoView({behavior: 'smooth', inline: 'center'})\",\n                    },\n                    person[\"name\"].split()[0],\n                )\n                for person in friends\n            ],\n        ),\n    )\n
friends = [\n    {\n        \"id\": 0,\n        \"name\": \"Creola Katherine Johnson\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,\n        \"name\": \"Mohammad Abdus Salam\",\n        \"image_id\": \"bE7W1ji\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}.jpg\"\n
.carousel {\n    display: flex;\n    gap: 10px;\n    overflow-x: auto;\n    scroll-snap-type: x mandatory;\n    padding: 10px 0;\n}\n\n.carousel li {\n    list-style: none;\n    flex: 0 0 auto;\n    scroll-snap-align: center;\n}\n\n.carousel img {\n    width: 130px;\n    height: 130px;\n    border-radius: 100%;\n    object-fit: cover;\n    display: block;\n}\n\n.buttons {\n    display: flex;\n    gap: 10px;\n    margin-top: 10px;\n}\n
# TODO\n

Note the f-string building the JavaScript: the id is interpolated server-side, so each button gets its own literal scrollIntoView call. That is also the risk \u2014 the interpolation is not escaped. Interpolating anything a user controls into that string is an injection hole; interpolate your own integer keys, like person[\"id\"] above, and nothing else.

"},{"location":"learn/manipulating-the-dom-with-refs/#showing-and-hiding","title":"Showing and hiding","text":"

Toggling visibility is the case where React's ref example and ReactPy agree on the goal but differ completely in method. There is no element.style.display = 'none'; you render the difference:

app.py Run
@component\ndef todo_list():\n    shown, set_shown = use_state(True)\n\n    async def handle_shown_click(event):\n        set_shown(not shown)\n\n    return html(\n        html.div(\n            {\"id\": \"todo\", \"hidden\": not shown},\n            html.p(\"Learn ReactPy\"),\n            html.p(\"Build something\"),\n        ),\n        html.button(\n            {\"onClick\": handle_shown_click},\n            \"Hide\" if shown else \"Show\",\n        ),\n    )\n
# TODO\n

Pressing the button flips shown, the \"hidden\" attribute changes, and the client updates the element. Any style you would have set by hand goes in the attribute dict the same way \u2014 \"style\" accepts a dict of camelCased CSS properties:

html.div(\n    {\"style\": {\"color\": \"red\", \"fontSize\": \"20px\", \"fontWeight\": \"700\"}},\n    \"styled text\",\n)\n

Changing a value in that dict from state re-renders the element with the new style, which is what you almost always want instead of touching element.style from the browser.

"},{"location":"learn/manipulating-the-dom-with-refs/#two-component-only-features-you-cannot-port","title":"Two component-only features you cannot port","text":"

Two sections of React's version of this page have no ReactPy equivalent at all, and it is worth knowing they are absent rather than hunting for them:

  • useImperativeHandle / forwardRef. React lets a component expose a hand-picked slice of its DOM to a parent. There is no forward_ref and no use_imperative_handle in ReactPy \u2014 a component cannot hand a parent a node it does not have. If a parent must drive a child, pass a prop and let the child render it.
  • flushSync. React lets you force a synchronous repaint so that a subsequent DOM read sees the new values. ReactPy renders by sending a message, so there is nothing to flush; the client applies updates as they arrive.
"},{"location":"learn/manipulating-the-dom-with-refs/#best-practices","title":"Best practices","text":"

Refs are an escape hatch, and here the hatch is even narrower: it is a string of JavaScript.

  • Prefer declarative. If the change can be expressed as an attribute, a class, a \"style\" dict, a \"key\", or a conditional render, write that. It survives re-renders, and it is visible to your tools.
  • Keep inline JavaScript to one call. \"document.getElementById('x').focus()\" is fine. A multi-statement script embedded in an attribute dict is unmaintainable and unlintable.
  • Never interpolate user input into an inline-JS string. Build the JS from your own constants and ids.
  • Do not mutate DOM that ReactPy renders. This is the same hazard as in React, and it is easier to hit because nothing warns you. If an inline-JS handler calls remove() on an element ReactPy believes it owns, the next render that tries to update that element is working against a node that is gone. Toggle a state value and let ReactPy remove it.
  • You may touch nodes ReactPy never manages. An element ReactPy renders empty and never writes children to is yours to fill from inline JavaScript. It has no reason to update that subtree, so nothing conflicts.
"},{"location":"learn/manipulating-the-dom-with-refs/#summary","title":"Summary","text":"
  • ReactPy is server-side: there is no DOM node to store in a ref, and {\"ref\": ...} either vanishes (dict) or throws in the browser (string).
  • Express focus, visibility, and styling declaratively first: \"autoFocus\", \"hidden\", \"style\", conditional rendering.
  • For the imperative remainder \u2014 focus on click, scroll into view \u2014 give elements ids and use the inline-JavaScript handler form, keeping each snippet to a single call and interpolating only trusted values.
  • forwardRef, useImperativeHandle, and flushSync do not exist in ReactPy.
  • Never mutate nodes ReactPy owns; remove them by changing state instead.
"},{"location":"learn/manually-register-a-client/","title":"Manually Register a Client","text":""},{"location":"learn/manually-register-a-client/#overview","title":"Overview","text":"

\"Registering a client\" in ReactPy means telling the browser three things: which component to mount, where the socket lives, and which JavaScript modules it may import. The executors do all three for you. This page is for the cases where they cannot \u2014 a page you render yourself, several components on one document, or a component written in JavaScript that the server has to describe but cannot run.

You will learn

  • What mountReactPy needs, and how to write the call by hand
  • How to mount several components in one document, and what breaks if you mount twice into the same node
  • How an author-supplied import map suppresses ReactPy's own
  • How to register a JavaScript component so ReactPy can render it
"},{"location":"learn/manually-register-a-client/#the-three-registrations","title":"The three registrations","text":"What Where it lives Who normally writes it The root component to mount componentPath in mountReactPy, and the backend's component registry the executor's HTML template The socket address pathPrefix + componentPath, concatenated the executor's HTML template Bare-specifier resolution (\"react\", \"react-dom\") <script type=\"importmap\"> injected only if no <script type=\"importmap\" id=\"reactpy-importmap\"> exists

The full reference for the JavaScript side, including every option and its quirks, is Client API. This page covers the parts the executors normally hide.

"},{"location":"learn/manually-register-a-client/#mounting-by-hand","title":"Mounting by hand","text":"

The minimal call needs only an element and a path:

<div id=\"app\"></div>\n<script type=\"module\">\n    import { mountReactPy } from \"/reactpy/static/index.js\";\n\n    mountReactPy({\n        mountElement: document.getElementById(\"app\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"main.counter/\",\n    });\n</script>\n

Three details are load-bearing, all verified in a real browser:

  • pathPrefix must start and end with /. The client builds the socket URL by string concatenation, so \"reactpy/\" on a host with a port throws TypeError: Failed to construct 'URL': Invalid URL \u2014 and on a host without a port silently connects to a hostname like example.comreactpy. The failure mode changes with deployment, which is what makes it nasty.
  • componentPath carries a trailing slash and must name a component the server knows. \"main.counter/\", not \"main.counter\".
  • The import must be <script type=\"module\">. The bundle statically imports two sibling chunks; a classic script fails with SyntaxError: Cannot use import statement outside a module before anything ReactPy-related runs.

The component itself lives in a Python module the backend can import. Under ReactPyMiddleware, componentPath is the dotted path you listed in root_components plus a slash \u2014 the middleware matches ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$. Under ReactPy there is one root and componentPath must be omitted: the client builds the URL as pathPrefix + componentPath (static/index.js) and ReactPy.match_dispatch_path compares the result against dispatcher_path exactly, so a stray \"/\" produces /reactpy//, matches nothing, and the socket dies while the server logs a NotImplementedError \u2014 one whose %s placeholders were never filled in, so the line reads (\"ReactPy app received unsupported request of type '%s' at path '%s'\", 'websocket', '/reactpy//'). Grep for unsupported request of type, not for the literal you expect. See The executors route differently.

Nothing stops you from mounting twice into the same element

mountReactPy returns nothing and keeps no registry, so a second call into the same node is not rejected. Verified in Chrome with two mounts pointing at one <div>: the second component's markup wins the visible tree, and the first keeps a fully live websocket \u2014 its socket stays OPEN, its server-side layout stays alive, and events it registered can still reach the server. You get one visible component and one invisible one consuming a connection and a layout each.

The client does not unmount the first subtree; it renders over it. Nothing you can do from JavaScript will stop it \u2014 there is no unmountReactPy. The only clean teardown is navigation or reload. If you are building a client-side router that swaps components, mount into a fresh element each time and remove the old one, accepting that the orphaned layout lives until the page does.

"},{"location":"learn/manually-register-a-client/#several-components-in-one-document","title":"Several components in one document","text":"

Each mountReactPy call opens its own websocket and gets its own Layout, which means separate hook state and no shared memory between them. Verified with two counters on one page: incrementing one left the other at zero, and the console logged two independent [ReactPy] Connected! lines.

<div id=\"header\"></div>\n<div id=\"cart\"></div>\n<script type=\"module\">\n    import { mountReactPy } from \"/reactpy/static/index.js\";\n\n    mountReactPy({\n        mountElement: document.getElementById(\"header\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"shop.header/\",\n    });\n    mountReactPy({\n        mountElement: document.getElementById(\"cart\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"shop.cart/\",\n    });\n</script>\n

Under ReactPyMiddleware, register both:

from reactpy.executors.asgi import ReactPyMiddleware\n\napp = ReactPyMiddleware(\n    starlette_app,\n    root_components=[\"shop.header\", \"shop.cart\"],\n)\n

Separate roots do not share state

Two components on one page are two layouts. They cannot use_state their way to agreement, and there is no event bus between them. If they must agree, either make them one component tree, or pass the shared value in as props from a single root \u2014 see Sharing State Between Components and Passing Data Deeply with Context.

You can also mount later, from a click handler or after a fetch \u2014 the call is ordinary JavaScript and nothing about it is privileged.

"},{"location":"learn/manually-register-a-client/#import-maps","title":"Import maps","text":"

An import map is only needed when your model contains an importSource, which is what JavaScript components produce. Bare specifiers like \"react\" and \"react-dom\" are resolved by the map; without one, the client falls back to its bundled preact and says so on console.debug, at a level most consoles filter out.

The executors inject a map only when one with a specific id is absent:

if (!document.querySelector(\"#reactpy-importmap\")) { /* inject ReactPy's map */ }\n

The trigger is the id, not the content. Write your own map and give it that id, or ReactPy adds a second one alongside yours and the browser reports a conflict on the \"react\" rule. See The import map for the three-case table.

ReactPy ships a helper that produces exactly that element, so you can decide the framework per page:

from reactpy.executors.asgi import ReactPy\nfrom reactpy.reactjs import import_reactjs\n\napp = ReactPy(\n    counter,\n    html_head=html.head(import_reactjs(\"react\", version=\"19\")),\n)\n

import_reactjs(framework=None, version=None, use_local=False) returns a html.script already carrying {\"type\": \"importmap\", \"id\": \"reactpy-importmap\"}, so putting it in html_head suppresses the automatic injection. \"preact\" (the default) maps the four react* specifiers onto esm.sh/v135/preact@10/compat; \"react\" uses real React at the version you pass (\"19\" if you omit it); use_local=True maps them to ReactPy's own bundled chunks, which is what the default injection does anyway.

use_local rejects companions

import_reactjs(use_local=True, framework=\"react\") raises ValueError: use_local cannot be used with framework or version. Choose one source or the other.

"},{"location":"learn/manually-register-a-client/#registering-a-javascript-component","title":"Registering a JavaScript component","text":"

Python describes; the browser runs. A JavaScript component is any ES module that exports the names you ask for. It can also export a bind function that tells the client how to create, render, and unmount nodes in a container the client owns \u2014 and that is what you want, because the alternative is silent:

// vdom.tsx, loadImportSource\nlet { bind } = module;\nif (typeof bind !== \"function\") {\n    bind = await infer_bind_from_environment();\n}\n

bind is therefore optional. Leave it out and the client infers one \u2014 first trying real React through the import map, then falling back to its bundled preact \u2014 and logs ReactPy will render JavaScript components using internal bindings for 'react' at console.debug, with no [ReactPy] prefix. Your module still mounts and appears to work, so a missing or misnamed bind is easy to miss. Ship it explicitly:

badge.jsmain.py Run
// The bind() export tells the client how to put nodes on screen. It receives\n// the container element the client owns and a config object carrying\n// sendMessage/onMessage, and must hand back create/render/unmount.\nexport function bind(node, config) {\n  return {\n    create: (component, props, children) => {\n      const el = document.createElement(\"span\");\n      el.className = props.tone || \"default\";\n      el.textContent = props.label + \": \" + (children || []).join(\"\");\n      el.addEventListener(\"click\", () => {\n        if (props.onPick) {\n          props.onPick({ label: props.label });\n        }\n      });\n      node.appendChild(el);\n      return el;\n    },\n    render: (el) => {\n      node.innerHTML = \"\";\n      node.appendChild(el);\n    },\n    unmount: () => {\n      node.innerHTML = \"\";\n    },\n  };\n}\n\n// One export per component you intend to register by name.\nexport function Badge(props, children) {\n  return { props, children };\n}\n
HERE = Path(__file__).parent\n\nBadge = component_from_file(\n    HERE / \"badge.js\",\n    \"Badge\",\n    name=\"badge\",\n    fallback=html.p(\"loading badge...\"),\n)\n\n\n@component\ndef product_card(title, tone):\n    return html.div(\n        html.h2(title),\n        Badge({\"label\": \"in stock\", \"tone\": tone}, \"ready to ship\"),\n    )\n
# TODO\n

The registration call gives you a normal constructor:

from pathlib import Path\n\nfrom reactpy.reactjs import component_from_file\n\nBadge = component_from_file(Path(\"badge.js\"), \"Badge\", name=\"badge\")\n\nBadge({\"label\": \"alpha\", \"tone\": \"warn\"}, \"hello\")\n

which produces the element the client executes:

{\n    \"tagName\": \"Badge\",\n    \"attributes\": {\"label\": \"alpha\", \"tone\": \"warn\"},\n    \"children\": [\"hello\"],\n    \"importSource\": {\n        \"source\": \"badge.js\",\n        \"sourceType\": \"NAME\",\n        \"fallback\": null,\n        \"unmountBeforeUpdate\": false\n    }\n}\n

importSource is the whole mechanism: sourceType is \"NAME\" or \"URL\", and the client resolves them differently \u2014 \"NAME\" becomes await import(\"/<prefix>modules/\" + source) and \"URL\" becomes await import(source) directly.

Verified in Chrome: the badge rendered as <span class=\"warn\">alpha: hello</span>, with the <span> coming from JavaScript and the wrapper from Python.

"},{"location":"learn/manually-register-a-client/#the-four-registration-functions","title":"The four registration functions","text":"

All in reactpy.reactjs, all returning a VdomConstructor (or a list, if you pass a list of names):

Function Source Typical use component_from_url(url, import_names, ...) a URL the browser can reach a CDN build, a dev server component_from_npm(package, import_names, version=\"latest\", cdn=\"https://esm.sh/v135\", bundle=...) an NPM package, rewritten to a CDN URL third-party React components component_from_file(file, import_names, name=\"\", symlink=False, ...) a file on disk, copied into the web-modules dir a component you ship in your package component_from_string(content, import_names, name=\"\", ...) a string of JavaScript tiny inline modules, tests

reactpy.web.module_from_url / module_from_file / module_from_string / export do the same thing and are all deprecated \u2014 they warn(DeprecationWarning) telling you to use the component_from_* form.

"},{"location":"learn/manually-register-a-client/#serving-the-module","title":"Serving the module","text":"

A \"NAME\" module is copied into config.REACTPY_WEB_MODULES_DIR and fetched from {path_prefix}modules/ \u2014 which is why a custom backend must serve that directory, and why the built-in ones do:

from reactpy.config import REACTPY_WEB_MODULES_DIR\n\nmodules_app = ServeStaticASGI(not_found, root=REACTPY_WEB_MODULES_DIR.current, prefix=\"/reactpy/modules/\")\n

The default web-modules directory is a temporary directory

REACTPY_WEB_MODULES_DIR defaults to a fresh TemporaryDirectory created at import (config.py). It is per process: read it in one process and you get a path that does not exist in the next. Two consequences:

  • Under a multi-worker server, workers get different directories and a module registered by one is 404 for another. Pin it \u2014 pass web_modules_dir=Path(...) to the executor or set the REACTPY_WEB_MODULES_DIR environment variable to one shared path.
  • The upstream config docstring says the directory must be treated as a black box. Do not read from it yourself; register modules and let the client fetch them.

A \"URL\" module needs no serving at all \u2014 the browser goes to the URL. That makes CDN registration the simpler path when you have a network, and a self-hosted directory the right one for offline or air-gapped use.

"},{"location":"learn/manually-register-a-client/#options-worth-knowing","title":"Options worth knowing","text":"
  • fallback \u2014 what to show while the module loads. Pass an element (html.p(\"loading...\")). Without it there is nothing, and a slow module reads as a broken page.
  • allow_children \u2014 set False for a component that takes props only. Passing a child then raises TypeError: 'Badge' nodes cannot have children. at the call site rather than silently dropping it.
  • unmount_before_update \u2014 unmount and rebuild on every update. Only for packages that fail to re-render on prop change; it costs a full DOM rebuild each time.
  • resolve_imports \u2014 parse the module to learn its export names, so a typo fails in Python at registration instead of in the browser at render. Off by default for component_from_file/component_from_string; it walks imports to resolve_imports_depth (default 5).

A wrong export name fails at different times depending on a flag

With resolve_imports=True, component_from_file(\"badge.js\", \"Nope\") raises immediately:

ValueError: 'badge.js' does not contain 'Nope'\n

With it off \u2014 the default \u2014 you get no Python error, and instead the browser logs, verbatim including the typo:

Module from source {\"source\": \"badge.js\", \"sourceType\": \"NAME\"} does not export Nope\n

and nothing renders. Turn the flag on during development; it turns a console hunt into a stack trace.

A bad bind is a console error, not an exception

If your module's bind does not return an object with create, render, and unmount, the client logs <source> returned an impropper binding \u2014 misspelling in the upstream string \u2014 and renders nothing. Python stays happy. The message goes to console.error, so it is easy to miss when the component simply is not there.

"},{"location":"learn/manually-register-a-client/#summary","title":"Summary","text":"
  • Registering a client means the component path, the socket prefix, and an import map \u2014 the executors write all three for you.
  • pathPrefix needs both slashes, componentPath needs its trailing slash, and the import must be a module script.
  • Mount each component into its own element. Mounting twice into one node leaves a live, invisible websocket you cannot close.
  • Multiple mounts on a page are multiple layouts with no shared state; pass shared values as props instead.
  • ReactPy injects an import map only if no element with id=\"reactpy-importmap\" exists \u2014 the id, not the content, is the trigger. import_reactjs() produces that element.
  • JavaScript components are ordinary ES modules with a bind export, registered with component_from_url/_npm/_file/_string, and served from {prefix}modules/ unless they are URL-sourced.
  • The default web-modules directory is a per-process temporary directory; pin it under any multi-worker deployment.
"},{"location":"learn/manually-register-a-client/#related","title":"Related","text":"
  • Client API \u2014 the full mountReactPy reference and failure-mode table
  • Creating Backends \u2014 serving static/ and modules/ yourself
  • Protocol Structure \u2014 what travels over the socket the mount opens
  • Common Props \u2014 the importSource model shape
  • ReactPyMiddleware \u2014 mounting components into pages you already own
"},{"location":"learn/passing-data-deeply-with-context/","title":"Passing Data Deeply with Context","text":""},{"location":"learn/passing-data-deeply-with-context/#overview","title":"Overview","text":"

Passing data through components that only hand it on is tedious and easy to get wrong. Context lets a component read a value that an ancestor provides, skipping the middle of the tree. In ReactPy it is purely a plumbing tool \u2014 it carries values down and nothing else \u2014 and there are three behaviours worth knowing before you rely on it.

You will learn

  • How to recognise prop drilling worth replacing
  • The three steps: create, use, provide
  • Why a component cannot read the context it provides
  • Why removing a provider throws away the subtree's state
"},{"location":"learn/passing-data-deeply-with-context/#the-problem-with-passing-props","title":"The problem with passing props","text":"

Here is a list of places, and each row is coloured by the current theme. Only the deepest component cares about the theme, but the prop has to travel through everyone:

app.py Run
PLACES = [\"Almaty\", \"Astana\", \"Shymkent\", \"Karaganda\", \"Turkestan\"]\n\n\n@component\ndef place_label(place, theme):\n    color = \"coral\" if theme == \"dark\" else \"black\"\n    return html.li({\"style\": {\"color\": color}}, place)\n\n\n@component\ndef place_list(places, theme):\n    return html.ul([place_label(place, theme) for place in places])\n\n\n@component\ndef list_of_places(places, theme):\n    # Renders its children inside the list box.\n    return html.div(\n        {\"style\": {\"border\": \"1px solid #ccc\", \"padding\": \"8px\"}},\n        place_list(places, theme),\n    )\n\n\n@component\ndef section(places, theme):\n    return html.section(html.h3(\"Places\"), list_of_places(places, theme))\n\n\n@component\ndef app():\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": toggle}, f\"Switch to {'light' if theme == 'dark' else 'dark'}\"\n        ),\n        html.p({\"id\": \"theme\"}, f\"theme={theme}\"),\n        html.div({\"id\": \"places\"}, section(PLACES, theme)),\n    )\n
# TODO\n

app holds the theme. section receives it, list_of_places receives it, place_list receives it, and only place_label reads it. Three components mention a value they never use, and adding a row to list_of_places means threading theme through one more signature.

That is the shape worth replacing: a prop that a component only forwards. If a component would use a prop itself, pass it as a prop \u2014 that is easier to trace.

"},{"location":"learn/passing-data-deeply-with-context/#context-an-alternative-to-passing-props","title":"Context: an alternative to passing props","text":"

Three steps, and the order matters: create the context, read it where you need it, and provide it somewhere above.

"},{"location":"learn/passing-data-deeply-with-context/#step-1-create-the-context","title":"Step 1: Create the context","text":"

A context is an object you create once, usually at module level:

from reactpy import create_context\n\nThemeContext = create_context(\"light\")\n

The argument is the default \u2014 what use_context returns when no provider sits above the reader. Pick something that renders sensibly on its own, because a component will occasionally render without a wrapper.

The object is matched by identity, so create it once and import it everywhere. Two create_context calls are two unrelated contexts even with identical defaults.

"},{"location":"learn/passing-data-deeply-with-context/#step-2-use-the-context","title":"Step 2: Use the context","text":"

In the component that needs the value:

from reactpy import use_context\n\ntheme = use_context(ThemeContext)\n

No parameter, no forwarding. The components in between never learn the value exists:

app.py Run
ThemeContext = create_context(\"light\")\n\n\n@component\ndef place_label(place):\n    # Reads the theme directly: no prop had to travel here.\n    theme = use_context(ThemeContext)\n    color = \"coral\" if theme == \"dark\" else \"black\"\n    return html.li({\"style\": {\"color\": color}}, place)\n\n\n@component\ndef place_list(places):\n    # No theme parameter, and none to forward.\n    return html.ul([place_label(place) for place in places])\n\n\n@component\ndef list_of_places(places):\n    return html.div(\n        {\"style\": {\"border\": \"1px solid #ccc\", \"padding\": \"8px\"}},\n        place_list(places),\n    )\n\n\n@component\ndef section(places):\n    return html.section(html.h3(\"Places\"), list_of_places(places))\n\n\n@component\ndef app():\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": toggle}, f\"Switch to {'light' if theme == 'dark' else 'dark'}\"\n        ),\n        html.p({\"id\": \"theme\"}, f\"theme={theme}\"),\n        # The provider is a component: it renders its children with no wrapper.\n        ThemeContext(html.div({\"id\": \"places\"}, section(PLACES)), value=theme),\n    )\n
# TODO\n

place_label calls use_context, and place_list, list_of_places, and section no longer mention theme at all. Verified: this version and the prop-drilling version above render the same five rows with the same colours, and both respond to the toggle identically.

Without any provider at all, every reader gets the default:

app.py Run
ThemeContext = create_context(\"light\")\n\n\n@component\ndef label(text):\n    theme = use_context(ThemeContext)\n    return html.p({\"id\": f\"label-{text}\"}, f\"{text}: theme={theme}\")\n\n\n@component\ndef app():\n    # Nothing wraps these in ThemeContext, so use_context falls back to the\n    # value passed to create_context.\n    return html.div(html.h3(\"No provider\"), label(\"a\"), label(\"b\"))\n
# TODO\n

Both labels read theme=light \u2014 the value from create_context, not None. This is what makes it safe to reuse a component in a page that never sets the theme up.

"},{"location":"learn/passing-data-deeply-with-context/#step-3-provide-the-context","title":"Step 3: Provide the context","text":"

Wrap the subtree you want to see the value:

ThemeContext(children, value=theme)\n

The context object is a component. Calling it produces a provider element whose children render in place, adding no wrapper of its own \u2014 verified: the element with id=\"places\" contains the whole tree with nothing extra between it and the list.

A provider only covers its own children. A sibling rendered after it sees the default, and a provider nested inside another one shadows it for its own subtree:

app.py Run
@component\ndef deep_label(tag):\n    # Three levels down, still no prop in sight.\n    return html.p({\"id\": tag}, f\"{tag} sees: {use_context(ThemeContext)}\")\n\n\n@component\ndef middle():\n    return html.div(html.h4(\"middle\"), deep_label(\"deep\"))\n\n\n@component\ndef wrapper():\n    # An intermediate component that neither receives nor forwards a theme.\n    return html.div(html.h4(\"wrapper\"), middle())\n\n\n@component\ndef inner_scope():\n    return html.div(\n        deep_label(\"inner\"),\n        # A provider nested inside another one shadows it for its subtree.\n        ThemeContext(deep_label(\"deepest\"), value=\"innermost\"),\n    )\n\n\n@component\ndef app():\n    return html.div(\n        # Same context, several providers, several scopes.\n        deep_label(\"outside\"),\n        ThemeContext(wrapper(), value=\"outer\"),\n        ThemeContext(inner_scope(), value=\"middle-provider\"),\n        deep_label(\"after\"),\n    )\n
# TODO\n

Verified readouts: outside and after see light (the default), deep \u2014 three levels below outer \u2014 sees outer, inner sees middle-provider, and deepest, inside the nested provider, sees innermost. The nearest provider wins; the search walks up and stops.

Pitfall

use_context belongs in the component that uses the value, not in the parent that places it. Write ThemeContext(deep_label(), value=\"x\"), not ThemeContext(html.p(f\"{use_context(ThemeContext)}\"), value=\"x\"). The second one calls the hook while the parent renders, so it reads the parent's context \u2014 the provider has not been entered yet. This is easy to hit by accident: a label formatted inline above a provider reported the default light while its own provider supplied middle-provider.

"},{"location":"learn/passing-data-deeply-with-context/#using-and-providing-context-from-the-same-component","title":"Using and providing context from the same component","text":"

A component cannot read the context it provides. The provider applies to its children, and a component is not its own child:

app.py Run
@component\ndef label(text):\n    return html.p({\"id\": f\"label-{text}\"}, f\"{text} sees: {use_context(ThemeContext)}\")\n\n\n@component\ndef section():\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button({\"onClick\": toggle}, \"toggle\"),\n        # Reads the context the way any *consumer* does...\n        html.p({\"id\": \"self\"}, f\"section itself sees: {use_context(ThemeContext)}\"),\n        # ...and provides it for everything below, which is not itself.\n        ThemeContext(label(\"child\"), value=theme),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Self-provider\"), section(), label(\"outside\"))\n
# TODO\n

The child reads child sees: dark while the component that rendered the provider reads section itself sees: light \u2014 the default, before and after the toggle. If the component needs the value too, read the state it comes from: theme is right there in section, so use it directly.

"},{"location":"learn/passing-data-deeply-with-context/#context-passes-through-intermediate-components","title":"Context passes through intermediate components","text":"

The value reaches any depth as long as the provider is a genuine ancestor. wrapper and middle in the nesting example above receive no props at all, and deep still resolves the theme.

This is also what makes context the right tool for a component you do not control \u2014 a third-party list renderer, a slot you pass children into. Anything rendered inside the provider's subtree can read it, however deep.

Note

A component's state, however, belongs to its position in the tree. Two sibling subtrees that both mount draft() get two independent pieces of state (verified: x#1 and x#2 under two providers of the same context). Context shares what flows down from the provider; it does not merge instances below it.

"},{"location":"learn/passing-data-deeply-with-context/#removing-a-provider-resets-the-subtree-below-it","title":"Removing a provider resets the subtree below it","text":"

A provider is a component, so adding or removing one changes the shape of the tree \u2014 and ReactPy identifies a component by where it sits. The subtree below is unmounted and rebuilt, with its state. Press change value and the draft you typed survives; press toggle provider and it is gone:

app.py Run
@component\ndef draft():\n    # State owned by the leaf. It is lost if this component is remounted.\n    text, set_text = use_state(\"\")\n    body = use_ref(0)\n    body.current += 1\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"placeholder\": \"type something\",\n            }\n        ),\n        html.p(\n            {\"id\": \"draft\"},\n            f\"draft={text!r} renders={body.current} theme={use_context(ThemeContext)}\",\n        ),\n    )\n\n\n@component\ndef panel():\n    wrap, set_wrap = use_state(True)\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle_wrap(event):\n        # Adding or removing the provider changes the shape of the tree, so the\n        # subtree below is unmounted and rebuilt: the draft text is lost.\n        set_wrap(not wrap)\n\n    async def toggle_theme(event):\n        # Changing only the value keeps the same tree: the draft survives.\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button({\"onClick\": toggle_wrap}, \"toggle provider\"),\n        html.button({\"onClick\": toggle_theme}, \"change value\"),\n        (ThemeContext(draft(), value=theme) if wrap else draft()),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Provider identity\"), panel())\n
# TODO\n

Verified sequence. Type hello: the readout shows draft='hello'. Press change value: draft='hello' ... theme=light, and the component's own render counter keeps counting, which is how you can tell it is the same instance \u2014 a changed value does not disturb the tree. Press toggle provider: the counter resets to renders=1 and draft=''. The leaf was unmounted and a fresh one mounted, so its state is not stale, it is new.

Flipping the conditional back does not restore it either \u2014 that is a third instance.

The rule: keep the provider in the tree and vary only its value. Toggling a theme off should be Theme(child, value=\"light\"), not Theme(child, value=...) if dark else child.

Do not reach for key to fix this \u2014 it does not. Verified across four variants: a conditional provider loses the state, and adding a stable key to the child beneath it loses the state too. What keeps the state is having the provider element present in both branches, so the tree shape never changes.

The version that works moves the condition inside value, expressing \"not provided\" as the default rather than as an absent wrapper:

app.py Run
@component\ndef draft():\n    # State owned by the leaf. It survives as long as this component keeps the\n    # same position in the tree.\n    text, set_text = use_state(\"\")\n    body = use_ref(0)\n    body.current += 1\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"placeholder\": \"type something\",\n            }\n        ),\n        html.p(\n            {\"id\": \"draft\"},\n            f\"draft={text!r} renders={body.current} theme={use_context(ThemeContext)}\",\n        ),\n    )\n\n\n@component\ndef panel():\n    provide, set_provide = use_state(True)\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle_wrap(event):\n        set_provide(not provide)\n\n    async def toggle_theme(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button({\"onClick\": toggle_wrap}, \"toggle provider\"),\n        html.button({\"onClick\": toggle_theme}, \"change value\"),\n        # The provider is always in the tree, so nothing below it is unmounted.\n        # \"Not provided\" is expressed as the default value, not as an absent\n        # wrapper.\n        ThemeContext(draft(), value=theme if provide else \"light\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Provider always present\"), panel())\n
# TODO\n

Type hello, press toggle provider: draft='hello' ... theme=light, and the render counter keeps counting. Same buttons, same user-visible behaviour as the broken version, state intact.

This is a special case of a rule that applies to all components, not just providers \u2014 see Preserving and Resetting State. Context makes it easier to hit by accident, because \"wrap this in a provider\" is a natural thing to put behind an if.

"},{"location":"learn/passing-data-deeply-with-context/#before-you-use-context","title":"Before you use context","text":"
  • Does a component only forward the prop? If it uses the value, pass a prop. If it is one level deep, pass a prop. Context pays off when the value crosses several levels that do not care about it.
  • Is the value genuinely shared? A value used by one subtree is simpler as a prop, and easier to trace when it is wrong.
  • Are you hoping it saves renders? It does not. ReactPy has no render bailout, so when the provider re-renders, everything below it re-renders too, whether or not the value changed. Context is a plumbing feature, never a performance one \u2014 see What context does not do.
  • Do you want to change a value from below? Then context is the wrong shape. Put the state in a common ancestor and pass the setter (or a dispatch) down with it \u2014 see Scaling Up with Reducer and Context.
"},{"location":"learn/passing-data-deeply-with-context/#recap","title":"Recap","text":"
  • Context replaces props that components only forward. It does not replace props a component actually uses.
  • Three steps: create_context(default) once at module level, use_context(Context) in the component that needs it, Context(children, value=...) above that subtree.
  • The default from create_context is what an unprovided reader gets \u2014 not None. Verified.
  • The nearest provider wins; a nested provider shadows the outer one, and siblings outside a provider see the default. Verified across four readers.
  • Call use_context in the component that uses the value. Formatting it inside the parent's arguments reads the parent's context.
  • A component cannot read the context it provides \u2014 verified reading the default while its own child read the provided value. Read the underlying state instead.
  • Keep the provider element in the tree and vary only its value. Conditionally wrapping in a provider unmounts the subtree and discards its state; a key does not prevent it (verified).
  • Context is plumbing only: no render bailout, no reactivity, no shared state. See Use Context for the parameter-level detail and error messages.
"},{"location":"learn/passing-data-deeply-with-context/#related","title":"Related","text":"
  • Use Context \u2014 parameters, caveats, error messages, and what context does not do
  • Scaling Up with Reducer and Context \u2014 sharing a reducer and its dispatch, the usual next step
  • Choosing the State Structure \u2014 what belongs in state at all, and where
  • Queueing a Series of State Updates \u2014 the updater form, when two handlers race
  • Reusing Logic with Custom Hooks \u2014 the other way to share behaviour, for logic rather than values
"},{"location":"learn/passing-props-to-a-component/","title":"Passing Props to a Component","text":""},{"location":"learn/passing-props-to-a-component/#overview","title":"Overview","text":"

ReactPy components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any Python value through them, including dictionaries, lists, and functions.

You will learn

  • How to pass props to a component
  • How to read props from a component
  • How to specify default values for props
  • How to pass some markup to a component
  • How props change over time
"},{"location":"learn/passing-props-to-a-component/#familiar-props","title":"Familiar props","text":"

Props are the information that you pass to a tag. For example, className, src, alt, width, and height are some of the props you can pass to an <img>:

app.pystyles.css Run
@component\ndef avatar():\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": \"https://i.imgur.com/1bX5QH6.jpg\",\n            \"alt\": \"Lin Lanying\",\n            \"width\": 100,\n            \"height\": 100,\n        }\n    )\n\n\n@component\ndef profile():\n    return avatar()\n\n\n@component\ndef app():\n    return profile()\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n

The props you can pass to an html.img tag are predefined (the browser conforms to the HTML standard), and they keep their familiar camelCase spelling because they are handed straight to the DOM. But you can pass any props to your own components, such as avatar(), to customize them. Here's how!

"},{"location":"learn/passing-props-to-a-component/#passing-props-to-a-component","title":"Passing props to a component","text":"

In this code, the profile() component isn't passing any props to its child component, avatar():

@component\ndef profile():\n    return avatar()\n

You can give avatar() some props in two steps.

"},{"location":"learn/passing-props-to-a-component/#step-1-pass-props-to-the-child-component","title":"Step 1: Pass props to the child component","text":"

First, pass some props to avatar(). For example, let's pass two props: person (a dictionary), and size (a number):

@component\ndef profile():\n    return html.div(\n        avatar(\n            person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"},\n            size=100,\n        ),\n    )\n

Unlike an HTML tag, a component you write takes its props as ordinary Python arguments. There is no attributes dictionary here \u2014 the values land directly in the function's parameters.

"},{"location":"learn/passing-props-to-a-component/#step-2-read-props-inside-the-child-component","title":"Step 2: Read props inside the child component","text":"

You read props by listing their names person, size directly after def avatar. This lets you use them inside the avatar() code, like you would with a variable.

@component\ndef avatar(person, size):\n    # person and size are available here\n    ...\n

Add some logic to avatar() that uses the person and size props for rendering, and you're done.

Now you can configure avatar() to render in many different ways with different props. Try tweaking the values!

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef profile():\n    return html.div(\n        avatar(\n            person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"},\n            size=100,\n        ),\n        avatar(\n            person={\"name\": \"Katsuko Saruhashi\", \"image_id\": \"YfeOqp2\"},\n            size=100,\n        ),\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n

Props let you think about parent and child components independently. For example, you can change the person or the size props inside profile() without having to think about how avatar() uses them. Similarly, you can change how avatar() uses these props, without looking at profile().

You can think of props like \"knobs\" that you can adjust. They serve the same role as arguments serve for functions\u2014in fact, props are the only argument to your component! A component is nothing but a Python function, and a function can only accept arguments.

Every argument is a prop

In JavaScript, a component receives a single props object which you then destructure into function Avatar({ person, size }). Python needs no such ceremony: the parameters are the props.

@component\ndef avatar(person, size): ...\n

is already the direct, Pythonic form of that idea. But it means every parameter you declare is a prop a caller can pass by name \u2014 so keep the signature of a component to the values it actually renders.

"},{"location":"learn/passing-props-to-a-component/#specifying-a-default-value-for-a-prop","title":"Specifying a default value for a prop","text":"

If you want to give a prop a default value to fall back on when no value is specified, put = and the default value right after the parameter:

@component\ndef avatar(person, size=100):\n    # ...\n

Now, if avatar(person=...) is rendered with no size prop, the size will be set to 100.

The default value is used only when the caller omits the argument entirely. If you pass size=None or size=0, the default value will not be used \u2014 Python applies defaults for missing arguments, not for \"empty\" ones.

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size=100):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef profile():\n    return html.div(\n        avatar(person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"}),\n        avatar(\n            person={\"name\": \"Katsuko Saruhashi\", \"image_id\": \"YfeOqp2\"},\n            size=200,\n        ),\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n
"},{"location":"learn/passing-props-to-a-component/#forwarding-props-with-keyword-argument-unpacking","title":"Forwarding props with keyword argument unpacking","text":"

Sometimes, passing props gets very repetitive:

@component\ndef profile(person, size, is_sepia, thick_border):\n    return html.div(\n        {\"className\": \"card\"},\n        avatar(\n            person=person,\n            size=size,\n            is_sepia=is_sepia,\n            thick_border=thick_border,\n        ),\n    )\n

There's nothing wrong with repetitive code\u2014it can be more legible. But at times you may value conciseness. Some components forward all of their props to their children, like this profile() does with avatar(). Because they don't use any of their props directly, it can make sense to use Python's keyword argument unpacking instead:

@component\ndef profile(**props):\n    return html.div({\"className\": \"card\"}, avatar(**props))\n

This forwards all of profile()'s props to the avatar() without listing each of their names. Inside the function, props is a plain dictionary, so you can also merge or filter it before forwarding.

Use unpacking with restraint. If you're using it in every other component, something is wrong. It hides the component's real interface, so neither your editor nor a reader can tell which props it accepts. Often, it indicates that you should split your components and pass children as arguments. More on that next!

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size=100):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef profile(**props):\n    return html.div({\"className\": \"card\"}, avatar(**props))\n\n\n@component\ndef app():\n    return profile(\n        person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"},\n        size=100,\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n
"},{"location":"learn/passing-props-to-a-component/#passing-children-to-a-component","title":"Passing children to a component","text":"

It is common to nest built-in browser tags:

html.div(html.img())\n

Sometimes you'll want to nest your own components the same way:

card(avatar())\n

Anything you pass positionally lands in a *args parameter of the parent component, which is the ReactPy equivalent of React's children prop. For example, the card() component below collects its children and renders them inside a wrapper <div>:

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size=100):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef card(*children):\n    return html.div({\"className\": \"card\"}, *children)\n\n\n@component\ndef profile():\n    return card(\n        avatar(person={\"name\": \"Katsuko Saruhashi\", \"image_id\": \"YfeOqp2\"}),\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n

Try replacing the avatar() inside card() with some text to see how the card() component can wrap any nested content. It doesn't need to \"know\" what's being rendered inside of it. You will see this flexible pattern in many places.

You can think of a component with a *children parameter as having a \"hole\" that can be \"filled in\" by its parent components with arbitrary markup. You will often use children for visual wrappers: panels, grids, etc.

Keyword-only children

Because positional arguments become children, a component that accepts both should make its props keyword-only to avoid an ambiguous signature:

@component\ndef card(*children, title=None):\n    return html.div({\"className\": \"card\"}, title, *children)\n

Now title= can never be mistaken for a child.

"},{"location":"learn/passing-props-to-a-component/#how-props-change-over-time","title":"How props change over time","text":"

The clock() component below receives two props from its parent component: color and time.

Try changing the color in the select box below:

app.py Run
@component\ndef clock(color, time):\n    return html.h1({\"style\": {\"color\": color}}, time)\n\n\n@component\ndef app():\n    color, set_color = use_state(\"lightcoral\")\n    now, set_now = use_state(datetime.now(timezone.utc))\n\n    async def tick():\n        while True:\n            await asyncio.sleep(1)\n            set_now(datetime.now(timezone.utc))\n\n    use_async_effect(tick, dependencies=[])\n\n    async def handle_change(event):\n        set_color(await event.target.value)\n\n    return html.div(\n        html.p(\n            \"Pick a color: \",\n            html.select(\n                {\"value\": color, \"onChange\": handle_change},\n                html.option({\"value\": \"lightcoral\"}, \"lightcoral\"),\n                html.option({\"value\": \"midnightblue\"}, \"midnightblue\"),\n                html.option({\"value\": \"rebeccapurple\"}, \"rebeccapurple\"),\n            ),\n        ),\n        clock(color=color, time=now.strftime(\"%H:%M:%S\")),\n    )\n
# TODO\n

This example illustrates that a component may receive different props over time. Props are not always static! Here, the time prop changes every second, and the color prop changes when you select another color. Props reflect a component's data at any point in time, rather than only in the beginning.

However, props are immutable\u2014a term from computer science meaning \"unchangeable\". When a component needs to change its props (for example, in response to a user interaction or new data), it will have to \"ask\" its parent component to pass it different props\u2014a new set of arguments! ReactPy re-renders the child with those new values rather than editing the old ones in place.

Don't try to change props

When you need to respond to user input (like changing the selected color), you will need to \"set state\", which you can learn about in State: A Component's Memory. Assigning to a prop parameter inside a component only rebinds the local name; the parent's value is untouched, and the next render overwrites it anyway.

"},{"location":"learn/passing-props-to-a-component/#recap","title":"Recap","text":"
  • To pass props, add them as arguments to the component call, just like you would with an HTML tag's attributes.
  • To read props, list them as parameters: def avatar(person, size).
  • You can specify a default value like size=100, which is used when the caller omits the argument.
  • You can forward all props with def profile(**props) and avatar(**props), but don't overuse it!
  • Nested children like card(avatar()) arrive as a *children parameter.
  • Props are read-only snapshots in time: every render receives a new version of props.
  • You can't change props. When you need interactivity, you'll need to set state.
"},{"location":"learn/preserving-and-resetting-state/","title":"Preserving and Resetting State","text":""},{"location":"learn/preserving-and-resetting-state/#overview","title":"Overview","text":"

State is isolated between components. ReactPy keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders.

You will learn

  • How state is tied to a position in the tree
  • When state is preserved, and when it resets
  • How to reset a component's state on purpose, with position or with key
  • Why defining a component inside another one loses state
"},{"location":"learn/preserving-and-resetting-state/#state-is-tied-to-a-position-in-the-tree","title":"State is tied to a position in the tree","text":"

When you give a component state, you might think the state \"lives\" inside the component. It doesn't. ReactPy holds the state, and associates each piece of it with a component by where that component sits in the tree.

Here counter() is rendered at two different positions:

app.pystyles.css Run
@component\ndef counter():\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(score),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef app():\n    # The same component function, rendered at two different positions in the\n    # tree. ReactPy tracks state by position, so these two counters each get\n    # their own score.\n    return html.div(counter(), counter())\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

Click both counters and notice they don't affect each other. Each position gets its own state, so counter() is really two separate components here.

It makes no difference whether you build the child once and reuse it, or call it twice:

one_counter = counter()\nhtml.div(one_counter, one_counter)  # same as: html.div(counter(), counter())\n

Both render counter() at two positions, and both get independent state.

"},{"location":"learn/preserving-and-resetting-state/#same-component-at-the-same-position-preserves-state","title":"Same component at the same position preserves state","text":"

In this example a checkbox flips a prop on the counter, but there is always exactly one counter() at that position:

app.pystyles.css Run
@component\ndef counter(is_fancy):\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": f\"counter{' fancy' if is_fancy else ''}\"},\n        html.h1(score),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef app():\n    is_fancy, set_is_fancy = use_state(False)\n\n    async def handle_is_fancy_change(event):\n        set_is_fancy(await event.target.checked)\n\n    return html.div(\n        # Only one counter() is ever rendered at this position, and it is the\n        # same component function either way, so ReactPy keeps its state across\n        # the toggle; only the is_fancy prop changes.\n        counter(is_fancy),\n        html.label(\n            html.input(\n                {\n                    \"type\": \"checkbox\",\n                    \"checked\": is_fancy,\n                    \"onChange\": handle_is_fancy_change,\n                }\n            ),\n            \"Use fancy styling\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

Raise the score, then toggle \"Use fancy styling\". The score survives, because the same component stayed at the same position \u2014 only its is_fancy prop changed.

This is the default, and usually what you want.

"},{"location":"learn/preserving-and-resetting-state/#different-components-at-the-same-position-reset-state","title":"Different components at the same position reset state","text":"

Now switch the child to something else entirely:

app.pystyles.css Run
@component\ndef counter():\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(score),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef app():\n    is_paused, set_is_paused = use_state(False)\n\n    async def handle_is_paused_change(event):\n        set_is_paused(await event.target.checked)\n\n    return html.div(\n        # Switching between a counter() and an html.p() puts a *different* kind\n        # of child at this position, so ReactPy discards the counter's state\n        # and starts a brand new one when the break ends.\n        html.p(\"See you later!\") if is_paused else counter(),\n        html.label(\n            html.input(\n                {\n                    \"type\": \"checkbox\",\n                    \"checked\": is_paused,\n                    \"onChange\": handle_is_paused_change,\n                }\n            ),\n            \"Take a break\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

Raise the score, tick \"Take a break\", then untick it. The counter comes back at zero. Replacing the child with an html.p() destroyed the old state, and the returning counter() is a brand new one.

The rule so far: same component at the same position keeps state; anything else at that position does not.

"},{"location":"learn/preserving-and-resetting-state/#resetting-state-at-the-same-position","title":"Resetting state at the same position","text":"

Suppose a scoreboard shows one player's score at a time, and \"Next player\" swaps Taylor for Sarah:

counter(\"Taylor\") if is_player_a else counter(\"Sarah\")\n

Both branches call counter() at the same position, so ReactPy sees one component whose person prop changed \u2014 and the score follows the player over. That's wrong here: Taylor's score and Sarah's score are different things.

There are two ways to get them treated as separate components.

"},{"location":"learn/preserving-and-resetting-state/#option-1-render-them-at-different-positions","title":"Option 1: render them at different positions","text":"

Give each player a container that always exists:

app.pystyles.css Run
@component\ndef counter(person):\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(f\"{person}'s score: {score}\"),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef scoreboard():\n    is_player_a, set_is_player_a = use_state(True)\n\n    async def handle_is_player_a_click(event):\n        set_is_player_a(not is_player_a)\n\n    return html.div(\n        # Two containers that always exist, so each player's counter has its\n        # own position in the tree. Only one holds a counter at a time, and the\n        # container that loses it destroys that state.\n        #\n        # Do not \"simplify\" this to *([] if cond else [counter(...)]): an empty\n        # expansion contributes no children, so both counters end up at\n        # position 0 and ReactPy treats them as one component -- no reset.\n        html.div(counter(\"Taylor\") if is_player_a else None),\n        html.div(counter(\"Sarah\") if not is_player_a else None),\n        html.button(\n            {\"onClick\": handle_is_player_a_click},\n            \"Next player!\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n
  • At first the first html.div() holds Taylor's counter and the second is empty.
  • After \"Next player!\" the first is empty and the second holds Sarah's.

Each counter is destroyed when its container empties, so every switch starts from zero. This works nicely when there are only a couple of alternatives to lay out side by side.

An empty list expansion is not a placeholder

The tempting Python spelling of \"or nothing\" collapses the whole mechanism:

*([] if not is_player_a else [counter(\"Taylor\")]),  # wrong: no child at all\n

An empty expansion contributes no children, so the two counters land at position 0 and ReactPy treats them as one component \u2014 no reset, and Sarah inherits Taylor's score. It is the two html.div() containers above that create the positions. (React avoids this because false still occupies a JSX slot.)

"},{"location":"learn/preserving-and-resetting-state/#option-2-reset-state-with-a-key","title":"Option 2: reset state with a key","text":"

You have seen \"key\" when rendering lists. Keys are not only for lists. By default ReactPy identifies a child by its index within its parent \u2014 \"the first counter\", \"the second counter\" \u2014 and a key lets you say this is not the first counter, it is Taylor's counter.

app.pystyles.css Run
@component\ndef counter(person):\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(f\"{person}'s score: {score}\"),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef scoreboard():\n    is_player_a, set_is_player_a = use_state(True)\n\n    async def handle_is_player_a_click(event):\n        set_is_player_a(not is_player_a)\n\n    return html.div(\n        # Both branches render counter() at the same position, so without a key\n        # ReactPy would treat them as the same component and carry the score\n        # from one player to the next. The key gives each player their own\n        # identity, so switching players resets the score.\n        (\n            counter(\"Taylor\", key=\"Taylor\")\n            if is_player_a\n            else counter(\"Sarah\", key=\"Sarah\")\n        ),\n        html.button(\n            {\"onClick\": handle_is_player_a_click},\n            \"Next player!\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

The two counters no longer share state, even though they appear in the same place. Give \"key\" a value that identifies what the component is \u2014 the contact id, the player name \u2014 not where it sits.

Keys are only unique within their parent

Two components with the same key under different parents are fine: keys are not globally unique, they only identify a position within one parent.

Note also that an unkeyed child is identified by its index, which shares a namespace with explicit keys. html.div(contact_list(...), chat(to, key=0)) raises ValueError: Duplicate keys [0], because the unkeyed contact_list() already occupies position 0. Prefix your keys (key=f\"chat-{to['id']}\") or key all the siblings.

"},{"location":"learn/preserving-and-resetting-state/#resetting-a-form-with-a-key","title":"Resetting a form with a key","text":"

Keying is most useful for forms. In this chat app, chat() holds the half-written message:

app.pydata.pystyles.css Run
@component\ndef chat(contact):\n    draft, set_draft = use_state(\"\")\n\n    async def handle_draft_click(event):\n        set_draft(\"\")\n\n    return html.section(\n        {\"className\": \"chat\"},\n        html.h3(f\"To: {contact['name']} <{contact['email']}>\"),\n        html.textarea({\"placeholder\": \"Write a message\", \"value\": draft}),\n        html.button({\"onClick\": handle_draft_click}, \"Clear\"),\n    )\n\n\n@component\ndef contact_list(selected_contact, on_select):\n    def make_select(contact):\n        # A factory, not a lambda: a handler must be a coroutine, and the\n        # contact it acts on is bound by the closure.\n        async def choose(event):\n            on_select(contact)\n\n        return choose\n\n    return html.section(\n        {\"className\": \"contact-list\"},\n        html.ul(\n            [\n                html.li(\n                    {\"key\": contact[\"id\"]},\n                    html.button(\n                        {\"onClick\": make_select(contact)},\n                        contact[\"name\"],\n                    ),\n                )\n                for contact in contacts\n            ]\n        ),\n    )\n\n\n@component\ndef messenger():\n    to, set_to = use_state(contacts[0])\n\n    return html.div(\n        contact_list(to, set_to),\n        # Without the key, switching contacts keeps the half-written message,\n        # because chat() stays at the same position. The key ties the draft to\n        # the contact instead of the slot.\n        chat(to, key=f\"chat-{to['id']}\"),\n    )\n
contacts = [\n    {\"id\": 0, \"name\": \"Taylor\", \"email\": \"taylor@mail.com\"},\n    {\"id\": 1, \"name\": \"Alice\", \"email\": \"alice@mail.com\"},\n    {\"id\": 2, \"name\": \"Bob\", \"email\": \"bob@mail.com\"},\n]\n
.contact-list ul {\n    list-style: none;\n    padding: 0;\n    display: flex;\n    gap: 8px;\n}\n\n.chat {\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 10px;\n    margin-top: 10px;\n}\n\n.chat textarea {\n    display: block;\n    width: 100%;\n    min-height: 60px;\n}\n
# TODO\n

Type a draft, then pick another contact. Without the key the draft would follow you over, because chat() never left its position. With key=f\"chat-{to['id']}\" the draft belongs to the conversation rather than the slot.

"},{"location":"learn/preserving-and-resetting-state/#keeping-state-for-a-component-that-is-no-longer-rendered","title":"Keeping state for a component that is no longer rendered","text":"

Resetting is often not what you want \u2014 a real chat app should restore the draft when you go back to Alice. Keying does not do that: an unmounted component loses its state, so returning to Taylor starts at zero. Options:

  • Render all the chats and hide the inactive ones with CSS. Nothing is unmounted, so state survives. Great for small UIs, wasteful when the hidden trees are large.
  • Lift the state up and keep each recipient's draft in the parent, in a dict keyed by recipient. Children coming and going stops mattering. This is the most common answer.
  • Keep it outside ReactPy entirely \u2014 a database, or localStorage in the browser \u2014 which you want anyway if a draft should survive a page reload.

Whichever you choose, a chat with Alice is conceptually different from a chat with Bob, so it still deserves a distinct key.

"},{"location":"learn/preserving-and-resetting-state/#nested-component-definitions-reset-state-accidentally","title":"Nested component definitions reset state accidentally","text":"

This one bites quietly:

@component\ndef outer():\n    @component\n    def nested():\n        value, set_value = use_state(...)\n        ...\n\n    return html.div(nested())\n

Every render of outer() creates a new function object for nested(). A new function is a new component, so ReactPy discards the old state and mounts a fresh one on every render \u2014 your state resets itself, with no warning. Measured: a nested child's mount counter climbs by one on each parent render, while a module-level child mounted once.

Define components at module level, or pass data through props.

"},{"location":"learn/preserving-and-resetting-state/#recap","title":"Recap","text":"
  • ReactPy keeps state for as long as the same component is rendered at the same position.
  • State is not stored in the call that produced the VDOM; it belongs to the tree position you put it in.
  • Force a subtree to reset by rendering it at a different position, or by giving it a different \"key\".
  • Unkeyed siblings are identified by index, in the same key namespace as explicit keys \u2014 prefix your keys to avoid ValueError: Duplicate keys.
  • Don't define a component inside another component, or you'll reset state by accident.
"},{"location":"learn/python-in-psx-with-curly-braces/","title":"Python in psx with curly braces","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

See this issue for more details.

"},{"location":"learn/queueing-a-series-of-state-updates/","title":"Queueing a Series of State Updates","text":""},{"location":"learn/queueing-a-series-of-state-updates/#overview","title":"Overview","text":"

Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render. To do this, it helps to understand how ReactPy batches state updates.

You will learn

  • How ReactPy batches state updates
  • Updating the same state multiple times before the next render
  • What happens when you mix values and updater functions
"},{"location":"learn/queueing-a-series-of-state-updates/#reactpy-batches-state-updates","title":"ReactPy batches state updates","text":"

You might expect that clicking the \"+3\" button will increment the counter three times, because it calls set_number(number + 1) three times:

app.pystyles.css Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(number + 1)\n        set_number(number + 1)\n        set_number(number + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+3\"),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n
# TODO\n

It lands on 1, not 3. Two things combine to produce that.

First, as you may recall from State as a Snapshot, each render's state values are fixed. Inside this render's event handler number is always 0, no matter how many times you call the setter, so all three lines evaluate to the same call:

set_number(0 + 1)\nset_number(0 + 1)\nset_number(0 + 1)\n

Second, a handler which does not await never yields, so nothing can render until it returns. ReactPy's setter applies its value immediately and only flags the component for re-render (core/hooks.py, _CurrentState.dispatch), and the flag is deduplicated \u2014 a component already flagged is not scheduled twice (core/_life_cycle_hook.py, LifeCycleHook.schedule_render and its _scheduled_render guard). Three calls to the setter inside one such handler therefore leave one render queued, showing 1.

This lets you update several state variables \u2014 even across several components \u2014 without triggering a render for each. It also avoids \"half-finished\" renders in which only some variables have been updated.

Batching stops at an await

The batching above belongs to a handler that never yields, and it is narrower than React's. Because the values are already stored, an await hands the event loop to the queued render task \u2014 which clears the flag, so the next setter schedules a second render. Nothing about the handler is special; the only difference is who holds the loop. Since every handler is a coroutine, the distinction is not def versus async def but whether anything is awaited between the setter calls.

Measured in a real browser, watching every value the counter takes after a single click:

Handler Observed sequence three set_number(lambda n: n + 1) with no await between them 0 \u2192 3 (one render) the same, with await asyncio.sleep(0.3) between the calls 0 \u2192 1 \u2192 2 \u2192 3 (three renders)

React 18+ would batch both into a single render. In ReactPy, an async def handler that awaits between updates renders \u2014 and repaints \u2014 in between. If that flicker matters, do the awaits first and set state once at the end, or collect the results and make a single setter call.

ReactPy does not batch across multiple intentional events like clicks \u2014 each click is handled separately. This ensures that, for example, if the first click disables a form, the second click cannot submit it again.

"},{"location":"learn/queueing-a-series-of-state-updates/#updating-the-same-state-multiple-times-before-the-next-render","title":"Updating the same state multiple times before the next render","text":"

It is an uncommon use case, but if you would like to update the same state variable multiple times before the next render, pass a function that computes the next value from the previous one instead of the value itself: set_number(lambda n: n + 1). This is a way of saying \"do something with the current state\" rather than \"replace the state\".

app.pystyles.css Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(lambda n: n + 1)\n        set_number(lambda n: n + 1)\n        set_number(lambda n: n + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+3\"),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n
# TODO\n

This time the counter reaches 3, because each updater receives whatever the previous one produced.

"},{"location":"learn/queueing-a-series-of-state-updates/#what-happens-if-you-update-state-after-replacing-it","title":"What happens if you update state after replacing it","text":"

What does this handler produce?

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        # number is 0 for this render, so this reads as set_number(5).\n        set_number(number + 5)\n        set_number(lambda n: n + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"Increase the number\"),\n    )\n
# TODO\n

Step by step, starting from number == 0:

  1. set_number(number + 5) \u2014 number is 0, so this is set_number(5). The state becomes 5.
  2. set_number(lambda n: n + 1) \u2014 an updater, so it runs against the current value: 5 + 1.

The next render shows 6, confirmed by rendering the example.

set_number(5) behaves like set_number(lambda n: 5)

A plain value ignores the argument entirely \u2014 it just replaces. That equivalence is what makes the next case surprising.

"},{"location":"learn/queueing-a-series-of-state-updates/#what-happens-if-you-replace-state-after-updating-it","title":"What happens if you replace state after updating it","text":"

One more:

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(number + 5)\n        set_number(lambda n: n + 1)\n        set_number(42)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"Increase the number\"),\n    )\n
# TODO\n
  1. set_number(number + 5) \u2014 number is 0, so state becomes 5.
  2. set_number(lambda n: n + 1) \u2014 updater, so 5 + 1 = 6.
  3. set_number(42) \u2014 a plain value, so state becomes 42.

The result is 42. The two updaters still ran, and their work was then overwritten \u2014 which is the same answer React's queue produces, arrived at by a different route.

To summarize what you can pass to a state setter:

  • An updater function (e.g. lambda n: n + 1) is applied to the current value.
  • Any other value (e.g. 5) replaces the state outright, discarding whatever the updaters produced.

Updater functions run while your handler runs, inside the setter call itself, so \u2014 like components \u2014 they must be pure. Don't set state from inside one or perform side effects there. They are eager, not deferred, which means the one thing they can still get wrong is the closure: a name the updater reads from the enclosing render is the value that render saw, not the current one. The updater argument is the only value guaranteed fresh, so take everything you need from n.

An updater that raises fails silently

Updaters run eagerly, inside the setter call itself (core/hooks.py, _CurrentState.dispatch calls new(self.value) before scheduling anything) \u2014 not later during rendering. Verified: an updater's side effect lands before the line after its set_number(...).

That has a nasty consequence. If an updater raises, the state is never assigned and no render is scheduled; ReactPy logs Failed to execute event handler on the server and swallows the error. The browser shows nothing, the console stays quiet, and the component simply stops responding to that button. Reproduced: a lambda x: 1 / 0 updater produced a server-side traceback and then an indefinite absence of any render.

Two habits follow: keep updaters to one pure expression, and when a counter \"stops working\" with no error in the browser, check the server log for Failed to execute event handler.

"},{"location":"learn/queueing-a-series-of-state-updates/#naming-conventions","title":"Naming conventions","text":"

Name the updater's argument after the state variable it updates. Python has no single-letter convention as strong as JavaScript's, but the intent is the same \u2014 the argument is the previous value:

set_enabled(lambda enabled: not enabled)\nset_last_name(lambda last_name: last_name[::-1])\nset_friend_count(lambda friend_count: friend_count * 2)\n

For clarity, prefer a descriptive name over n: lambda previous: previous + 1 reads better in a handler that also does other work.

"},{"location":"learn/queueing-a-series-of-state-updates/#recap","title":"Recap","text":"
  • Setting state does not change the variable in the existing render; it requests a new render.
  • ReactPy processes state updates after a handler which never awaits has finished. That is batching.
  • Batching does not survive an await inside an async def handler \u2014 each await can flush a render with partial updates.
  • To update the same state several times in one handler, use updater functions: set_number(lambda n: n + 1).
  • A plain value replaces the state and discards earlier updater results; an updater is applied to the current value.
  • Updater functions must be pure, and ReactPy will not check them for you.
"},{"location":"learn/quick-start/","title":"Quick Start","text":""},{"location":"learn/quick-start/#overview","title":"Overview","text":"

Welcome to the ReactPy documentation! This page will give you an introduction to the 80% of React concepts that you will use on a daily basis.

You will learn

  • How to create and nest components
  • How to add markup and styles
  • How to display data
  • How to render conditions and lists
  • How to respond to events and update the screen
  • How to share data between components
"},{"location":"learn/quick-start/#creating-and-nesting-components","title":"Creating and nesting components","text":"

React apps are made out of components. A component is a piece of the UI (user interface) that has its own logic and appearance. A component can be as small as a button, or as large as an entire page.

React components are Python functions that return markup:

@component\ndef my_button():\n    return html.button(\"I'm a button!\")\n

Now that you've declared my_button, you can nest it into another component:

@component\ndef my_app():\n    return html.div(\n        html.h1(\"Welcome to my app\"),\n        my_button(),\n    )\n

Have a look at the result:

app.py Run
from reactpy import component, html\n\n\n@component\ndef my_button():\n    return html.button(\"I'm a button!\")\n\n\n@component\ndef my_app():\n    return html.div(\n        html.h1(\"Welcome to my app\"),\n        my_button(),\n    )\n
# TODO\n
"},{"location":"learn/quick-start/#adding-styles","title":"Adding styles","text":"

In React, you specify a CSS class with className. It works the same way as the HTML class attribute:

html.img({\"className\": \"avatar\"})\n

Then you write the CSS rules for it in a separate CSS file:

/* In your CSS */\n.avatar {\n    border-radius: 50%;\n}\n

React does not prescribe how you add CSS files. In the simplest case, you'll add a <link> tag to your HTML. If you use a build tool or web framework, consult its documentation to learn how to add a CSS file to your project.

"},{"location":"learn/quick-start/#displaying-data","title":"Displaying data","text":"

You can fetch data from a variety of sources and directly embed it into your components. You can also use the style attribute when your styles depend on JavaScript variables.

app.pystyles.css Run
from reactpy import component, html\n\nuser = {\n    \"name\": \"Hedy Lamarr\",\n    \"image_url\": \"https://i.imgur.com/yXOvdOSs.jpg\",\n    \"image_size\": 90,\n}\n\n\n@component\ndef profile():\n    return html.div(\n        html.h3(user[\"name\"]),\n        html.img(\n            {\n                \"className\": \"avatar\",\n                \"src\": user[\"image_url\"],\n                \"alt\": f\"Photo of {user['name']}\",\n                \"style\": {\n                    \"width\": user[\"image_size\"],\n                    \"height\": user[\"image_size\"],\n                },\n            }\n        ),\n    )\n
.avatar {\n    border-radius: 50%;\n}\n\n.large {\n    border: 4px solid gold;\n}\n
# TODO\n
"},{"location":"learn/quick-start/#conditional-rendering","title":"Conditional rendering","text":"

In React, there is no special syntax for writing conditions. Instead, you'll use the same techniques as you use when writing regular Python code. For example, you can use an if statement to conditionally include components:

@component\ndef my_component():\n    if is_logged_in:\n        content = admin_panel()\n    else:\n        content = login_form()\n    return html.div(content)\n

If you prefer more compact code, you can use the ternary operator.:

@component\ndef my_component():\n    return html.div(admin_panel() if is_logged_in else login_form())\n

When you don't need the else branch, unpack a list that is either one item or empty \u2014 and not Python's logical and, which React's cond && <X/> habit tempts people into writing:

@component\ndef my_component():\n    # No `else` branch needed: unpack a one-item list, or an empty one.\n    # Unpack the *component*, never the dict it returns -- `*admin_panel()`\n    # would iterate the dict and emit its keys as text.\n    # `is_logged_in and admin_panel()` would render the word \"False\".\n    return html.div(*([admin_panel()] if is_logged_in else []))\n

is_logged_in and admin_panel() returns False when the test fails, and ReactPy renders that as the literal word False on the page \u2014 only None and an absent child disappear. See Conditional Rendering for the same trap with numbers.

All of these approaches also work for conditionally specifying attributes. If you're unfamiliar with some of this Python syntax, you can start by always using if...else.

"},{"location":"learn/quick-start/#rendering-lists","title":"Rendering lists","text":"

You will rely on Python features like for loop and list comprehension to render lists of components.

For example, let's say you have an array of products:

products = [\n    {\"title\": \"Cabbage\", \"id\": 1},\n    {\"title\": \"Garlic\", \"id\": 2},\n    {\"title\": \"Apple\", \"id\": 3},\n]\n

Inside your component, use list comprehension to transform an array of products into an array of <li> items:

list_items = [html.li({\"key\": product[\"id\"]}, product[\"title\"]) for product in products]\n

Notice how <li> has a key attribute. For each item in a list, you should pass a string or a number that uniquely identifies that item among its siblings. Usually, a key should be coming from your data, such as a database ID. React uses your keys to know what happened if you later insert, delete, or reorder the items.

app.py Run
from reactpy import component, html\n\nproducts = [\n    {\"title\": \"Cabbage\", \"is_fruit\": False, \"id\": 1},\n    {\"title\": \"Garlic\", \"is_fruit\": False, \"id\": 2},\n    {\"title\": \"Apple\", \"is_fruit\": True, \"id\": 3},\n]\n\n\n@component\ndef shopping_list():\n    list_items = [\n        html.li(\n            {\n                \"key\": product[\"id\"],\n                \"style\": {\"color\": \"magenta\" if product[\"is_fruit\"] else \"darkgreen\"},\n            },\n            product[\"title\"],\n        )\n        for product in products\n    ]\n\n    return html.ul(list_items)\n
# TODO\n
"},{"location":"learn/quick-start/#responding-to-events","title":"Responding to events","text":"

You can respond to events by declaring event handler functions inside your components:

@component\ndef my_button():\n    async def handle_click(event):\n        print(\"You clicked me!\")\n\n    return html.button(\n        {\"onClick\": handle_click},\n        \"Click me\",\n    )\n

Notice how \"onClick\": handle_click has no parentheses at the end! Do not call the event handler function: you only need to pass it down. React will call your event handler when the user clicks the button.

"},{"location":"learn/quick-start/#updating-the-screen","title":"Updating the screen","text":"

Often, you'll want your component to \"remember\" some information and display it. For example, maybe you want to count the number of times a button is clicked. To do this, add state to your component.

First, import use_state from reactpy:

from reactpy import use_state\n

Now you can declare a state variable inside your component:

@component\ndef my_button():\n    count, set_count = use_state(0)\n    # ...\n

You\u2019ll get two things from use_state: the current state (count), and the function that lets you update it (set_count). You can give them any names, but the convention is to write something, set_something = ....

The first time the button is displayed, count will be 0 because you passed 0 to use_state(). When you want to change state, call set_count() and pass the new value to it. Clicking this button will increment the counter:

@component\ndef my_button():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.button({\"onClick\": handle_click}, f\"Clicked {count} times\")\n

React will call your component function again. This time, count will be 1. Then it will be 2. And so on.

If you render the same component multiple times, each will get its own state. Click each button separately:

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef my_app():\n    return html.div(\n        html.h1(\"Counters that update separately\"),\n        my_button(),\n        my_button(),\n    )\n\n\n@component\ndef my_button():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.button({\"onClick\": handle_click}, f\"Clicked {count} times\")\n
button {\n    display: block;\n    margin-bottom: 5px;\n}\n
# TODO\n

Notice how each button \"remembers\" its own count state and doesn't affect other buttons.

"},{"location":"learn/quick-start/#using-hooks","title":"Using Hooks","text":"

Functions starting with use are called Hooks. use_state is a built-in Hook provided by ReactPy. You can find other built-in Hooks in the API reference. You can also write your own Hooks by combining the existing ones.

Hooks are more restrictive than other functions. You can only call Hooks at the top of your components (or other Hooks). If you want to use use_state in a condition or a loop, extract a new component and put it there.

"},{"location":"learn/quick-start/#sharing-data-between-components","title":"Sharing data between components","text":"

In the previous example, each my_button had its own independent count, and when each button was clicked, only the count for the button clicked changed:

However, often you'll need components to share data and always update together.

To make both my_button components display the same count and update together, you need to move the state from the individual buttons \"upwards\" to the closest component containing all of them.

In this example, it is my_app.

Now when you click either button, the count in my_app will change, which will change both of the counts in my_button. Here's how you can express this in code.

First, move the state up from my_button into my_app:

@component\ndef my_app():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(\"Counters that update separately\"),\n        my_button(),\n        my_button(),\n    )\n\n\n@component\ndef my_button(): ...  # this code is being moved up into `my_app`\n

Then, pass the state down from my_app to each my_button, together with the shared click handler. You can pass information to my_button using props:

@component\ndef my_app():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(\"Counters that update together\"),\n        my_button(count, handle_click),\n        my_button(count, handle_click),\n    )\n

The information you pass down like this is called props. Now the my_app component contains the count state and the handle_click event handler, and passes both of them down as props to each of the buttons.

Finally, change my_button to read the props you have passed from its parent component:

@component\ndef my_button(count, on_click):\n    return html.button({\"onClick\": on_click}, f\"Clicked {count} times\")\n

When you click the button, the on_click handler fires. Each button's on_click prop was set to the handle_click function inside my_app, so the code inside of it runs. That code calls set_count(count + 1), incrementing the count state variable. The new count value is passed as a prop to each button, so they all show the new value. This is called \"lifting state up\". By moving state up, you've shared it between components.

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef my_app():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(\"Counters that update together\"),\n        my_button(count, handle_click),\n        my_button(count, handle_click),\n    )\n\n\n@component\ndef my_button(count, on_click):\n    return html.button({\"onClick\": on_click}, f\"Clicked {count} times\")\n
button {\n    display: block;\n    margin-bottom: 5px;\n}\n
# TODO\n
"},{"location":"learn/quick-start/#next-steps","title":"Next Steps","text":"

By now, you know the basics of how to write React code!

Check out the Tutorial to put them into practice and build your first mini-app with React.

"},{"location":"learn/react-developer-tools/","title":"React developer tools","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

See this issue for more details.

"},{"location":"learn/reacting-to-input-with-state/","title":"Reacting to Input with State","text":""},{"location":"learn/reacting-to-input-with-state/#overview","title":"Overview","text":"

ReactPy provides a declarative way to manipulate the UI. Instead of manipulating individual pieces of the UI directly, you describe the different states your component can be in, and switch between them in response to the user input. This is similar to how designers think about the UI.

You will learn

  • How declarative UI programming differs from imperative UI programming
  • How to enumerate the different visual states your component can be in
  • How to trigger the changes between the different visual states from code
"},{"location":"learn/reacting-to-input-with-state/#how-declarative-ui-compares-to-imperative","title":"How declarative UI compares to imperative","text":"

When you design UI interactions, you probably think about how the UI changes in response to user actions. Consider a form that lets the user submit an answer:

  • When you type something into the form, the \"Submit\" button becomes enabled.
  • When you press \"Submit\", both the form and the button become disabled, and a spinner appears.
  • If the request succeeds, the form gets hidden, and a \"Thank you\" message appears.
  • If the request fails, an error message appears, and the form becomes enabled again.

In imperative programming, the above corresponds directly to how you implement interaction. You have to write the exact instructions to manipulate the UI depending on what just happened. Here's another way to think about this: imagine riding next to someone in a car and telling them turn by turn where to go. They don't know where you want to go, they just follow your commands. (And if you get the directions wrong, you end up in the wrong place!) It's called imperative because you have to \"command\" each element, from the spinner to the button, telling the computer how to update the UI:

async function handleFormSubmit(e) {\n    e.preventDefault();\n    disable(textarea);\n    disable(button);\n    show(loadingMessage);\n    hide(errorMessage);\n    try {\n        await submitForm(textarea.value);\n        show(successMessage);\n        hide(form);\n    } catch (err) {\n        show(errorMessage);\n        errorMessage.textContent = err.message;\n    } finally {\n        hide(loadingMessage);\n        enable(textarea);\n        enable(button);\n    }\n}\n\nfunction handleTextareaChange() {\n    if (textarea.value.length === 0) {\n        disable(button);\n    } else {\n        enable(button);\n    }\n}\n\nfunction hide(el) {\n    el.style.display = \"none\";\n}\n\nfunction show(el) {\n    el.style.display = \"\";\n}\n\nfunction enable(el) {\n    el.disabled = false;\n}\n\nfunction disable(el) {\n    el.disabled = true;\n}\n

Manipulating the UI imperatively works well enough for isolated examples, but it gets exponentially more difficult to manage in more complex systems. Adding a new UI element or a new interaction would require carefully checking all existing code to make sure you haven't introduced a bug (for example, forgetting to show or hide something).

ReactPy was built to solve this problem.

In ReactPy, you don't directly manipulate the UI \u2014 meaning you don't enable, disable, show, or hide components directly. Instead, you declare what you want to show, and ReactPy figures out how to update the UI.

This is less a stylistic preference than a physical constraint: a ReactPy server component has no DOM to manipulate. Your code runs on the server and returns a description of the UI; there is no element object to set .style.display on. See Manipulating the DOM with Refs for the one narrow escape hatch and why it rarely helps.

"},{"location":"learn/reacting-to-input-with-state/#thinking-about-ui-declaratively","title":"Thinking about UI declaratively","text":"

You've learned that CSS enables declarative string-based styling so you can describe what to show, not how to show it. You can also learn to think about your ReactPy components declaratively.

ReactPy stands at the intersection of design and computer science, so both of these ideas are sources of inspiration. To think declaratively, take each possible state of a component's UI, break it down into a list, and then write code to describe each state at once.

"},{"location":"learn/reacting-to-input-with-state/#step-1-identify-your-components-different-visual-states","title":"Step 1: Identify your component's different visual states","text":"

First, enumerate the visual states of the UI the user could see:

  1. Empty \u2014 the form has no answer yet.
  2. Typing \u2014 the form has a non-empty answer.
  3. Submitting \u2014 the answer has just been submitted and nothing else can be clicked.
  4. Success \u2014 the user wants to see a \"Correct!\" message instead of the form.
  5. Error \u2014 like Typing, but with an error message.

Replace the form() component with a mock that returns the right UI based on a status prop:

app.pystyles.css Run
@component\ndef form(status=\"empty\"):\n    \"\"\"A mock: the visual states are driven by a prop, not by real logic.\n\n    Rendering this with each status is the fastest way to enumerate the states\n    before writing any interaction code.\n    \"\"\"\n    if status == \"success\":\n        return html.h1(\"That's right!\")\n\n    return html.div(\n        html.h2(\"City quiz\"),\n        html.p(\n            \"In which city is there a billboard that turns air into drinkable water?\"\n        ),\n        html.form(\n            html.textarea({\"disabled\": status == \"submitting\"}),\n            html.br(),\n            html.button(\n                {\"disabled\": status in {\"empty\", \"submitting\"}},\n                \"Submit\",\n            ),\n            (\n                html.p(\n                    {\"className\": \"Error\"}, \"Good guess but a wrong answer. Try again!\"\n                )\n                if status == \"error\"\n                else None\n            ),\n        ),\n    )\n
.Error {\n    color: red;\n}\n\ntextarea {\n    width: 260px;\n    height: 60px;\n}\n\nsection {\n    border-bottom: 1px solid #ddd;\n    padding-bottom: 10px;\n    margin-bottom: 10px;\n}\n\nh4 {\n    margin: 4px 0;\n    color: #888;\n}\n
# TODO\n

The status prop is a \"mock\" of the state inside the component, and controls which visual states the user will see. This mock is controlled by a prop called status with a default value of \"empty\".

You could call that prop anything you like, the naming is not important. Try changing the default to status=\"success\" to see the success message appear. Mocking lets you quickly iterate on the UI before you wire up any logic.

Two details in that mock are worth pointing out. \"Typing\" and \"Submitting\" render the same layout, differing only in whether the controls are disabled \u2014 so the difference is expressed as data, not as a different branch of markup:

html.button({\"disabled\": status in {\"empty\", \"submitting\"}}, \"Submit\")\n

And the error state deliberately keeps the form enabled. A state that showed an error while also disabling the input would leave the user unable to correct their answer \u2014 imperative code has to enforce that by hand, and it is exactly the kind of thing that gets forgotten.

Displaying many visual states at once

You could try each state by editing the source, but then you'd navigate back and forth many times. It's often easier to show many visual states at once, like a \"living styleguide\" or \"storybook\":

app.pystyles.css Run
@component\ndef form(status=\"empty\"):\n    if status == \"success\":\n        return html.h1(\"That's right!\")\n\n    return html.div(\n        html.h2(\"City quiz\"),\n        html.p(\n            \"In which city is there a billboard that turns air into drinkable water?\"\n        ),\n        html.form(\n            html.textarea({\"disabled\": status == \"submitting\"}),\n            html.br(),\n            html.button(\n                {\"disabled\": status in {\"empty\", \"submitting\"}},\n                \"Submit\",\n            ),\n            (\n                html.p(\n                    {\"className\": \"Error\"}, \"Good guess but a wrong answer. Try again!\"\n                )\n                if status == \"error\"\n                else None\n            ),\n        ),\n    )\n\n\n@component\ndef app():\n    \"\"\"Render every visual state at once -- a \"living styleguide\".\n\n    Because the mock is driven by a prop, all five states can sit side by side\n    in one page and be compared without clicking anything.\n    \"\"\"\n    statuses = [\"empty\", \"typing\", \"submitting\", \"success\", \"error\"]\n    return html.div(\n        [\n            html.section(\n                {\"key\": status},\n                html.h4(status),\n                form(status=status),\n            )\n            for status in statuses\n        ]\n    )\n
.Error {\n    color: red;\n}\n\ntextarea {\n    width: 260px;\n    height: 60px;\n}\n\nsection {\n    border-bottom: 1px solid #ddd;\n    padding-bottom: 10px;\n    margin-bottom: 10px;\n}\n\nh4 {\n    margin: 4px 0;\n    color: #888;\n}\n
# TODO\n

Because the mock is driven by a prop, all five states mount side by side and can be compared without a single click. Each html.section() carries a \"key\" because they are children of a list.

"},{"location":"learn/reacting-to-input-with-state/#step-2-determine-what-triggers-those-state-changes","title":"Step 2: Determine what triggers those state changes","text":"

There are two categories of \"cause\" that can change state in a component:

  • Input (the user typing, clicking, changing a field, and so on)
  • Output (one of the component's effects completing \u2014 a timer firing, a request finishing, and so on)

In both cases, you must set state variables to update the UI.

  • Changing the text input (human) should switch it from the Empty state to the Typing state or back, depending on whether the text box is empty or not.
  • Clicking the Submit button (human) should switch it to the Submitting state.
  • A successful response (computer) should switch it to the Success state.
  • A failed response (computer) should switch it to the Error state with the matching error message.

Notice that human inputs often require event handlers!

"},{"location":"learn/reacting-to-input-with-state/#step-3-represent-the-state-in-memory-with-use_state","title":"Step 3: Represent the state in memory with use_state","text":"

Next you'll need to represent the visual states of your component in memory with use_state.

Simplicity is key: each piece of state is a \"moving piece\", and you want as few \"moving pieces\" as possible. More complexity leads to more bugs!

Start with the state that everyone agrees on:

answer, set_answer = use_state(\"\")\nerror, set_error = use_state(\"\")\n

Then you'll need a state variable representing which of the visual states to display. There's usually more than a single way to represent that in memory, so you'll need to experiment with it. Here's one attempt \u2014 a boolean per state:

is_empty, set_is_empty = use_state(True)\nis_typing, set_is_typing = use_state(False)\nis_submitting, set_is_submitting = use_state(False)\nis_success, set_is_success = use_state(False)\nis_error, set_is_error = use_state(False)\n
"},{"location":"learn/reacting-to-input-with-state/#step-4-remove-any-non-essential-state-variables","title":"Step 4: Remove any non-essential state variables","text":"

You want to avoid duplication in the state content so you're only tracking what is essential. Spending a little time refactoring your state structure will make your components easier to understand, reduce duplication, and avoid unintended meanings. Your goal is to prevent the cases where the state in memory doesn't represent any valid UI that you'd want a user to see.

Take those five booleans:

  • is_typing and is_submitting can't both be True. A paradox usually means the state is not constrained enough. There are four possible combinations of two booleans, but only three correspond to valid states. To remove the \"impossible\" state, combine them into a status that must be one of three values: \"typing\", \"submitting\", or \"success\".
  • Another paradox: is_empty and is_typing can't both be True. Keeping them as separate state variables risks them going out of sync and causing bugs. Fortunately you can drop is_empty and check len(answer) == 0 instead.
  • is_error is not needed, because you can check if error instead.

After this clean-up you're left with 3 (down from 7!) essential state variables:

answer, set_answer = use_state(\"\")\nerror, set_error = use_state(\"\")\nstatus, set_status = use_state(\"typing\")  # 'typing', 'submitting', or 'success'\n

You know they are essential, because you can't remove any of them without breaking the functionality.

Eliminating 'impossible' states with a reducer

These three variables are a good enough representation of this form's state. However, some intermediate states still don't fully make sense. A non-empty error doesn't make sense when status is \"success\". Reducers let you unify multiple state variables into a single object and consolidate all the related logic!

"},{"location":"learn/reacting-to-input-with-state/#step-5-connect-the-event-handlers-to-set-state","title":"Step 5: Connect the event handlers to set state","text":"

Finally, connect the event handlers to the set functions from Step 4:

app.pystyles.css Run
@component\ndef form():\n    answer, set_answer = use_state(\"\")\n    error, set_error = use_state(\"\")\n    status, set_status = use_state(\"typing\")  # 'typing', 'submitting', or 'success'\n\n    if status == \"success\":\n        return html.h1(\"That's right!\")\n\n    async def handle_submit(event):\n        # Awaiting inside the handler is fine: ReactPy still suppresses the\n        # form's default navigation, because it detects the preventDefault()\n        # call by reading this function's bytecode -- whether or not the handler\n        # ever finishes does not matter. The call must appear in this function\n        # itself, though: hiding it in a helper defeats the inspection.\n        event.preventDefault()\n        set_status(\"submitting\")\n        try:\n            await submit_form(answer)\n        except ValueError as exc:\n            set_status(\"typing\")\n            set_error(str(exc))\n        else:\n            set_status(\"success\")\n\n    async def handle_textarea_change(event):\n        set_answer(await event.target.value)\n\n    return html.div(\n        html.h2(\"City quiz\"),\n        html.p(\n            \"In which city is there a billboard that turns air into drinkable water?\"\n        ),\n        html.form(\n            {\"onSubmit\": handle_submit},\n            html.textarea(\n                {\n                    \"value\": answer,\n                    \"onChange\": handle_textarea_change,\n                    \"disabled\": status == \"submitting\",\n                }\n            ),\n            html.br(),\n            html.button(\n                {\n                    \"disabled\": len(answer) == 0 or status == \"submitting\",\n                    \"type\": \"submit\",\n                },\n                \"Submit\",\n            ),\n            html.p({\"className\": \"Error\"}, error) if error else None,\n        ),\n    )\n
.Error {\n    color: red;\n}\n\ntextarea {\n    width: 260px;\n    height: 60px;\n}\n\nsection {\n    border-bottom: 1px solid #ddd;\n    padding-bottom: 10px;\n    margin-bottom: 10px;\n}\n\nh4 {\n    margin: 4px 0;\n    color: #888;\n}\n
# TODO\n

Three things are worth noticing.

The handlers only set state. You never say \"hide the textarea\" or \"enable the button\". The disabled attributes and the error paragraph are computed from status and error on every render, so it's impossible to end up in a state where one was updated and the other wasn't. Submitting a wrong answer really does lock the form, then bring back the form with the error message and the input usable again.

The error is stored as a string, not an exception. React's version keeps an Error object and reads .message off it. In Python, store the message \u2014 str(exc) \u2014 and render it directly.

event.preventDefault() matters here, and ReactPy reads your code to find it. Without it the browser navigates away and your app is gone. Detection happens by inspecting the handler's code rather than at call time, so an async def handler that calls event.preventDefault() before its await is still detected. What defeats it is indirection: rebinding the argument (e = event) or delegating to a helper hides the call from the handler's own code, and the default is not prevented. If you can't put the call in the handler itself, declare it with @event(prevent_default=True).

An await in the handler means an extra paint

Calling set_status(\"submitting\") before await submit_form(answer) is what makes the locked-form state visible at all: ReactPy renders what has been set so far when the handler suspends, so you see the form lock, then the result arrive. That is the behaviour you want here.

It is narrower than React's batching, and will surprise you when you don't want the intermediate paint. See Queueing a Series of State Updates for the measured difference.

Although this code is longer than the original imperative example, it is much less fragile. Expressing all interactions as state changes lets you introduce new visual states later without breaking existing ones. It also lets you change what should be displayed in each state without changing the logic of the interaction itself.

"},{"location":"learn/reacting-to-input-with-state/#recap","title":"Recap","text":"
  • Declarative programming means describing the UI for each visual state rather than micromanaging the UI (imperative).
  • Identify the visual states, and mock them with a prop so all of them can be seen at once.
  • Then:
    • Determine the possible causes \u2014 user inputs and computer outputs.
    • Represent those states with as few state variables as possible, and remove any that can be derived.
    • Connect the event handlers to set state.
"},{"location":"learn/referencing-values-with-refs/","title":"Referencing Values with Refs","text":""},{"location":"learn/referencing-values-with-refs/#overview","title":"Overview","text":"

When you want a component to \"remember\" some information, but you don't want that information to trigger new renders, you can use a ref.

You will learn

  • How to add a ref to your component
  • How to update a ref's value
  • How refs are different from state
  • How to use refs safely
"},{"location":"learn/referencing-values-with-refs/#adding-a-ref-to-your-component","title":"Adding a ref to your component","text":"

You can add a ref to your component by importing the use_ref hook from ReactPy:

from reactpy import use_ref\n

Inside your component, call the use_ref hook and pass the initial value that you want to reference as the only argument. For example, here is a ref to the value 0:

clicks = use_ref(0)\n

use_ref returns a Ref object holding that value:

Ref(0)  # holds the value you passed to use_ref\n

You can access the current value of that ref through the clicks.current attribute. This value is intentionally mutable, meaning you can both read and write to it. It's like a secret pocket of your component that ReactPy doesn't track. (This is what makes it an \"escape hatch\" from ReactPy's one-way data flow \u2014 more on that below!)

Here, a button will increment clicks.current on every click:

app.py Run
@component\ndef counter():\n    clicks = use_ref(0)\n\n    async def handle_click(event):\n        clicks.current += 1\n        # Nothing on screen changes, but the server console proves the ref is\n        # really incrementing between clicks.\n        print(f\"You clicked {clicks.current} times!\")\n\n    return html.button({\"onClick\": handle_click}, \"Click me!\")\n
# TODO\n

The ref points to a number, but, like state, you could point to anything: a string, an object, or even a function. Unlike state, a ref is a plain Python object with a current attribute that you can read and modify.

Note that the component doesn't re-render with every increment. Like state, refs are retained by ReactPy between re-renders. However, setting state re-renders a component. Changing a ref does not! Watch the server console: each click prints a new count, yet the page never redraws.

Pass the initial value

Unlike useRef() in React, use_ref() has no no-argument form \u2014 initial_value is required, so use_ref() fails outright with TypeError: use_ref() missing 1 required positional argument: 'initial_value'. Pass None explicitly when you have no better starting value: tick_task = use_ref(None).

The lower-level reactpy.utils.Ref() is the one that accepts no arguments, and there current starts unset rather than at None, so the first read raises AttributeError: 'Ref' object has no attribute 'current'. Components should call use_ref rather than construct a Ref directly.

"},{"location":"learn/referencing-values-with-refs/#example-building-a-stopwatch","title":"Example: building a stopwatch","text":"

You can combine refs and state in a single component. For example, let's make a stopwatch that the user can start or stop by pressing a button. In order to display how much time has passed since the user pressed \"Start\", you will need to keep track of when the Start button was pressed and what the current time is. This information is used for rendering, so you'll keep it in state:

elapsed, set_elapsed = use_state(0.0)\nrunning, set_running = use_state(False)\n

When the user presses \"Start\", you'll update the elapsed time about ten times a second. ReactPy has no setInterval, but it runs on asyncio, so the equivalent is a coroutine that sleeps and sets state, started with use_async_effect.

Now the awkward part. That coroutine runs for a long time, and it needs to know when the user pressed Start in order to compute time.monotonic() - <start>. It cannot read it from a local variable, because the render that created the coroutine is long gone by the time the coroutine wakes up. Since the start time is not used for rendering directly, you can keep it in a ref:

app.py Run
@component\ndef stopwatch():\n    # What the UI renders lives in state. What only the handlers and the\n    # ticking effect read -- and which must never trigger a render of its own\n    # -- lives in a ref.\n    elapsed, set_elapsed = use_state(0.0)\n    running, set_running = use_state(False)\n    started_at: Ref[float | None] = use_ref(None)\n\n    async def tick():\n        # The ref is the same object on every render, so this long-lived loop\n        # always sees whatever the button handlers wrote into it.\n        while started_at.current is not None:\n            await asyncio.sleep(0.1)\n            set_elapsed(time.monotonic() - started_at.current)\n\n    use_async_effect(tick, dependencies=[running])\n\n    async def start(event=None):\n        started_at.current = time.monotonic()\n        set_running(True)\n\n    async def stop(event=None):\n        if started_at.current is not None:\n            # Write the exact final value; the ticking effect only samples\n            # every 0.1s, so relying on it alone would leave the display\n            # slightly behind.\n            set_elapsed(time.monotonic() - started_at.current)\n            started_at.current = None\n        set_running(False)\n\n    async def reset(event=None):\n        started_at.current = None\n        set_running(False)\n        set_elapsed(0.0)\n\n    return html.div(\n        html.h1(f\"{elapsed:.1f}s\"),\n        html.button(\n            {\"onClick\": stop if running else start},\n            \"Stop\" if running else \"Start\",\n        ),\n        html.button({\"onClick\": reset}, \"Reset\"),\n    )\n
# TODO\n

Press Start and the counter climbs; press Stop and it freezes at the exact final value. The started_at ref is the same object on every render, so the button handlers and the long-lived ticking coroutine all see whatever the others wrote into it.

Note what the ref is not doing here: it is not holding the asyncio.Task. The ticking coroutine is owned by use_async_effect, which cancels it when running flips to False and when the component unmounts. Storing a manually created task in a ref is tempting \u2014 tick_task.current = asyncio.create_task(keep_ticking()) \u2014 but nothing ever cancels it. Measure it by unmounting the component while it runs and the loop keeps rendering forever. Reach for an effect to own background work, and use the ref for the plain values the handlers need to share.

When a piece of information is used for rendering, keep it in state. When a piece of information is only needed by event handlers and changing it doesn't require a re-render, using a ref may be more efficient.

"},{"location":"learn/referencing-values-with-refs/#differences-between-refs-and-state","title":"Differences between refs and state","text":"

Perhaps you're thinking refs seem less \"strict\" than state \u2014 you can mutate them instead of always having to use a state setting function, for instance. But in most cases, you'll want to use state. Refs are an \"escape hatch\" you won't need often. Here's how state and refs compare:

refs state use_ref(initial_value) returns Ref(current=initial_value) use_state(initial_value) returns the current value of a state variable and a state setter function Doesn't trigger re-render when you change it. Triggers re-render when you change it. Mutable \u2014 you can modify and update current outside of the rendering process. \"Immutable\" \u2014 you must use the state setting function to modify state variables to queue a re-render. You shouldn't read (or write) current during rendering. You can read state at any time. However, each render has its own snapshot of state which does not change.

Here is a counter button that's implemented with state:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.button({\"onClick\": handle_click}, f\"You clicked {count} times\")\n
# TODO\n

Because the count value is displayed, it makes sense to use a state value for it. When the counter's value is set with set_count(), ReactPy re-renders the component and the screen updates to reflect the new count.

If you tried to implement this with a ref, ReactPy would never re-render the component, so you'd never see the count change! See how clicking this button does not update its text:

app.py Run
@component\ndef counter():\n    count = use_ref(0)\n\n    async def handle_click(event):\n        # This doesn't re-render the component!\n        count.current += 1\n\n    return html.button({\"onClick\": handle_click}, f\"You clicked {count.current} times\")\n
# TODO\n

This is why reading ref.current during render leads to unreliable code. If you need that, use state instead.

"},{"location":"learn/referencing-values-with-refs/#how-does-use_ref-work-inside","title":"How does use_ref work inside?","text":"

Although both use_state and use_ref are provided by ReactPy, use_ref really is implemented on top of use_state. In reactpy/core/hooks.py:

def use_ref(initial_value):\n    return _use_const(lambda: Ref(initial_value))\n\n\ndef _use_const(function):\n    return HOOK_STACK.current_hook().use_state(function)\n

During the first render, use_ref stores a Ref object as if it were state. That object is retained by ReactPy, so every later render gets the very same object back. There is no setter in the picture because use_ref always needs to return the same object \u2014 which is exactly why it can reuse state machinery without re-rendering.

If you're familiar with object-oriented programming, refs might remind you of instance fields \u2014 but instead of self.something you write something.current.

"},{"location":"learn/referencing-values-with-refs/#when-to-use-refs","title":"When to use refs","text":"

Typically, you will use a ref when your component needs to \"step outside\" ReactPy and communicate with external APIs \u2014 often an API that won't impact the appearance of the component. Here are a few of these rare situations:

  • Storing asyncio tasks or timer handles, as in the stopwatch above
  • Storing other objects that aren't needed to calculate the VDOM \u2014 a client, a cache, a handle from an external library

If your component needs to store some value, but it doesn't impact the rendering logic, choose refs.

A ref cannot hold a DOM node

React's docs list \"accessing a DOM element\" among the top reasons to reach for a ref. That one does not carry over: reactpy.utils.Ref is a plain Python box that lives on the server, and there is no attribute that hands a browser node to Python. What you do instead \u2014 give the element an \"id\" and reach it from inline JavaScript \u2014 uses no ref at all. See Manipulating the DOM with Refs for why the name is a misnomer and what replaces each React example.

"},{"location":"learn/referencing-values-with-refs/#best-practices-for-refs","title":"Best practices for refs","text":"

Following these principles will make your components more predictable:

  • Treat refs as an escape hatch. Refs are useful when you work with external systems. If much of your application logic and data flow relies on refs, you might want to rethink your approach.
  • Don't read or write ref.current during rendering. If some information is needed during rendering, use state instead. Since ReactPy doesn't know when ref.current changes, even reading it while rendering makes your component's behavior difficult to predict. (The only exception to this is code like if not ref.current: ref.current = Thing(), which only sets the ref once during the first render.)

Limitations of ReactPy state don't apply to refs. For example, state acts like a snapshot for every render and doesn't update synchronously. But when you mutate the current value of a ref, it changes immediately:

clicks.current = 5\nprint(clicks.current)  # 5\n

This is because the ref itself is a regular Python object, and so it behaves like one.

You also don't need to worry about avoiding mutation when you work with a ref. As long as the object you're mutating isn't used for rendering, ReactPy doesn't care what you do with the ref or its contents.

"},{"location":"learn/referencing-values-with-refs/#refs-and-the-dom","title":"Refs and the DOM","text":"

You can point a ref to any value \u2014 but not to a DOM element. ReactPy has no ref attribute to pass to a tag, and no server-side handle on a browser node: use_ref lives in the Python process, the node lives in the page. The React idiom input_ref.current.focus() therefore has no Python spelling at all. What replaces it is to give the element an \"id\" and reach it from inline JavaScript, which runs in the page \u2014 and which uses no ref. The trap to avoid is reaching for it from an effect: effects run on the server too, where document does not exist. Manipulating the DOM with Refs walks through focus, scrolling, and play/pause.

"},{"location":"learn/referencing-values-with-refs/#summary","title":"Summary","text":"
  • A ref is a value that lets a component \"remember\" something without triggering a re-render.
  • Call use_ref(initial_value) and read or write ref.current.
  • Setting state re-renders; changing a ref does not, which makes refs suitable for values only event handlers need.
  • Don't read or write ref.current during rendering \u2014 use state for anything that affects what's on screen.
"},{"location":"learn/removing-effect-dependencies/","title":"Removing Effect Dependencies","text":""},{"location":"learn/removing-effect-dependencies/#overview","title":"Overview","text":"

Every dependency you list is a promise: \"when this changes, redo the work.\" Removing one you don't need is fine \u2014 and proving it is not needed is the whole job. This page covers what counts as a change in ReactPy, and the six legitimate reasons a value can leave the list.

You will learn

  • How to prove a value is not a dependency, rather than guess
  • How ReactPy actually compares dependencies \u2014 not what the docstring says
  • The six safe ways to remove a dependency
  • Why a rebuilt object is not \"new\" here, and when that saves you
  • What to do when a value must stay out of the list for real
"},{"location":"learn/removing-effect-dependencies/#dependencies-should-match-the-code","title":"Dependencies should match the code","text":"

The list describes the code, not your intent. Look at the Effect body, list every name it reads that came from the component body, and that is the list. Nothing more, nothing less.

A missing dependency is a correctness bug: the Effect reports values from an older render. An extra dependency is a behaviour bug: the work runs when it shouldn't, tearing down something that should have stayed up. Both are worth fixing, and they are fixed differently.

To remove a dependency, prove one of these about it:

  • it cannot change between renders (module constant, imported helper)
  • it is read only to compute a new value, not to decide what to do
  • it is read for a \"latest value\" snapshot, and reacting to it is unwanted
  • the code that reads it should be in an event handler, not an Effect
  • the Effect is doing two jobs and only one of them needs it
  • it changes unintentionally, and the real fix is to stop it changing

If none of those is true, the value stays. Deleting it from the list to quiet a symptom produces an Effect that looks fine and reports the past.

"},{"location":"learn/removing-effect-dependencies/#a-missing-dependency-is-visible-if-you-look","title":"A missing dependency is visible if you look","text":"

This Effect writes a summary into state but lists only topic:

app.py Run
@component\ndef reporter(topic):\n    count, set_count = use_state(0)\n    theme, set_theme = use_state(\"light\")\n    summary, set_summary = use_state(\"no report yet\")\n\n    def report():\n        # reads `count` and `theme`, but neither is in the dependency list\n        set_summary(f\"topic={topic} count={count} theme={theme}\")\n        print(f\"report topic={topic} count={count} theme={theme}\")\n\n    use_effect(report, dependencies=[topic])\n\n    async def handle_count_click(e):\n        set_count(count + 1)\n\n    async def handle_theme_click(e):\n        set_theme(\"dark\" if theme == \"light\" else \"light\")\n\n    return html.div(\n        html.button({\"onClick\": handle_count_click}, f\"Count {count}\"),\n        html.button(\n            {\"onClick\": handle_theme_click},\n            f\"Theme {theme}\",\n        ),\n        html.p(f\"live: count={count} theme={theme}\"),\n        html.p(f\"summary: {summary}\"),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        reporter(topic),\n    )\n
# TODO\n

Verified: press Count twice and Theme once, and the page reads

live:    count=2 theme=dark\nsummary: topic=news count=0 theme=light\n

The summary describes the render that created the Effect. Press Topic and both lines agree again \u2014 because a new Effect was built from the current render. That pair of lines is the whole missing-dependency bug made visible: it is not frozen at mount, and it is not current. It is from some render.

"},{"location":"learn/removing-effect-dependencies/#how-reactpy-compares-dependencies","title":"How ReactPy compares dependencies","text":"

This is where React's advice and ReactPy's behaviour part ways, and it decides which removals are safe.

Dependencies go through core/hooks.py, strictly_equal:

  1. Type gate first. If type(x) is not type(y), it is a change \u2014 1 and 1.0 never compare equal here.
  2. Local functions compare code. If both values have a __qualname__ containing <locals> or <lambda> and both expose __code__, their co_* bytecode attributes are compared \u2014 same code, and the same __qualname__, means unchanged. One side qualifying is not enough: a locally-defined function compared against an import is not code-compared.
  3. Otherwise ==, but only if the value has an __eq__ at all (hasattr(x, \"__eq__\")), and falling back to is if == raises.

Note step 3. The strictly_equal docstring claims only int, float, str, bytes and friends are compared by equality, but the implementation reaches x == y for anything with an __eq__ \u2014 which is everything. Measured over 5 renders each (mount plus four unrelated state changes), counting how many times the Effect ran:

Dependency, rebuilt on every render Runs {\"sort\": \"date\", \"limit\": 10} \u2014 never changes 1 [1, 2, 3] \u2014 never changes 1 DataFilter(\"a\") (@dataclass) \u2014 content changed twice 3 TupleFilter(\"a\") (NamedTuple) \u2014 content changed twice 3 {\"sort\": \"date\", \"limit\": count} \u2014 content tracks state 3 plain class instance, no __eq__ 5 (every render) (1, 2, 3) alternating with [1, 2, 3] 3 (type gate)

Consequences:

  • React's \"a rebuilt object is a new dependency\" warning does not apply here. A dict or list you rebuild on every render is not a dependency change, so it will not loop. That is why the classic infinite-loop example needs no fix in ReactPy \u2014 and why you must not rely on it either, since it depends on values comparing equal, not on identity.
  • A plain class instance always counts as changed, because it inherits object.__eq__ and identity never holds across renders. That is the shape that genuinely spins.
  • The type gate catches things == would not: strictly_equal(1, True) is False even though 1 == True, and a tuple swapped for a list is a change.

Because ReactPy cannot check your list for you \u2014 reactpy-flake8 reports a missing dependency only for an inline lambda, never for a named def or a decorated Effect \u2014 proving the list is yours to do. See Separating Events from Effects and Lifecycle of Reactive Effects for the linter's exact coverage and for the Layout-based test that catches what it cannot.

"},{"location":"learn/removing-effect-dependencies/#removing-unnecessary-dependencies","title":"Removing unnecessary dependencies","text":""},{"location":"learn/removing-effect-dependencies/#should-this-code-move-to-an-event-handler","title":"Should this code move to an event handler?","text":"

If the work should happen because the user did something, it belongs in the handler, and the dependency disappears along with the Effect. This is the most common removal and the one that deletes the most code \u2014 see Separating Events from Effects.

"},{"location":"learn/removing-effect-dependencies/#is-your-effect-doing-several-unrelated-things","title":"Is your Effect doing several unrelated things?","text":"

One Effect connecting a socket and loading rows means each job inherits the other's dependencies:

app.py Run
@component\ndef feed(topic):\n    page, set_page = use_state(1)\n    rows, set_rows = use_state([])\n    online, set_online = use_state(True)\n\n    def subscribe():\n        # one Effect doing two unrelated jobs: connect AND load rows\n        if not online:\n            print(\"offline \u2014 nothing to do\")\n            return\n        print(f\"connect {topic} page={page}\")\n        set_rows([f\"{topic} row {i} (page {page})\" for i in range(3)])\n\n        def close():\n            print(f\"disconnect {topic} page={page}\")\n\n        return close\n\n    use_effect(subscribe, dependencies=[topic, page, online])\n\n    async def handle_page_click(e):\n        set_page(page + 1)\n\n    async def handle_online_click(e):\n        set_online(not online)\n\n    return html.div(\n        html.button({\"onClick\": handle_page_click}, f\"Page {page}\"),\n        html.button({\"onClick\": handle_online_click}, \"Toggle online\"),\n        html.ul([html.li({\"key\": row}, row) for row in rows]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        feed(topic),\n    )\n
# TODO\n

Verified: press Toggle online and the log shows disconnect news page=2 before offline \u2014 nothing to do. Going offline tears down a connection by way of a page number. Split the Effect in two and each list shrinks to what its own job reads.

"},{"location":"learn/removing-effect-dependencies/#are-you-reading-some-state-to-calculate-the-next-state","title":"Are you reading some state to calculate the next state?","text":"

Then that state is not a dependency \u2014 it is an input to the next value. Take an updater and it leaves the list:

app.py Run
@component\ndef log_viewer(topic):\n    lines, set_lines = use_state([])\n    visible, set_visible = use_state(3)\n\n    def record():\n        # `lines` is only used to build the next value, so it is not a\n        # dependency \u2014 and listing it would make this Effect re-trigger itself\n        def append(current):\n            return [*current, f\"opened {topic}\"]\n\n        set_lines(append)\n\n    use_effect(record, dependencies=[topic])\n\n    async def handle_visible_click(e):\n        set_visible(visible + 1)\n\n    return html.div(\n        html.button({\"onClick\": handle_visible_click}, f\"Show {visible}\"),\n        html.ul([html.li({\"key\": i}, line) for i, line in enumerate(lines[-visible:])]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        log_viewer(topic),\n    )\n
# TODO\n

Verified: each topic change appends exactly one entry; changing Show does not append anything.

Now the reason this rule matters. Listing lines as well \u2014 \"just to be safe\", exactly the mistake the ReactJS page warns about in the other direction \u2014 gives:

def record():\n    def append(current):\n        return [*current, f\"opened {topic}\"]\n\n    set_lines(append)\n\nuse_effect(record, dependencies=[topic, lines])   # never settles\n

Measured: the connection rendered 400 times without settling and the component body ran 800 times. The Effect appends, lines changes, the Effect runs again. A functional updater is not a micro-optimisation here; it is what breaks the cycle.

"},{"location":"learn/removing-effect-dependencies/#do-you-want-to-read-a-value-without-reacting-to-its-changes","title":"Do you want to read a value without reacting to its changes?","text":"

Keep it in a ref that you update during render, and leave it out of the list. The difference is easy to see on a poll loop with a user-adjustable delay.

The version that lists the delay:

app.py Run
@component\ndef poller(topic):\n    delay, set_delay = use_state(0.2)\n    polls, set_polls = use_state(0)\n\n    async def loop():\n        print(f\"start polling {topic} delay={delay:.1f}s\")\n        try:\n            while True:\n                await asyncio.sleep(delay)\n                print(f\"tick {topic} delay={delay:.1f}s\")\n                # the loop is created once per run, so read state via an updater\n                set_polls(lambda current: current + 1)\n        except asyncio.CancelledError:\n            print(f\"stop polling {topic} delay={delay:.1f}s\")\n            raise\n\n    # `delay` here means adjusting it cancels and recreates the whole loop\n    use_async_effect(loop, dependencies=[topic, delay])\n\n    async def handle_delay_click(e):\n        set_delay(delay + 0.2)\n\n    return html.div(\n        html.p(f\"{topic}: {polls} polls\"),\n        html.button({\"onClick\": handle_delay_click}, f\"Delay {delay:.1f}s\"),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        poller(topic),\n    )\n
# TODO\n

The version that keeps it in a ref:

app.py Run
@component\ndef poller(topic):\n    delay, set_delay = use_state(0.2)\n    polls, set_polls = use_state(0)\n    # updated during render, so the loop always sleeps with the newest value\n    delay_ref = use_ref(delay)\n    delay_ref.current = delay\n\n    async def loop():\n        print(f\"start polling {topic}\")\n        try:\n            while True:\n                await asyncio.sleep(delay_ref.current)\n                print(f\"tick {topic} delay={delay_ref.current:.1f}s\")\n                # the loop is created once, so read state through an updater\n                set_polls(lambda current: current + 1)\n        except asyncio.CancelledError:\n            print(f\"stop polling {topic}\")\n            raise\n\n    # `delay` is deliberately absent: adjusting it must not reconnect\n    use_async_effect(loop, dependencies=[topic])\n\n    async def handle_delay_click(e):\n        set_delay(delay + 0.2)\n\n    return html.div(\n        html.p(f\"{topic}: {polls} polls\"),\n        html.button({\"onClick\": handle_delay_click}, f\"Delay {delay:.1f}s\"),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        poller(topic),\n    )\n
# TODO\n

Verified with the same script on both \u2014 two delay adjustments, half a second of ticking, then a topic change:

Loop starts Loop stops delay in the list 4 2 delay in a ref 2 1

Two of those restarts are pure waste: adjusting a setting nobody asked to reconnect for. And because the ref is assigned during render, the loop still sleeps with the newest delay \u2014 tick news delay=0.6s appears in the log of the ref version even though the Effect never re-ran.

The ref assignment during render looks like a write in a render, and it is the one case that is safe: the box is created once, the write is idempotent, and nothing derived from it feeds the render output.

"},{"location":"learn/removing-effect-dependencies/#does-some-reactive-value-change-unintentionally","title":"Does some reactive value change unintentionally?","text":"

Sometimes the dependency is correct and the value is the problem.

Move static values out of the component. A module-level constant, an imported helper, or a frozen config is the same object on every render and every connection, so it is not a dependency at all:

app.py Run
PAGE_SIZE = 20\nHEADERS = {\"Accept\": \"application/json\"}\n\n\ndef format_rows(topic, raw):\n    \"\"\"Module-level helper: same object on every render and every connection.\"\"\"\n    return [f\"{topic}: {name}\" for name in raw]\n\n\n@component\ndef directory(topic):\n    query, set_query = use_state(\"\")\n    rows, set_rows = use_state([])\n\n    def load():\n        raw = [f\"user-{i}\" for i in range(PAGE_SIZE // 4)]\n        print(f\"load topic={topic} page_size={PAGE_SIZE} headers={HEADERS}\")\n        set_rows(format_rows(topic, raw))\n\n    # neither PAGE_SIZE, HEADERS, nor format_rows belongs in this list\n    use_effect(load, dependencies=[topic])\n\n    async def handle_query_change(e):\n        set_query(await e.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": query,\n                \"onChange\": handle_query_change,\n            }\n        ),\n        html.p(f\"query={query!r} rows={len(rows)}\"),\n        html.ul([html.li({\"key\": row}, row) for row in rows]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        directory(topic),\n    )\n
# TODO\n

Verified: typing in the box re-renders the component and the Effect does not run; only the topic change logs load topic=newsx page_size=20 headers={'Accept': 'application/json'}.

Read primitive values out of objects. user.id is a dependency; user is a wider promise than you need, and any unrelated field change re-runs the Effect. Depend on the scalars you actually use.

Calculate primitives from functions, not objects. This is also the fix for the trap that silently freezes an Effect: a locally defined function passed as a dependency compares code-wise and therefore never changes. Depend on the string or number the function would have produced.

"},{"location":"learn/removing-effect-dependencies/#rebuilding-an-object-is-not-a-dependency-change-but-dont-rely-on-it","title":"Rebuilding an object is not a dependency change \u2014 but don't rely on it","text":"app.py Run
@component\ndef feed(topic):\n    page, set_page = use_state(1)\n    rows, set_rows = use_state([])\n\n    # rebuilt on every render, but compares equal to the previous one\n    options = {\"topic\": topic, \"per_page\": 20}\n\n    def load():\n        print(f\"loading {options} page={page}\")\n        set_rows([f\"{topic} row {i} (page {page})\" for i in range(3)])\n\n    use_effect(load, dependencies=[options, page])\n\n    async def handle_page_click(e):\n        set_page(page + 1)\n\n    return html.div(\n        html.button({\"onClick\": handle_page_click}, f\"Page {page}\"),\n        html.ul([html.li({\"key\": row}, row) for row in rows]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n    refreshes, set_refreshes = use_state(0)\n\n    async def handle_refreshes_click(e):\n        set_refreshes(refreshes + 1)\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_refreshes_click},\n            f\"Re-render ({refreshes})\",\n        ),\n        feed(topic),\n    )\n
# TODO\n

Verified: press Re-render three times, which rebuilds options every time \u2014 the Effect runs zero extra times. Press Page and it runs, because page is a real dependency.

It is useful to know this: query-option dicts and filter objects do not have to be use_memo-ed to avoid a reconnect storm. But it is a behaviour, not a contract. It holds only while your values compare equal, and it silently inverts for a plain class instance (5 runs out of 5 renders above). Write the list you mean, and let equality be a bonus.

"},{"location":"learn/removing-effect-dependencies/#when-a-dependency-must-come-out","title":"When a dependency must come out","text":"

There are cases where the honest answer is \"I read this value, I do not want to react to it, and a ref is the wrong shape for it.\" Then be explicit about the exception rather than quietly deleting the name:

def effect():\n    notify(user_id, channel_id)   # reads channel_id\n\nuse_effect(effect, dependencies=[user_id])\n# channel_id is read but not reacted to: it changes only when the user\n# switches channel, which unmounts this component anyway.\n

The comment is the review surface. An unexplained missing name is indistinguishable from the bug in the first example on this page \u2014 and, as measured, nothing in ReactPy will tell you which one you wrote.

What dependencies=None does not mean

None is not \"no dependencies\"; it means re-run after every render. To create the Effect once and never replace it, use dependencies=[] \u2014 which also seals the body against future renders. See the four-way table in Lifecycle of Reactive Effects.

"},{"location":"learn/removing-effect-dependencies/#recap","title":"Recap","text":"
  • The list describes the code. Prove a value is not a dependency; don't vote it off.
  • ReactPy compares dependencies with strictly_equal: type gate, then code comparison for local functions, then ==. Rebuilt dicts and lists are not changes; plain class instances always are.
  • A missing dependency is silent \u2014 no warning, no log. Make the staleness visible in the page, as the first example does, or assert it in a test.
  • The six removals: hoist what cannot change, take an updater for next-state reads, keep latest-value reads in a ref, move interaction logic to handlers, split Effects that do two jobs, and stop values changing unintentionally.
  • Listing state you only append to is the classic self-triggering loop: measured, 400 renders and no settle.
  • reactpy-flake8 will not check the list for you, so an explanatory comment is the only reviewer you have.
"},{"location":"learn/removing-effect-dependencies/#related","title":"Related","text":"
  • Lifecycle of Reactive Effects \u2014 dependency semantics, the four settings, and silent Effect failures
  • Synchronizing with Effects \u2014 the patterns that genuinely need an Effect
  • Separating Events from Effects \u2014 moving interaction logic into handlers
  • You Might Not Need an Effect \u2014 logic that belongs in the render
  • Use Effect \u2014 parameter-level reference
"},{"location":"learn/render-and-commit/","title":"Render and Commit","text":""},{"location":"learn/render-and-commit/#overview","title":"Overview","text":"

Before your components are displayed on screen, they must be rendered by ReactPy. Understanding the steps in this process will help you think about how your code executes and explain its behavior.

You will learn

  • What rendering means in ReactPy
  • When and why ReactPy renders a component
  • The steps involved in displaying a component on screen
  • Why rendering does not always produce a DOM update

A ReactPy component is a Python function that returns a description of the UI. That description has to travel from your server to someone else's browser before it becomes pixels. The journey has four steps:

  1. Triggering a render \u2014 mounting the app, or updating state.
  2. Rendering the component \u2014 Python calls your functions and builds a VDOM tree.
  3. Committing to the DOM \u2014 the diff is sent over the websocket and patched into the page.
  4. Painting \u2014 the browser redraws.

The first two happen in Python, on your server. The last two happen in the browser. Keeping that boundary in mind explains a lot of ReactPy's behavior that differs from a browser-only React app \u2014 most obviously the round-trip latency on every interaction.

"},{"location":"learn/render-and-commit/#step-1-trigger-a-render","title":"Step 1: Trigger a render","text":"

There are two reasons for a component to render:

  1. It's the component's initial render, caused by mounting it.
  2. The component's (or one of its ancestors') state has been updated.
"},{"location":"learn/render-and-commit/#initial-render","title":"Initial render","text":"

When your app starts, you need to mount a root component. Unlike React, where you call createRoot yourself, in ReactPy the executor does the mounting, and which executor you pick determines what the mounting line looks like:

app.py Run
@component\ndef image():\n    return html.img(\n        {\n            \"src\": \"https://i.imgur.com/ZF6s192.jpg\",\n            \"alt\": \"'Floralis Gen\u00e9rica' by Eduardo Catalano: a gigantic \"\n            \"metallic flower sculpture with reflective petals\",\n        }\n    )\n\n\n# Mounting is what triggers the initial render. Which line you use depends on\n# how the app is served; see the executors reference.\nmy_app = ReactPy(image)\n
# TODO\n

ReactPy(image) is the line that matters. It registers image as the root component and, when a browser connects, calls it and sends the result down. There is no DOM node to hand in \u2014 the executor owns the mount point (<div id=\"app\"> in the page it generates).

The other executors mount differently: ReactPyMiddleware takes dotted paths to components so a URL can select one, and ReactPyCsr mounts a component from a Python file that the browser runs itself. See Creating a Standalone React App for those.

"},{"location":"learn/render-and-commit/#re-renders-when-state-updates","title":"Re-renders when state updates","text":"

Once the component has been initially rendered, you can trigger further renders by updating its state with the set function from use_state. Updating your component's state automatically queues a render.

app.py Run
@component\ndef clock():\n    now, set_now = use_state(datetime.now(timezone.utc))\n\n    async def tick():\n        while True:\n            await asyncio.sleep(1)\n            set_now(datetime.now(timezone.utc))\n\n    use_async_effect(tick, dependencies=[])\n\n    return html.h1(now.strftime(\"%H:%M:%S\"))\n
# TODO\n

Note the difference from a browser-only React app: the state lives on the server. Calling set_now(...) schedules a render on the server, and the new VDOM reaches the browser a websocket message later. That is why an interaction in ReactPy feels slightly laggy compared to the same UI written in JavaScript, and why holding a lock or sleeping inside an event handler stalls the whole connection.

Setting the same value does not re-render

Calling a setter with a value equal to the current one renders nothing \u2014 verified: set_n(0) when n is already 0 produces no further render at all, while set_n(n + 1) does. The comparison is not an identity check: it accepts anything that compares equal, so even a freshly built list or dict schedules nothing when its contents match. Note this is a state-update bailout only; it is not component memoization \u2014 see step 2.

"},{"location":"learn/render-and-commit/#step-2-reactpy-renders-your-components","title":"Step 2: ReactPy renders your components","text":"

After you trigger a render, ReactPy calls your components to figure out what to display. \"Rendering\" is ReactPy calling your functions.

  • On initial render, ReactPy calls the root component.
  • For subsequent renders, ReactPy calls the function component whose state update triggered the render.

This process is recursive: if the updated component returns some other component, ReactPy renders that component next, and if that component returns yet another, ReactPy renders that one too. In this way, a state update inside a component can trigger renders all the way down its children.

Under the hood, Layout._render_component compares the new component against the previous one at the same position. Two things matter here, and both are worth internalizing:

  • A component is matched by its function object identity. If you define a @component inside another component, it is a brand-new function object on every render, so ReactPy unmounts the old one and loses all its state. Always define components at module level.
  • Children are matched by key, falling back to position. See Keeping list items in order with key.

Every child re-renders; there is no memo

ReactPy has no component-level memo. A child function runs again whenever its parent renders, even when its props are identical \u2014 verified by instrumenting a sibling: it re-ran on the parent's state update. use_memo caches values inside a component, not whole component subtrees.

This makes the \"don't nest component definitions\" rule above more than a style preference, and it means expensive children stay expensive. If a subtree is genuinely costly, the tools are use_memo for the computation and moving state down so fewer components re-render \u2014 not wrapping the child.

ReactPy will not render twice to catch you

React's Strict Mode double-invokes components in development to expose impure rendering. ReactPy has no Strict Mode and never double-invokes. A component that mutates module state or reads the clock will render perfectly once and misbehave later. If you want that safety net, write the test: render the component twice inside a reactpy.core.layout.Layout and assert the two results are equal.

"},{"location":"learn/render-and-commit/#step-3-commit-to-the-dom","title":"Step 3: Commit to the DOM","text":"

After a component renders, ReactPy has a Python description of the UI. This is not yet on the screen \u2014 ReactPy must deliver it to the browser, which must then update the DOM:

  1. Update the DOM. ReactPy's client applies the changes to existing DOM nodes, patching rather than replacing wherever it can.
  2. Display the DOM. The browser updates the screen to reflect the changes.

The first half happens on the server: ReactPy walks the new tree, computes a diff, and sends it over the websocket as a path plus a partial model. The second half happens in the browser, where the bundled client patches that subtree in place.

This is where ReactPy is most different from React, and it's the most useful thing to understand. React re-renders from the root and diffs the whole tree; ReactPy sends a patch addressed to one path in the tree. Observed directly: after clicking a button inside the first of two children, the update carried the path \"/children/0/children/0\" and a model describing only that button's subtree. Nothing about the untouched sibling went over the wire.

Because the patch is applied to existing nodes rather than rebuilt, DOM state that ReactPy doesn't know about survives. Verified in a browser: an <input> with no value attribute kept both its typed text and its focus across a parent re-render, and was the same node object before and after.

That is the practical payoff of the \"controlled vs uncontrolled\" distinction:

app.py Run
@component\ndef app():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.button({\"onClick\": handle_count_click}, \"Re-render\"),\n        html.p(f\"Rendered {count} times\"),\n        # No `value`: the browser owns this field, so ReactPy never rewrites it.\n        html.input({\"placeholder\": \"Type here and then click the button\"}),\n    )\n
# TODO\n

Type into the field, then click Re-render. The text stays. ReactPy re-runs app(), produces a new tree, and the diff for the untouched input is empty \u2014 so the browser never touches it, and neither the user's text nor their cursor position is disturbed.

Had that input carried \"value\": something, ReactPy would own it and rewrite it on every render, which is what you want when the value is genuinely derived from state \u2014 and what makes typing feel broken when you forget the matching onChange.

You can skip the browser entirely

Because rendering is pure Python, the same components can be rendered to an HTML string with reactpy_to_string from reactpy.utils \u2014 no browser, no websocket. That is useful for email bodies, static fragments, and tests. It is not server-side rendering of an app: it calls component.render() directly, with no Layout behind it, so a component that uses any hook raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 including the use_state example further up this page. Plain html.* elements and hook-free components are all it can render. And it is only safe because rendering is pure: a component that reads the clock or mutates a global will leak one request's output into another's.

"},{"location":"learn/render-and-commit/#epilogue-browser-paint","title":"Epilogue: Browser paint","text":"

After ReactPy has patched the DOM, the browser repaints the screen. Although this process is known as \"browser rendering\", we'll refer to it as \"painting\" to avoid confusion throughout the docs.

Because a round trip sits between the click and the paint, ReactPy apps feel more like a networked app than a local one. Two habits follow from that: keep event handlers short (they run on the server, blocking that connection), and prefer optimistic UI where a wrong guess is cheap.

"},{"location":"learn/render-and-commit/#recap","title":"Recap","text":"
  • Any screen update in a ReactPy app happens in four steps:
    1. Trigger
    2. Render (Python, on the server)
    3. Commit (a path-addressed patch over the websocket)
    4. Paint (the browser)
  • Mounting a root component triggers the initial render; the executor decides how.
  • Setting state queues a render; setting it to an equal value queues nothing.
  • Children always re-run \u2014 there is no memo, so keep definitions at module level and expensive work in use_memo.
  • ReactPy has no Strict Mode, so nothing will double-render your components to expose impurity for you.
  • Untouched parts of the tree are neither recomputed for the wire nor rewritten in the DOM, which is why an uncontrolled input keeps what the user typed.
"},{"location":"learn/rendering-lists/","title":"Rendering Lists","text":""},{"location":"learn/rendering-lists/#overview","title":"Overview","text":"

You will often want to display multiple similar components from a collection of data. You can use Python's list comprehensions to manipulate a list of data. On this page, you'll use comprehensions with ReactPy to filter and transform your data into lists of components.

You will learn

  • How to render components from a list using a comprehension
  • How to render only specific components using a filtering condition
  • When and why to use ReactPy keys
"},{"location":"learn/rendering-lists/#rendering-data-from-lists","title":"Rendering data from lists","text":"

Say that you have a list of content.

html.ul(\n    html.li(\"Creola Katherine Johnson: mathematician\"),\n    html.li(\"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez: chemist\"),\n    html.li(\"Mohammad Abdus Salam: physicist\"),\n    html.li(\"Percy Lavon Julian: chemist\"),\n    html.li(\"Subrahmanyan Chandrasekhar: astrophysicist\"),\n)\n

The only difference among those list items is their contents, their data. You will often need to show several instances of the same component using different data when building interfaces: from lists of comments to galleries of profile images. In these situations, you can store that data in Python dictionaries and lists and use comprehensions to render lists of components from them.

Here's a short example of how to generate a list of items from a list:

  1. Move the data into a list:
people = [\n    \"Creola Katherine Johnson: mathematician\",\n    \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez: chemist\",\n    \"Mohammad Abdus Salam: physicist\",\n    \"Percy Lavon Julian: chemist\",\n    \"Subrahmanyan Chandrasekhar: astrophysicist\",\n]\n
  1. Build a new list of VDOM nodes from people:
list_items = [html.li(person) for person in people]\n
  1. Return list_items from your component wrapped in a <ul>:
return html.ul(list_items)\n

Here is the result:

app.pystyles.css Run
people = [\n    \"Creola Katherine Johnson: mathematician\",\n    \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez: chemist\",\n    \"Mohammad Abdus Salam: physicist\",\n    \"Percy Lavon Julian: chemist\",\n    \"Subrahmanyan Chandrasekhar: astrophysicist\",\n]\n\n\n@component\ndef people_list():\n    list_items = [html.li(person) for person in people]\n    return html.ul(list_items)\n
li {\n    margin-bottom: 10px;\n}\n
# TODO\n

If you run this with REACTPY_DEBUG=1, ReactPy prints a warning for each child:

Key not specified for child in list row(7f2c11ac75e0, person={...})\n

You'll learn how to fix this warning later on this page. Before we get to that, let's add some structure to your data.

"},{"location":"learn/rendering-lists/#filtering-lists-of-items","title":"Filtering lists of items","text":"

This data can be structured even more. Rather than strings, each person becomes a dictionary:

people = [\n    {\n        \"id\": 0,\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    # ...\n]\n

Say you want to display only the chemists. In JavaScript React you would chain filter() and map(); in Python a single comprehension with an if clause does both:

chemists = [person for person in people if person[\"profession\"] == \"chemist\"]\nlist_items = [html.li(person[\"name\"]) for person in chemists]\n
app.pydata.pystyles.css Run
@component\ndef people_list():\n    chemists = [person for person in people if person[\"profession\"] == \"chemist\"]\n    list_items = [\n        html.li(\n            {\"key\": person[\"id\"]},\n            html.img({\"src\": get_image_url(person), \"alt\": person[\"name\"]}),\n            html.p(\n                html.b(person[\"name\"]),\n                f\" {person['profession']} known for {person['accomplishment']}\",\n            ),\n        )\n        for person in chemists\n    ]\n    return html.ul(list_items)\n
people = [\n    {\n        \"id\": 0,  # used as the key\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,  # used as the key\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"discovery of Arctic ozone hole\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,  # used as the key\n        \"name\": \"Mohammad Abdus Salam\",\n        \"profession\": \"physicist\",\n        \"accomplishment\": \"electromagnetism theory\",\n        \"image_id\": \"bE7W1ji\",\n    },\n    {\n        \"id\": 3,  # used as the key\n        \"name\": \"Percy Lavon Julian\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"pioneering cortisone drugs, steroids and birth control pills\",\n        \"image_id\": \"IOjWm71\",\n    },\n    {\n        \"id\": 4,  # used as the key\n        \"name\": \"Subrahmanyan Chandrasekhar\",\n        \"profession\": \"astrophysicist\",\n        \"accomplishment\": \"white dwarf star mass calculations\",\n        \"image_id\": \"lrWQx8l\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}s.jpg\"\n
ul {\n    list-style-type: none;\n    padding: 0 10px;\n}\n\nli {\n    margin-bottom: 10px;\n    display: grid;\n    grid-template-columns: auto 1fr;\n    gap: 20px;\n    align-items: center;\n}\n\nimg {\n    width: 100px;\n    height: 100px;\n    border-radius: 50%;\n}\n
# TODO\n

A helper that forgets return renders nothing

The JavaScript \"=> with a block body needs a return\" trap has a direct Python twin that is worse, because Python fails silently. If a helper used inside a comprehension forgets its return, it evaluates to None \u2014 and ReactPy skips None children instead of raising:

def render_person(person):  # WRONG: no return\n    html.li(person[\"name\"])\n\n\nlist_items = [render_person(person) for person in people]\n

list_items becomes a list of None values and the <ul> renders empty, with no error. Use a comprehension directly when the expression is simple, and add a -> VdomDict return annotation to helpers so type checkers catch the omission.

"},{"location":"learn/rendering-lists/#keeping-list-items-in-order-with-key","title":"Keeping list items in order with key","text":"

When you render children from a list, ReactPy needs a way to tell which child is which. Give each item a key \u2014 a string or a number that uniquely identifies it among the other items in that list. Unlike React, where key is a special JSX attribute, in ReactPy it lives in the attributes dictionary like any other attribute:

html.li({\"key\": person[\"id\"]}, ...)\n

Children built from a list always need keys

Any child that comes out of a comprehension \u2014 or any other list \u2014 should carry a key. ReactPy does not warn about this by default; the check is part of its debug mode, so run your app with REACTPY_DEBUG=1 (or set reactpy.config.REACTPY_DEBUG.current = True) while developing to surface them.

Keys tell ReactPy which list item each component corresponds to, so that it can match them up later. This becomes important if your list items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen key helps ReactPy infer what exactly has happened, and make the correct updates to the DOM tree.

Rather than generating keys on the fly, you should include them in your data:

app.pydata.pystyles.css Run
@component\ndef people_list():\n    list_items = [\n        html.li(\n            {\"key\": person[\"id\"]},\n            html.img({\"src\": get_image_url(person), \"alt\": person[\"name\"]}),\n            html.p(\n                html.b(person[\"name\"]),\n                f\" {person['profession']} known for {person['accomplishment']}\",\n            ),\n        )\n        for person in people\n    ]\n    return html.ul(list_items)\n
people = [\n    {\n        \"id\": 0,  # used as the key\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,  # used as the key\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"discovery of Arctic ozone hole\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,  # used as the key\n        \"name\": \"Mohammad Abdus Salam\",\n        \"profession\": \"physicist\",\n        \"accomplishment\": \"electromagnetism theory\",\n        \"image_id\": \"bE7W1ji\",\n    },\n    {\n        \"id\": 3,  # used as the key\n        \"name\": \"Percy Lavon Julian\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"pioneering cortisone drugs, steroids and birth control pills\",\n        \"image_id\": \"IOjWm71\",\n    },\n    {\n        \"id\": 4,  # used as the key\n        \"name\": \"Subrahmanyan Chandrasekhar\",\n        \"profession\": \"astrophysicist\",\n        \"accomplishment\": \"white dwarf star mass calculations\",\n        \"image_id\": \"lrWQx8l\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}s.jpg\"\n
ul {\n    list-style-type: none;\n    padding: 0 10px;\n}\n\nli {\n    margin-bottom: 10px;\n    display: grid;\n    grid-template-columns: auto 1fr;\n    gap: 20px;\n    align-items: center;\n}\n\nimg {\n    width: 100px;\n    height: 100px;\n    border-radius: 50%;\n}\n
# TODO\n
"},{"location":"learn/rendering-lists/#displaying-several-dom-nodes-for-each-list-item","title":"Displaying several DOM nodes for each list item","text":"

What do you do when each item needs to render not one, but several DOM nodes?

A plain html(...) fragment takes no positional attributes, but it does accept an attributes dictionary \u2014 so pass the key there:

[\n    html(\n        {\"key\": person[\"id\"]},\n        html.h1(person[\"name\"]),\n        html.p(person[\"profession\"]),\n    )\n    for person in people\n]\n
app.pydata.py Run
@component\ndef people_list():\n    list_items = [\n        html(\n            {\"key\": person[\"id\"]},\n            html.h1(person[\"name\"]),\n            html.p(person[\"profession\"]),\n        )\n        for person in people\n    ]\n    return html.div(list_items)\n
people = [\n    {\n        \"id\": 0,  # used as the key\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,  # used as the key\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"discovery of Arctic ozone hole\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,  # used as the key\n        \"name\": \"Mohammad Abdus Salam\",\n        \"profession\": \"physicist\",\n        \"accomplishment\": \"electromagnetism theory\",\n        \"image_id\": \"bE7W1ji\",\n    },\n    {\n        \"id\": 3,  # used as the key\n        \"name\": \"Percy Lavon Julian\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"pioneering cortisone drugs, steroids and birth control pills\",\n        \"image_id\": \"IOjWm71\",\n    },\n    {\n        \"id\": 4,  # used as the key\n        \"name\": \"Subrahmanyan Chandrasekhar\",\n        \"profession\": \"astrophysicist\",\n        \"accomplishment\": \"white dwarf star mass calculations\",\n        \"image_id\": \"lrWQx8l\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}s.jpg\"\n
# TODO\n

Fragments disappear from the DOM, so this produces a flat list of <h1>, <p>, <h1>, <p>, and so on.

"},{"location":"learn/rendering-lists/#where-to-get-your-key","title":"Where to get your key","text":"

Different sources of data provide different sources of keys:

  • Data from a database: If your data is coming from a database, you can use the database keys/IDs, which are unique by nature.
  • Locally generated data: If your data is generated and persisted locally (e.g. notes in a note-taking app), use an incrementing counter from itertools.count or a UUID from uuid.uuid4 when creating the item \u2014 never while rendering.
"},{"location":"learn/rendering-lists/#rules-of-keys","title":"Rules of keys","text":"
  • Keys must be unique among siblings. However, it's okay to use the same keys for nodes in different lists.
  • Keys must not change or that defeats their purpose! Don't generate them while rendering.

Duplicate keys are a hard error in ReactPy

React logs a warning for duplicate keys and carries on. ReactPy raises instead:

ValueError: Duplicate keys [1] at '/children/0'\n

The whole component fails to render, so a data set with repeated IDs takes down the section rather than degrading it. If your data can legitimately repeat \u2014 for example, two rows that render the same underlying record \u2014 make the key unique among siblings by combining the fields that distinguish them.

"},{"location":"learn/rendering-lists/#why-does-reactpy-need-keys","title":"Why does ReactPy need keys?","text":"

Imagine that files on your desktop didn't have names. Instead, you'd refer to them by their order \u2014 the first file, the second file, and so on. You could get used to it, but once you delete a file, it would get confusing. The second file would become the first file, the third file would be the second file, and so on.

File names in a folder and keys in a list serve a similar purpose. They let us uniquely identify an item between its siblings. A well-chosen key provides more information than the position within the list. Even if the position changes due to reordering, the key lets ReactPy identify the item throughout its lifetime.

This is observable. Give each row its own use_state seeded from the person it renders, then reverse the list. With keys, every row's state travels with its person. Without keys, the state stays behind in the old slot:

keyed=True\n   before: shown=ada   state=ada#0\n   after : shown=alan  state=alan#2\n\nkeyed=False\n   before: shown=ada   state=ada#0\n   after : shown=alan  state=ada#0    # the row for \"alan\" inherited ada's state\n

Index-based keys are a trap

You might be tempted to use an item's index in the list as its key. In fact, that's effectively what ReactPy uses if you don't specify a key at all. But the order in which you render items will change over time if an item is inserted, deleted, or if the list gets reordered, and \u2014 as the output above shows \u2014 state then attaches to the wrong item.

Similarly, do not generate keys on the fly, e.g. with key=uuid.uuid4() or key=id(person) inside the render. Keys that differ on every render never match up, so every component and DOM node is recreated each time. Not only is this slow, it will also lose any user input inside the list items. Instead, use a stable ID that comes from the data.

Note that your component never receives key as a prop \u2014 ReactPy reserves it. Declaring a key parameter raises immediately:

TypeError: Component render function <function row ...> uses reserved parameter 'key'\n

If your component needs an ID, pass it as a separate prop: profile(id=id, key=id).

Pass a list, not a generator

html.ul(html.li(person) for person in people) renders, but a generator has no length, so ReactPy cannot verify key integrity and warns about it in debug mode. Bracket the comprehension so the children are a real list: html.ul([html.li(person) for person in people]).

"},{"location":"learn/rendering-lists/#recap","title":"Recap","text":"

On this page you learned:

  • How to move data out of components and into data structures like lists and dictionaries.
  • How to generate sets of similar components with a list comprehension.
  • How to create filtered lists with an if clause in a comprehension.
  • Why and how to set key on each component in a collection so ReactPy can keep track of each of them even if their position or data changes.
  • That ReactPy checks keys only in debug mode, treats duplicate keys as an error, and reserves key so components can never receive it as a prop.
"},{"location":"learn/responding-to-events/","title":"Responding to Events","text":""},{"location":"learn/responding-to-events/#overview","title":"Overview","text":"

ReactPy lets you add event handlers to your VDOM. Event handlers are your own functions that will be triggered in response to interactions like clicking, hovering, focusing form inputs, and so on.

You will learn

  • Different ways to write an event handler
  • How to pass event handling logic from a parent component
  • How events propagate and how to stop them
  • What the event object actually contains
"},{"location":"learn/responding-to-events/#adding-event-handlers","title":"Adding event handlers","text":"

To add an event handler, you first define a function and then pass it as a prop to the appropriate tag. For example, here is a button that doesn't do anything yet:

app.py Run
@component\ndef button():\n    return html.button(\"I don't do anything\")\n
# TODO\n

You can make it show a message when a user clicks by following these three steps:

  1. Declare a function called handle_click inside your def button() component.
  2. Implement the logic inside that function (use print to show the message).
  3. Add \"onClick\": handle_click to the html.button attributes dictionary.
app.pystyles.css Run
from reactpy import component, html\n\n\n@component\ndef button():\n    async def handle_click(event):\n        print(\"You clicked me!\")\n\n    return html.button({\"onClick\": handle_click}, \"Click me\")\n
button {\n    margin-right: 10px;\n}\n
# TODO\n

You defined the handle_click function and then passed it as a prop to html.button. handle_click is an event handler. Event handler functions:

  • Are usually defined inside your components.
  • Have names that start with handle, followed by the name of the event.

By convention, it is common to name event handlers as handle followed by the event name. You'll often see \"onKeyDown\": handle_key_down, \"onMouseEnter\": handle_mouse_enter, and so on.

The handler key is camelCase, and it lives in the attributes dict

This is the single most common mistake when moving from ReactJS examples. The key is the DOM event name in camelCase \u2014 \"onClick\", \"onChange\", \"onSubmit\" \u2014 and it goes inside the attributes dictionary, not as a keyword argument:

html.button({\"onClick\": handle_click}, \"Click me\")   # correct\n

Writing html.button({\"on_click\": handle_click}, ...) does not raise. ReactPy ships it to the browser verbatim as a handler for an event called _click \u2014 the client registers a listener for the key with the leading on stripped \u2014 and no browser ever fires one. The button looks fine and simply never responds. Only the names you define (variables, components, component props) are snake_case.

Note what does and does not decide \"this is a handler\". In core/vdom.py, separate_attributes_handlers_and_inline_javascript the test is callable(v) on the value, checked before the key is ever looked at: a callable under \"on_click\", or even under \"foo\", lands in eventHandlers. That sort step is also where the coroutine requirement is enforced, because it builds an EventHandler for the value \u2014 so any callable which cannot be awaited raises TypeError at element-build time, whatever key it sits under. The key pattern ^on[A-Z]\\w+ (core/vdom.py, EVENT_ATTRIBUTE_PATTERN) is used only for the string case below \u2014 it decides whether a string becomes inlineJavaScript or a plain attribute. So {\"onClick\": \"alert(1)\"} runs as JavaScript, while {\"onclick\": \"alert(1)\"} and {\"on_click\": \"alert(1)\"} become inert attributes.

An event handler has to be declared with async def, so it cannot be written inline:

async def handle_click(event):\n    print(\"You clicked me!\")\n\n\nhtml.button({\"onClick\": handle_click}, \"Click me\")\n

A handler must be a coroutine \u2014 so a lambda never works

The first rule of an event handler is that it is declared async def. ReactPy checks it the moment the element is built, and refuses anything else:

TypeError: handle_click cannot be used as an event handler because it is not a coroutine.\nEvent handlers must be declared with 'async def':\n\n    async def on_click(event):\n        ...\n

This is not ceremony. Reading the object an event fired from means waiting on the browser, and only a coroutine can await that round trip without stalling the layout \u2014 a blocking read would have to run off-loop, which is the one thread where set_state() fails with no running event loop.

The consequence for a lambda is direct: a lambda can never be a coroutine, so {\"onClick\": lambda event: ...} raises. Name the handler instead. Passing a state setter directly ({\"onClick\": set_count}) is likewise refused \u2014 a setter is not a handler, and the TypeError says so and shows you the fix.

What is accepted: a plain async def, a callable object whose __call__ is a coroutine, and a functools.partial or functools.wraps wrapper around either \u2014 _unwrap_handler in core/events.py peels partials and then runs inspect.unwrap before asking whether the result is a coroutine.

A string is not a handler \u2014 it is JavaScript

ReactPy has a second, easy-to-miss form. If the value of an on* key is a string, ReactPy treats it as JavaScript to run in the browser instead of calling your Python code:

html.button({\"onClick\": \"alert('You clicked me!')\"}, \"Click me\")\n

That renders into an inlineJavaScript field on the VDOM node rather than an eventHandlers field, and nothing on the server ever runs. It is a real escape hatch for things only the browser can do, but it is never what you want when you meant to write a Python function. If your handler silently does nothing, check whether you passed a string.

Pass the function, don't call it

Functions passed to event handlers must be passed, not called:

html.button({\"onClick\": handle_click}, \"...\")    # correct: fires on click\nhtml.button({\"onClick\": handle_click()}, \"...\")  # wrong: fires during render\n

In the second case the () calls handle_click immediately during rendering, without any clicks \u2014 because the Python inside the call is evaluated right away, exactly like an expression inside JSX braces. Whatever it returns is what gets stored. Handlers are picked out by callable(v), so a None return is not a handler: it falls through to the plain attributes and ships as {\"onClick\": None}. The client never turns that into a DOM attribute \u2014 a value under an on* name is consumed as a listener, and a falsy one means \"register nothing\". Nothing fires, and the button renders and does nothing, forever.

If handle_click is a coroutine function \u2014 the only shape a handler may have \u2014 then calling it does not run its body at all; it returns a coroutine object. That is not callable either, so it takes the same fall through to the attributes as None does. The difference is what happens next: the layout renders, and the failure appears when the model is encoded for the wire \u2014 TypeError: Type is not JSON serializable: coroutine out of the orjson.dumps on send (executors/asgi/middleware.py). So one form of this mistake gives you a silently dead button, the other a traceback at send time. Either way, pass the function; do not call it.

The same trap appears with inline code:

# This prints when the component renders, not when clicked!\nhtml.button({\"onClick\": print(\"You clicked me!\")}, \"Click me\")\n\n# Wrap it in a named coroutine so it becomes a function to call later.\nasync def handle_click(event):\n    print(\"You clicked me!\")\n\nhtml.button({\"onClick\": handle_click}, \"Click me\")\n

In both working cases, what you pass is a function. A lambda cannot be the wrapper here, because a handler has to be async def.

"},{"location":"learn/responding-to-events/#reading-props-in-event-handlers","title":"Reading props in event handlers","text":"

Because event handlers are declared inside a component, they have access to the component's props. Here is a button that, when clicked, displays its message prop:

app.py Run
@component\ndef alert_button(message, *children):\n    shown, set_shown = use_state(\"\")\n\n    async def handle_click(event):\n        set_shown(message)\n\n    return html.div(\n        html.button({\"onClick\": handle_click}, *children),\n        html.p(shown),\n    )\n\n\n@component\ndef toolbar():\n    return html.div(\n        alert_button(\"Playing!\", \"Play Movie\"),\n        alert_button(\"Uploading!\", \"Upload Image\"),\n    )\n
# TODO\n

This lets the two buttons show different messages. Try changing the messages passed to them.

Note the set_shown(message) line: the handler closes over the message prop, which is a plain function parameter, so this behaves like any other Python closure.

"},{"location":"learn/responding-to-events/#passing-event-handlers-as-props","title":"Passing event handlers as props","text":"

Often you'll want the parent component to specify a child's event handler. Consider buttons: depending on where you're using a button() component, you might want to execute a different function\u2014perhaps one plays a movie and another uploads an image.

To do this, pass a prop the component receives from its parent as the event handler:

app.py Run
@component\ndef button(on_click, *children):\n    return html.button({\"onClick\": on_click}, *children)\n\n\n@component\ndef play_button(movie_name):\n    shown, set_shown = use_state(\"\")\n\n    async def handle_play_click(event):\n        set_shown(f\"Playing {movie_name}!\")\n\n    return html.div(\n        button(handle_play_click, f'Play \"{movie_name}\"'),\n        html.p(shown),\n    )\n\n\n@component\ndef upload_button():\n    shown, set_shown = use_state(\"\")\n\n    async def handle_upload_click(event):\n        set_shown(\"Uploading!\")\n\n    return html.div(\n        button(handle_upload_click, \"Upload Image\"),\n        html.p(shown),\n    )\n\n\n@component\ndef toolbar():\n    return html.div(play_button(\"Kiki's Delivery Service\"), upload_button())\n
# TODO\n

Here, toolbar() renders a play_button() and an upload_button():

  • play_button() passes handle_play_click as the on_click prop to the button() inside.
  • upload_button() passes handle_upload_click as the on_click prop.

Finally, the button() component accepts a prop called on_click and passes it straight to the built-in browser button with {\"onClick\": on_click}. This tells ReactPy to call the passed function on click.

Notice the two levels of naming: the prop your own component accepts is an ordinary Python parameter (on_click, snake_case), while the key handed to the browser tag is \"onClick\". A design system commonly puts styling in components like buttons but leaves the behavior to callers, so play_button() and upload_button() pass handlers down.

"},{"location":"learn/responding-to-events/#naming-event-handler-props","title":"Naming event handler props","text":"

Built-in tags like html.button and html.div only support DOM event names as handler keys \u2014 \"onClick\", \"onChange\", and so on. When you build your own components, however, you can name their handler props anything you like, because they are just Python parameters.

By convention, name them on_ followed by the interaction:

app.py Run
@component\ndef button(on_smash, *children):\n    return html.button({\"onClick\": on_smash}, *children)\n\n\n@component\ndef app():\n    status, set_status = use_state(\"Nothing selected\")\n\n    async def handle_play(event):\n        set_status(\"Playing!\")\n\n    async def handle_upload(event):\n        set_status(\"Uploading!\")\n\n    return html.div(\n        html.p(status),\n        button(handle_play, \"Play Movie\"),\n        button(handle_upload, \"Upload Image\"),\n    )\n
# TODO\n

In this example {\"onClick\": on_smash} shows that the browser button still needs the key \"onClick\", but the prop name received by your own button() component is up to you.

When a component supports multiple interactions, name handler props after app-specific concepts. A toolbar() could receive on_play_movie and on_upload_image. The calling app() does not need to know what toolbar() will do with them \u2014 that's an implementation detail. It could wire them to a click today and a keyboard shortcut tomorrow. Naming props after app-specific interactions gives you that flexibility.

Use the right tag

Use the semantically correct HTML tag for the interaction. To handle clicks, use html.button({\"onClick\": handle_click}) rather than html.div({\"onClick\": handle_click}). A real browser <button> gives you built-in behavior such as keyboard navigation and focus handling. If you dislike the default styling, restyle it with CSS rather than downgrading it to a <div>. Learn more about writing accessible markup.

"},{"location":"learn/responding-to-events/#the-event-object","title":"The event object","text":"

Handlers receive the event data as their single positional argument. By convention it's called event.

It is not React's SyntheticEvent, and it is not a serialized snapshot either. There is nothing to persist and no native object to unwrap: persist() and nativeEvent are React's answers to a pooling problem this design never had. They are not members of Event \u2014 but __getattr__ will happily hand you a proxy for either name, truthy and waiting to fail, so if event.persist: and hasattr(event, \"nativeEvent\") both say yes and only the await raises EventProxyNotFound. Never duck-type a proxy by attribute; read what you know is there. What your handler holds is a reactpy.types.Event \u2014 a live, lazily resolved view onto the browser's object (_event_proxy.py, Event). Reading any property of it hands back another proxy, and awaiting the proxy is what asks the browser for the value:

async def handle_change(event):\n    value = await event.target.value   # one round trip\n    await event.target.focus()         # methods are callable too\n

The property chain is spelled exactly as it is in JavaScript. The one difference is the await, because the value lives in the browser rather than in this process. There is no dictionary behind it: event[\"target\"][\"value\"] raises TypeError: 'Event' object is not subscriptable, because a proxy is deliberately not a container and has no __getitem__, __iter__, or __len__.

Nothing is fetched until a handler asks for it, so a handler which only calls set_state() never sends a request. Two things keep the count down for the handlers which do read. A property which has been read is remembered against its event, so reading it twice costs one trip. And properties which are in flight at the same moment go out as a single message:

value, key = await event.read(\"target.value\", \"key\")\nvalue, key = await asyncio.gather(event.target.value, event.key)\n

Both cost one round trip. Two await expressions written one after the other cannot be batched \u2014 the second is not known until the first has landed.

A read belongs to the moment it is awaited

await event.target.value reports what the element holds now, just as the same expression would in JavaScript \u2014 not what it held when the keystroke fired. A handler still queued behind another event's work sees whatever that element has become by the time it runs. A handler which must act on the value as its own event saw it has to read it before anything else can change it.

Reads are cached too, so a second read hands back what the first one found however much the page has moved on since. Ask the browser what it holds by now with refresh:

(latest,) = await event.read(\"target.value\", refresh=True)\n

Nothing is quietly answered with None. A property the browser will not give \u2014 one it blocks, an object it has forgotten, a path that is not there \u2014 raises a subclass of EventProxyError (Unavailable, Timeout, NotFound, Evicted, AccessError). A typo such as event.target.vallue is therefore an error, not a silent None. What is quiet is the mistake in the other direction: leave the await off and you hold a proxy, which is always truthy and never raises on its own \u2014 it reaches set_state() or an f-string as its own repr, or fails at the wire. A None you did read is a real answer from the page: a property that exists and holds null comes back as one.

The reads worth knowing:

Read Meaning await event.target.value Text of an <input>, <textarea>, or selected <option> await event.target.checked Whether a checkbox or radio is on \u2014 read it from \"onChange\", which is the event that fires when one is toggled await event.target.files Files from an <input type=\"file\"> await event.key Which key, on onKeyDown / onKeyUp await event.type The event name, e.g. \"click\" \u2014 the one value the client volunteers with the id, so awaiting it sends nothing await event.target.dataset The element's data-* attributes await event.composedPath() The event's path, read from the browser

Since the client holds the real object instead of serializing it, that list is a shortlist and not a ceiling. Anything the browser can answer is readable, and whatever it cannot turn into JSON \u2014 a DOM node, a DataTransfer \u2014 comes back as a reference which is itself a proxy, so you can keep walking. What the client refuses is anything that could be used to break out of the page (window, document, parentNode, __proto__, eval, ...) and anything that looks like an event handler, which is matched as /^on[a-z]/i and so includes onPointerDown. Whole-object views omit methods; an explicit method path still returns something callable.

Form submits

For onSubmit the target is the <form> itself, which means value is not there to read \u2014 await event.target.value raises EventProxyNotFound, and batching it into a read() fails the whole read. A form does have a name, so (name,) = await event.read(\"target.name\") works; to reach what was typed, ask for the control collection: controls = await event.target.elements, then index c[\"value\"] on the entries it returns. See Communicate Data Between Server and Client.

"},{"location":"learn/responding-to-events/#event-propagation","title":"Event propagation","text":"

Event handlers also catch events from any children a component has. We say that an event \"bubbles\" or \"propagates\" up the tree: it starts where the event happened, and then goes up.

ReactPy attaches an ordinary DOM listener to each element, so bubbling is the browser's native behavior rather than something ReactPy implements. That has one consequence worth knowing: unlike React, which delegates every event to the document root and then replays it down your component tree, ReactPy's handlers follow the real DOM tree. A parent handler fires for events from anything nested inside it, including elements you did not render yourself.

This <div> contains a button. Both the <div> and the button have their own \"onClick\" handlers. Which do you think fire when you click the button?

app.pystyles.css Run
@component\ndef toolbar():\n    messages, set_messages = use_state([])\n\n    async def handle_toolbar_click(event):\n        set_messages([*messages, \"You clicked on the toolbar!\"])\n\n    async def handle_play_click(event):\n        set_messages([*messages, \"Playing!\"])\n\n    return html.div(\n        {\"className\": \"Toolbar\", \"onClick\": handle_toolbar_click},\n        html.button({\"onClick\": handle_play_click}, \"Play Movie\"),\n    )\n
.toolbar {\n    background: #aaa;\n    padding: 5px;\n}\n\nbutton {\n    margin: 5px;\n}\n
# TODO\n

If you click the button, its \"onClick\" runs first, followed by the parent <div>'s. Two messages appear. If you click the toolbar itself, only the parent's handler runs.

Scroll does not bubble

Because propagation is the browser's own, the browser's exceptions apply. \"onScroll\" does not bubble: attaching it to an ancestor will not catch scrolls from descendants. Attach it to the element that actually scrolls.

"},{"location":"learn/responding-to-events/#stopping-propagation","title":"Stopping propagation","text":"

To prevent an event from reaching parent components, call event.stopPropagation():

app.pystyles.css Run
@component\ndef button(on_click, *children):\n    async def handle_click(event):\n        # Detected from this line, so the client stops the event before\n        # it reaches the parent's handler.\n        event.stopPropagation()\n        on_click()\n\n    return html.button({\"onClick\": handle_click}, *children)\n\n\n@component\ndef toolbar():\n    messages, set_messages = use_state([])\n\n    async def handle_toolbar_click(event):\n        set_messages([*messages, \"You clicked on the toolbar!\"])\n\n    def handle_play_click():\n        set_messages([*messages, \"Playing!\"])\n\n    return html.div(\n        {\"className\": \"Toolbar\", \"onClick\": handle_toolbar_click},\n        button(handle_play_click, \"Play Movie\"),\n    )\n
.toolbar {\n    background: #aaa;\n    padding: 5px;\n}\n\nbutton {\n    margin: 5px;\n}\n
# TODO\n

Now clicking the button produces only \"Playing!\".

ReactPy detects this call by reading your bytecode

Calling event.stopPropagation() inside a handler does not itself stop anything: by the time your handler runs, the event has already bubbled. What stops propagation is a flag sent to the browser in advance, and ReactPy derives it by inspecting the bytecode of your handler (core/_event_inspect.py, inspect_event_handler).

It looks for .stopPropagation (and .preventDefault, and .stopImmediatePropagation) called directly on the first parameter. That produces some sharp edges, all of them observable:

  • The handler must take a parameter at all. async def handler(): can never be detected.
  • It must be the first positional parameter. Renaming it is fine; the position is what matters.
  • Rebinding defeats it. e = event followed by e.stopPropagation() is not detected, and propagation silently continues.
  • Passing the event into a helper that calls it is likewise not detected \u2014 the inspector reads one function's bytecode and does not follow calls.

When in doubt, skip detection and declare the intent explicitly:

from reactpy import event\n\n@event(stop_propagation=True, prevent_default=True)\nasync def handle_click(event): ...\n

Because the live proxy keeps the browser object alive, the call is also forwarded to it without being waited for \u2014 but by then the browser has usually moved on, so rely on the handler flag when the action truly must not happen. event.propagationStopped and event.defaultPrevented answer synchronously, and with what Python asked for. What the browser settled on is a property like any other: (prevented,) = await event.read(\"defaultPrevented\"), which is worth the round trip when other scripts on the page may have prevented the action too.

"},{"location":"learn/responding-to-events/#preventing-default-behavior","title":"Preventing default behavior","text":"

Call event.preventDefault() the same way to stop the browser's built-in action. The classic case is a form submit, which would otherwise navigate away from the page:

app.py Run
@component\ndef signup():\n    signed_up, set_signed_up = use_state(0)\n    name, set_name = use_state(\"\")\n\n    async def handle_name_change(event):\n        set_name(await event.target.value)\n\n    async def handle_submit(event):\n        # Without this line the browser would navigate away from the page.\n        event.preventDefault()\n        set_signed_up(signed_up + 1)\n        set_name(\"\")\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.input({\"value\": name, \"onChange\": handle_name_change}),\n        html.button({\"type\": \"submit\"}, \"Sign up\"),\n        html.p(f\"{signed_up} signed up\"),\n    )\n
# TODO\n

The same detection rules \u2014 and the same @event(prevent_default=True) escape hatch \u2014 apply.

Capture-phase handlers

To see an event on the way down, before your children handle it, append Capture to the event name:

async def handle_toolbar_click(event):\n    ...\n\n\nasync def handle_play_click(event):\n    ...\n\n\nhtml.div(\n    {\"onClickCapture\": handle_toolbar_click},\n    html.button({\"onClick\": handle_play_click}, \"Play Movie\"),\n)\n

ReactPy forwards the key untouched \u2014 there is no capture logic in vdom.tsx at all. The suffix is honoured one layer down, by Preact, which strips a trailing Capture, registers the listener with addEventListener(name, handler, true), and dispatches \"click\" to it in the capture phase. So \"onClickCapture\" fires before the button's own \"onClick\", and the ordering is the browser's, not ReactPy's. Nothing in ReactPy checks that the name you gave is a real event, which is why the same freedom lets \"onMyThing\" through and lets \"on_click\" register a listener for \"_click\" that nothing ever dispatches.

"},{"location":"learn/responding-to-events/#can-event-handlers-have-side-effects","title":"Can event handlers have side effects?","text":"

Yes. Unlike component bodies, which must stay pure, event handlers run in response to an action rather than during rendering, so they are the correct place to change things. See Where you can cause side effects for where side effects belong and why.

"},{"location":"learn/responding-to-events/#recap","title":"Recap","text":"
  • Declare the handler with async def. A def, a lambda, or a state setter is refused with a TypeError when the element is built.
  • Add a handler by putting the DOM event name in camelCase into the attributes dictionary: {\"onClick\": handle_click}.
  • Only names you define are snake_case; \"on_click\" renders but never fires.
  • Pass a function, never the result of calling one; a string value means client-side JavaScript, not Python.
  • Handlers may read props, call setters, and have side effects.
  • The event object is a live proxy onto the browser object, not a snapshot and not a SyntheticEvent. Read await event.target.value and await event.key; batch with await event.read(\"target.value\", \"key\"). Subscripting it raises.
  • Events bubble natively, with the browser's own exceptions such as \"onScroll\". Stop them with event.stopPropagation(), which is detected from your bytecode \u2014 or declare it with @event(stop_propagation=True).
"},{"location":"learn/reusing-logic-with-custom-hooks/","title":"Reusing Logic with Custom Hooks","text":""},{"location":"learn/reusing-logic-with-custom-hooks/#overview","title":"Overview","text":"

A custom Hook is a plain Python function whose name starts with use_ and which calls other Hooks. It lets you reuse stateful logic between components \u2014 each caller still gets its own state, and nothing about it is special to ReactPy beyond the naming rule that makes the linter work.

You will learn

  • How to extract a custom Hook, and what it does and does not share
  • Why each call site gets independent state
  • Why passing a callback into a Hook silently freezes it
  • What actually happens when a Hook is called conditionally
  • What reactpy-flake8 can and cannot see inside a custom Hook
"},{"location":"learn/reusing-logic-with-custom-hooks/#custom-hooks-sharing-logic-between-components","title":"Custom Hooks: sharing logic between components","text":"

Consider a shopping list where each row can be selected. The selection logic is stateful, and a second screen will need it too. Pulling it out gives you a function that reads like a Hook and behaves like one:

app.py Run
def use_cart(items):\n    \"\"\"Selection logic for a list of items \u2014 the state lives in the caller.\"\"\"\n    selected, set_selected = use_state([])\n\n    def handler_for(item):\n        async def toggle(event):\n            if item in selected:\n                set_selected([name for name in selected if name != item])\n            else:\n                set_selected([*selected, item])\n\n        return toggle\n\n    return selected, handler_for\n\n\n@component\ndef checkout(items):\n    selected, handler_for = use_cart(items)\n    total = 10 * len(selected)\n    return html.div(\n        html.ul(\n            [\n                html.li(\n                    {\"key\": item},\n                    html.button(\n                        {\"onClick\": handler_for(item)},\n                        f\"{item} {'[x]' if item in selected else '[ ]'}\",\n                    ),\n                )\n                for item in items\n            ]\n        ),\n        html.p(f\"{len(selected)} selected, total ${total}\"),\n    )\n\n\n@component\ndef app():\n    return checkout([\"apple\", \"bread\", \"cheese\"])\n
# TODO\n

Verified: pressing apple, cheese, bread, then apple again walks the counter 1 \u2192 2 \u2192 3 \u2192 2 and the total follows it. Nothing about use_cart is registered, exported, or declared \u2014 it is an ordinary function that happens to call use_state.

Two rules make it a Hook rather than a helper:

  • Its name starts with use_. This is not cosmetic; see below.
  • It is called during rendering, at the top level of a component or another Hook \u2014 never in an if, a loop, a callback, or after an early return.
"},{"location":"learn/reusing-logic-with-custom-hooks/#hook-names-must-match-_use_w","title":"Hook names must match ^_*use_\\w+$","text":"

reactpy_flake8 decides what counts as a Hook with a regex, DEFAULT_HOOK_FUNCTION_PATTERN = r\"^_*use_\\w+$\" (reactpy_flake8/run.py). Measured against the same file with only the function name changed, calling it conditionally from a component:

Function name Reported use_form REACTPY102 \u2014 hook used inside if _use_form REACTPY102 useForm REACTPY101 \u2014 hook used outside component or hook get_form REACTPY101

So the naming rule is what buys you the good diagnostic. A camelCase useForm still gets flagged \u2014 but as REACTPY101 at every use_state line inside it, which points at the wrong thing and reads like you called a Hook from the wrong place. Name them use_snake_case, and the linter can tell control-flow violations apart from misplaced calls.

Not every render-time function needs the prefix

Add use_ when the function calls Hooks. Leave it off when it does not \u2014 a format_rows() or total_price() is an ordinary function, and prefixing it would make the linter police it as a Hook.

"},{"location":"learn/reusing-logic-with-custom-hooks/#custom-hooks-share-logic-not-state","title":"Custom Hooks share logic, not state","text":"

A common fear is that a Hook is a singleton. It is not: state is stored per call site, in order. Call the same Hook twice in one component and you get two independent boxes.

app.py Run
def use_toggle(initial=False):\n    on, set_on = use_state(initial)\n\n    async def flip(event):\n        set_on(not on)\n\n    return on, flip\n\n\n@component\ndef panel():\n    left, flip_left = use_toggle()\n    right, flip_right = use_toggle()\n    return html.div(\n        html.button({\"onClick\": flip_left}, f\"left {'ON' if left else 'off'}\"),\n        html.button({\"onClick\": flip_right}, f\"right {'ON' if right else 'off'}\"),\n        html.p(f\"both on: {left and right}\"),\n    )\n\n\n@component\ndef app():\n    return panel()\n
# TODO\n

Verified: pressing left gives left ON right off; pressing right gives left ON right ON; pressing left again gives left off right ON. Same function, same arguments, two unrelated pieces of state \u2014 and both on only when both are flipped.

The same Hook called from two different components is equally independent, since each component instance has its own Hook slots. There is no shared registry, no module-level store, and no Context involved.

This is also why a custom Hook is not a place to put values you want everyone to see. For that, pass props or use create_context (Passing Data Deeply with Context).

"},{"location":"learn/reusing-logic-with-custom-hooks/#passing-reactive-values-between-hooks","title":"Passing reactive values between Hooks","text":"

A Hook receives plain values during render and returns plain values. It re-runs whenever its caller re-renders, so it sees the same props and state the caller does.

def use_selection(items, limit):\n    selected, set_selected = use_state([])\n    too_many = len(selected) > limit          # re-evaluated every render\n    return selected, set_selected, too_many\n

What you cannot do is pass a callback in and list it as a dependency.

"},{"location":"learn/reusing-logic-with-custom-hooks/#the-callback-you-pass-in-will-go-stale","title":"The callback you pass in will go stale","text":"

use_polling below takes a result handler and lists it as the Effect's dependency \u2014 which looks exactly right, and never works:

app.py Run
def use_polling(on_result):\n    async def loop():\n        while True:\n            await asyncio.sleep(0.2)\n            on_result(\"poll finished\")\n\n    # `on_result` looks like a sensible dependency; it never counts as changed\n    use_async_effect(loop, dependencies=[on_result])\n\n\n@component\ndef dashboard():\n    topic, set_topic = use_state(\"news\")\n    last, set_last = use_state(\"nothing yet\")\n    tick = use_ref(0)\n\n    def on_result(value):\n        tick.current += 1\n        # reads `topic` from whichever render created this callback\n        set_last(f\"{value} #{tick.current} (handler saw topic={topic})\")\n\n    use_polling(on_result)\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        html.p(last),\n    )\n\n\n@component\ndef app():\n    return dashboard()\n
# TODO\n

Verified: press Topic once so the page reads Topic newsx, and the line still says handler saw topic=news. The poll counter keeps climbing, so it looks alive \u2014 but it is calling the handler from the first render, forever.

The reason is in core/hooks.py, strictly_equal: a def nested in a component is compared by its co_* bytecode attributes, not by identity. on_result re-created on the next render compares code-equal, so the dependency never changes and the Effect is never replaced.

The fix is to keep the callback in a ref that is refreshed during render, and leave it out of the list:

app.py Run
def use_polling(topic, on_result):\n    latest = use_ref(on_result)\n    # always the newest callback, without making it a dependency\n    latest.current = on_result\n\n    async def loop():\n        while True:\n            await asyncio.sleep(0.2)\n            latest.current(f\"result for {topic}\")\n\n    # deliberately excludes on_result: listing it would never help anyway\n    use_async_effect(loop, dependencies=[topic])\n\n\n@component\ndef dashboard():\n    topic, set_topic = use_state(\"news\")\n    last, set_last = use_state(\"nothing yet\")\n\n    def on_result(value):\n        # reads `topic` from the render that created it\n        set_last(f\"{value} (topic was {topic})\")\n\n    use_polling(topic, on_result)\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        html.p(last),\n    )\n\n\n@component\ndef app():\n    return dashboard()\n
# TODO\n

Verified: after changing the topic, the same line reads (topic was newsx). The Effect still has exactly one dependency; only the ref's contents changed.

This is the same trap as a locally defined function used as an Effect dependency (Lifecycle of Reactive Effects), and it is nastier inside a Hook because the code that lists the dependency and the code that created the callback live in different files.

"},{"location":"learn/reusing-logic-with-custom-hooks/#when-a-hook-is-called-conditionally-nothing-fails","title":"When a Hook is called conditionally, nothing fails","text":"

This is the section React's version of the page does not need, because React's compiler and dev-mode warnings are not present here.

ReactPy matches Hooks to their state slots by call order. Nothing names them, and nothing checks them. When the number of Hooks called changes between renders, the slots shift and values land in the wrong variables:

app.py Run
def use_profile(show_nickname):\n    name, set_name = use_state(\"name\")\n    if show_nickname:\n        nickname, set_nickname = use_state(\"nickname\")\n    else:\n        nickname = \"-\"\n    # `bio` is the third hook when the branch is taken and the second when it\n    # is not, so it inherits the slot that `nickname` used to own\n    bio, set_bio = use_state(\"bio\")\n    return name, nickname, bio, set_bio\n\n\n@component\ndef profile(show_nickname):\n    name, nickname, bio, set_bio = use_profile(show_nickname)\n\n    async def extend_bio(event):\n        set_bio(bio + \"!\")\n\n    return html.div(\n        html.button({\"onClick\": extend_bio}, f\"bio={bio}\"),\n        html.p(f\"name={name} nickname={nickname}\"),\n    )\n\n\n@component\ndef app():\n    show, set_show = use_state(True)\n\n    async def handle_show_click(e):\n        set_show(not show)\n\n    return html.div(\n        html.button({\"onClick\": handle_show_click}, \"toggle nickname\"),\n        profile(show),\n    )\n
# TODO\n

Verified: press bio= twice, then toggle nickname. The page prints

bio=bio!!          # before the branch changes\nbio=nickname       # after \u2014 bio is now nickname's state\n

No exception. No warning. No log record \u2014 the probe captured a completely silent reactpy logger across the whole transition. bio is the third Hook when the branch is taken and the second when it is not, so after the flip it reads the box that nickname used to own.

The corrected version calls every Hook unconditionally and makes only the return value conditional:

app.py Run
def use_profile(show_nickname):\n    name, set_name = use_state(\"name\")\n    # the hook is called unconditionally; only the returned value is conditional\n    nickname, set_nickname = use_state(\"nickname\")\n    bio, set_bio = use_state(\"bio\")\n    return name, (nickname if show_nickname else \"-\"), bio, set_bio\n\n\n@component\ndef profile(show_nickname):\n    name, nickname, bio, set_bio = use_profile(show_nickname)\n\n    async def extend_bio(event):\n        set_bio(bio + \"!\")\n\n    return html.div(\n        html.button({\"onClick\": extend_bio}, f\"bio={bio}\"),\n        html.p(f\"name={name} nickname={nickname}\"),\n    )\n\n\n@component\ndef app():\n    show, set_show = use_state(True)\n\n    async def handle_show_click(e):\n        set_show(not show)\n\n    return html.div(\n        html.button({\"onClick\": handle_show_click}, \"toggle nickname\"),\n        profile(show),\n    )\n
# TODO\n

Verified with the same script: bio=bio!! before and after the toggle. The toggle now changes only what is displayed.

"},{"location":"learn/reusing-logic-with-custom-hooks/#what-the-linter-can-see","title":"What the linter can see","text":"

REACTPY102 does fire on the broken file above \u2014 hook 'use_state' used inside if statement \u2014 provided the function is named as a Hook. That coverage is real and worth wiring into CI. So does REACTPY103, which catches the early-return case: a hook after a return reports hook 'use_state' used after an early return on line N. REACTPY101 catches hooks used outside any component or hook, and REACTPY100 hooks defined as closures. All four are in reactpy-flake8's RulesOfHooksVisitor. What it does not extend to:

  • calling a Hook-conditionally through a wrapper that the linter does not recognise (see the name table above),
  • a Hook list in one branch matching the caller's expectation in another,
  • the order the hooks actually run in \u2014 that is a runtime property, and nothing checks it.

And it checks dependency lists only under exhaustive_hook_deps=True, and even then only for a narrow set of shapes. See Lifecycle of Reactive Effects for exactly which. Treat the linter as a partial net and the fix \u2014 Hooks at the top level, always \u2014 as the actual rule.

Do not branch to disable a Hook

# wrong: the second Hook disappears when polling is off\nif polling:\n    use_async_effect(loop, dependencies=[topic])\n
Keep the call and branch inside the Hook body instead, returning None when there is nothing to do. See Lifecycle of Reactive Effects.

"},{"location":"learn/reusing-logic-with-custom-hooks/#passing-event-handlers-into-custom-hooks","title":"Passing event handlers into custom Hooks","text":"

A handler is an ordinary value: pass it in, call it, done \u2014 it does not need to be a dependency, and listing it does not make it fresher (measured above). The two shapes that work:

# the Hook calls it; the ref keeps it current\ndef use_polling(on_result):\n    latest = use_ref(on_result)\n    latest.current = on_result\n    ...\n\n# or the caller owns the callback entirely and the Hook just hands back a value\nselected, toggle = use_cart(items)\n

If you find yourself wanting to store a callback so it stays fresh, that is the same problem useEffectEvent solves in React \u2014 and ReactPy has no such Hook. The ref-updated-during-render idiom above is the equivalent, with the caveat that reading .current during render makes the render depend on an external value (Manipulating the DOM with Refs).

"},{"location":"learn/reusing-logic-with-custom-hooks/#when-to-use-custom-hooks","title":"When to use custom Hooks","text":"

Write one when you have logic \u2014 not markup \u2014 that two components genuinely need in the same shape. Good candidates in ReactPy:

  • selection, pagination, and form-draft logic
  • a subscription or polling loop with its clean-up wired correctly
  • reading a value from a use_connection-scoped resource
  • a compound updater that several components apply to the same kind of state

Keep them focused on a concrete use case. A use_async(fetcher, deps) that wraps use_async_effect in generic terms tends to encode one person's cancellation assumptions for everyone; the specific use_polling(topic, on_result) is easier to reason about and easier to change.

Two things a custom Hook is not:

  • A render bailout. ReactPy has no memo, so extracting a Hook does not reduce how often a component's body runs (Separating Events from Effects).
  • A state container. It cannot share state between components; only props and Context can.
"},{"location":"learn/reusing-logic-with-custom-hooks/#recap","title":"Recap","text":"
  • A custom Hook is a function named use_* that calls Hooks; extract one to reuse stateful logic.
  • State is per call site, in call order: the same Hook called twice is two independent boxes (verified).
  • Name them use_snake_case \u2014 the linter's regex is ^_*use_\\w+$, and camelCase versions get flagged as REACTPY101 instead of the accurate REACTPY102.
  • Never list a callback you were handed as a dependency: it compares code-equal and freezes the Effect on the first render. Read it through a ref updated during render.
  • Conditional Hook calls do not raise; state slots shift and values silently migrate between variables (verified: bio became nickname). Call every Hook unconditionally and branch on the return value.
  • reactpy-flake8 sees control-flow violations inside recognised Hooks, and nothing else \u2014 no dependency checks, no wrapper coverage.
"},{"location":"learn/reusing-logic-with-custom-hooks/#related","title":"Related","text":"
  • Synchronizing with Effects \u2014 the Effect patterns a Hook will most often wrap
  • Lifecycle of Reactive Effects \u2014 strictly_equal, frozen closures, and silent Effect failures
  • Separating Events from Effects \u2014 handlers, and why identity buys nothing here
  • Rules of Hooks and Components and Hooks Must Be Pure \u2014 the constraints a Hook inherits
  • Use State and Use Ref \u2014 parameter-level reference
"},{"location":"learn/scaling-up-with-reducer-and-context/","title":"Scaling Up with Reducer and Context","text":""},{"location":"learn/scaling-up-with-reducer-and-context/#overview","title":"Overview","text":"

A reducer keeps a component's update logic in one pure function; context carries values past components that only forward them. Together they let a deep tree read and change shared state without any wiring through props. In ReactPy there is one extra fact that makes this work cleanly: dispatch has a stable identity across renders, so it is safe to put in a context value.

You will learn

  • How to combine a reducer with context, in three steps
  • Why dispatch is safe to share through context in ReactPy
  • How to move all the wiring into a single module the UI never imports from directly
"},{"location":"learn/scaling-up-with-reducer-and-context/#combining-a-reducer-with-context","title":"Combining a reducer with context","text":"

Start with the version where state and dispatch travel as props. Only task_form and task want dispatch, but it has to be handed to everyone in between:

app.py Run
@component\ndef task_form(state, dispatch, task):\n    # The only component that actually wants dispatch...\n    async def handle_toggle(event):\n        dispatch({\"type\": \"toggled\", \"id\": task[\"id\"]})\n\n    return html.div(\n        {\"style\": {\"textDecoration\": \"line-through\" if task[\"done\"] else \"none\"}},\n        task[\"text\"],\n        html.button({\"onClick\": handle_toggle}, \"Toggle\"),\n    )\n\n\n@component\ndef task(state, dispatch, task):\n    # ...but must receive it and hand it on, unchanged.\n    async def handle_delete(event):\n        dispatch({\"type\": \"deleted\", \"id\": task[\"id\"]})\n\n    return html.li(\n        {\"key\": task[\"id\"]},\n        task_form(state, dispatch, task),\n        html.button({\"onClick\": handle_delete}, \"Delete\"),\n    )\n\n\n@component\ndef task_list(state, dispatch):\n    # ...and this one forwards both, without reading either.\n    return html.ul(\n        [task(state, dispatch, item, key=item[\"id\"]) for item in state[\"tasks\"]]\n    )\n\n\n@component\ndef task_app():\n    state, dispatch = use_reducer(tasks_reducer, {\"tasks\": INITIAL_TASKS, \"next_id\": 2})\n\n    async def handle_add(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add}, \"Add task\"),\n        task_list(state, dispatch),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Read the signatures: task_list(state, dispatch) receives both and reads neither \u2014 it only builds children and passes both along. task forwards state untouched to task_form, which uses only dispatch. Every new layer in the middle copies the same two parameters.

The reducer is already correct for ReactPy: module-level, reading nothing but its two arguments, returning new objects, and raising on an unknown action type. See Extracting State Logic into a Reducer for why each of those matters here.

"},{"location":"learn/scaling-up-with-reducer-and-context/#step-1-create-the-context","title":"Step 1: Create the context","text":"

One context carries both halves \u2014 the state to read and the dispatch to change it with. Keep it beside the reducer, in a module that does not import the UI:

app.pytasks.py Run
@component\ndef task_form(task):\n    # Reads dispatch from context; no prop arrived.\n    _, dispatch = use_context(TaskContext)\n\n    async def handle_toggle(event):\n        dispatch({\"type\": \"toggled\", \"id\": task[\"id\"]})\n\n    return html.div(\n        {\"style\": {\"textDecoration\": \"line-through\" if task[\"done\"] else \"none\"}},\n        task[\"text\"],\n        html.button({\"onClick\": handle_toggle}, \"Toggle\"),\n    )\n\n\n@component\ndef task(task):\n    _, dispatch = use_context(TaskContext)\n\n    async def handle_delete(event):\n        dispatch({\"type\": \"deleted\", \"id\": task[\"id\"]})\n\n    return html.li(\n        {\"key\": task[\"id\"]},\n        task_form(task),\n        html.button({\"onClick\": handle_delete}, \"Delete\"),\n    )\n\n\n@component\ndef task_list():\n    # Reads only the state; dispatch never has to come through here.\n    state, _ = use_context(TaskContext)\n    return html.ul([task(item, key=item[\"id\"]) for item in state[\"tasks\"]])\n\n\n@component\ndef task_app():\n    state, dispatch = use_reducer(tasks_reducer, INITIAL_STATE)\n\n    async def handle_add(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add}, \"Add task\"),\n        # One provider for the whole subtree.\n        TaskContext(task_list(), value=(state, dispatch)),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
\"\"\"The reducer, the context, and the initial state \u2014 all outside the UI.\"\"\"\n\nfrom collections.abc import Callable\nfrom typing import Any\n\nfrom reactpy import create_context\nfrom reactpy.types import Context\n\nTaskState = dict[str, Any]\nTaskValue = tuple[TaskState, Callable[[dict[str, Any]], None]]\n\n# The reducer never mutates what it is given, so one shared constant is\n# safe as the initial state for every instance.\nINITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n]\n\nINITIAL_STATE: TaskState = {\"tasks\": INITIAL_TASKS, \"next_id\": 2}\n\n# One context carries both halves: the state to read and the dispatch to change\n# it with. Annotate the name, or the default narrows the value type.\nTaskContext: Context[TaskValue] = create_context((INITIAL_STATE, lambda action: None))\n\n\ndef tasks_reducer(state: TaskState, action: dict[str, Any]) -> TaskState:\n    kind = action[\"type\"]\n    if kind == \"added\":\n        task = {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        return {\n            **state,\n            \"tasks\": [*state[\"tasks\"], task],\n            \"next_id\": state[\"next_id\"] + 1,\n        }\n    if kind == \"toggled\":\n        return {\n            **state,\n            \"tasks\": [\n                (\n                    {**task, \"done\": not task[\"done\"]}\n                    if task[\"id\"] == action[\"id\"]\n                    else task\n                )\n                for task in state[\"tasks\"]\n            ],\n        }\n    if kind == \"deleted\":\n        return {\n            **state,\n            \"tasks\": [task for task in state[\"tasks\"] if task[\"id\"] != action[\"id\"]],\n        }\n    raise ValueError(f\"unknown action type {kind!r}\")\n
# TODO\n

Two things to notice in tasks.py.

The context object is matched by identity, so create it once and import it from here everywhere. The annotation is load-bearing: create_context infers the value type from its default, so without Context[TaskValue] a type checker fixes the value to the default's shape and rejects a real one. Write the default first, then annotate. See Use Context.

The default value is a usable value, not None: an empty state and a dispatch that does nothing. A component rendered without a provider then renders an empty list instead of crashing when a handler fires.

"},{"location":"learn/scaling-up-with-reducer-and-context/#step-2-put-state-and-dispatch-into-context","title":"Step 2: Put state and dispatch into context","text":"

Call use_reducer once, in the component that owns the state, and provide both halves:

state, dispatch = use_reducer(tasks_reducer, INITIAL_STATE)\n...\nTaskContext(task_list(), value=(state, dispatch))\n

A tuple is the simplest value that carries both. If you prefer a dict or a dataclass, the same rules apply \u2014 but do not expect the value to control how often consumers render. ReactPy compares nothing here: use_context hands back whatever the provider is currently holding, and a provider that re-renders re-renders its children whether the value is new, equal, or literally the same object. Re-render cost is governed by where you place the provider, not by how you build the value.

"},{"location":"learn/scaling-up-with-reducer-and-context/#step-3-use-context-anywhere-in-the-tree","title":"Step 3: Use context anywhere in the tree","text":"

Now the middle of the tree forgets both parameters:

@component\ndef task_list():\n    state, _ = use_context(TaskContext)\n    return html.ul([task(item, key=item[\"id\"]) for item in state[\"tasks\"]])\n

task_list no longer mentions dispatch at all, and task and task_form each call use_context themselves. Verified: this version and the prop version above are behaviourally identical \u2014 the same initial two tasks, the same row struck through by Toggle, the same count after Add task, the same rows after Delete. All three versions in this page pass the same user-journey comparison.

Note

use_context reserves no state slot, so unlike use_state it is safe to call conditionally. That is not an invitation to hide it: read it at the top of the component, the way you would a prop, so it is obvious where the value comes from.

"},{"location":"learn/scaling-up-with-reducer-and-context/#why-dispatch-is-safe-to-share-in-reactpy","title":"Why dispatch is safe to share in ReactPy","text":"

In React, sharing dispatch through context is safe because React guarantees its identity. In ReactPy it is safe for a more concrete reason you can check in the source: use_reducer builds the dispatcher once with _use_const \u2014 a use_state holding the function \u2014 and returns that same object on every render. Verified: across five renders and four dispatches, id(dispatch) took exactly one value.

That matters because the dispatcher closes over the reducer, which ReactPy also freezes at the first render. A stale dispatcher is not a hazard here as long as the reducer is module-level \u2014 which is exactly the rule the reducer page already imposes. Share dispatch freely; do not share a value you expect to be re-read from a closure.

What context does not do: it is not a render optimisation. ReactPy has no memo, so when the provider re-renders with a new value, the whole subtree re-renders, including every component whose read value did not change. Context removes parameters, not work. See What context does not do.

"},{"location":"learn/scaling-up-with-reducer-and-context/#moving-all-wiring-into-a-single-file","title":"Moving all wiring into a single file","text":"

Once the UI reads everything from context, the reducer, the context object, and even use_reducer can disappear from it. Move the whole mechanism into tasks.py, and expose exactly two names \u2014 a provider component and an accessor hook:

app.pytasks.py Run
@component\ndef task_form(task):\n    # No props. The dispatch comes from the wiring file.\n    _, dispatch = use_tasks()\n\n    async def handle_toggle(event):\n        dispatch({\"type\": \"toggled\", \"id\": task[\"id\"]})\n\n    return html.div(\n        {\"style\": {\"textDecoration\": \"line-through\" if task[\"done\"] else \"none\"}},\n        task[\"text\"],\n        html.button({\"onClick\": handle_toggle}, \"Toggle\"),\n    )\n\n\n@component\ndef task(task):\n    _, dispatch = use_tasks()\n\n    async def handle_delete(event):\n        dispatch({\"type\": \"deleted\", \"id\": task[\"id\"]})\n\n    return html.li(\n        {\"key\": task[\"id\"]},\n        task_form(task),\n        html.button({\"onClick\": handle_delete}, \"Delete\"),\n    )\n\n\n@component\ndef task_list():\n    state, _ = use_tasks()\n    return html.ul([task(item, key=item[\"id\"]) for item in state[\"tasks\"]])\n\n\n@component\ndef task_app():\n    state, dispatch = use_tasks()\n\n    async def handle_add(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add}, \"Add task\"),\n        task_list(),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks\"),\n    )\n\n\n@component\ndef app():\n    # One name from the wiring file: the provider component.\n    return task_store(task_app())\n
\"\"\"All the reducer, context, and wiring lives here; the UI imports two names.\"\"\"\n\nfrom collections.abc import Callable\nfrom typing import Any\n\nfrom reactpy import component, create_context, use_context, use_reducer\nfrom reactpy.types import Context\n\nTaskState = dict[str, Any]\nTaskValue = tuple[TaskState, Callable[[dict[str, Any]], None]]\n\n# The reducer never mutates what it is given, so one shared constant is\n# safe as the initial state for every instance.\nINITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n]\n\nINITIAL_STATE: TaskState = {\"tasks\": INITIAL_TASKS, \"next_id\": 2}\n\nTaskContext: Context[TaskValue] = create_context((INITIAL_STATE, lambda action: None))\n\n\ndef tasks_reducer(state: TaskState, action: dict[str, Any]) -> TaskState:\n    kind = action[\"type\"]\n    if kind == \"added\":\n        task = {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        return {\n            **state,\n            \"tasks\": [*state[\"tasks\"], task],\n            \"next_id\": state[\"next_id\"] + 1,\n        }\n    if kind == \"toggled\":\n        return {\n            **state,\n            \"tasks\": [\n                (\n                    {**task, \"done\": not task[\"done\"]}\n                    if task[\"id\"] == action[\"id\"]\n                    else task\n                )\n                for task in state[\"tasks\"]\n            ],\n        }\n    if kind == \"deleted\":\n        return {\n            **state,\n            \"tasks\": [task for task in state[\"tasks\"] if task[\"id\"] != action[\"id\"]],\n        }\n    raise ValueError(f\"unknown action type {kind!r}\")\n\n\n@component\ndef task_store(*children: Any):\n    \"\"\"Owns the reducer and provides it. The UI never sees the reducer.\"\"\"\n    state, dispatch = use_reducer(tasks_reducer, INITIAL_STATE)\n    return TaskContext(*children, value=(state, dispatch))\n\n\ndef use_tasks() -> TaskValue:\n    \"\"\"The only accessor the UI needs: current state plus dispatch.\"\"\"\n    return use_context(TaskContext)\n
# TODO\n

The UI file now imports only task_store and use_tasks. It never mentions use_reducer, create_context, tasks_reducer, or TaskContext. Two consequences worth having:

  • The UI cannot reach the reducer except through an action, so there is no way to bypass it.
  • Changing how the state is stored \u2014 a different reducer, a different initial shape, even dropping context entirely \u2014 touches one file and no component.

task_store is a component that calls use_reducer and returns the provider. A component's render may return another component, so this composes without ceremony. Note that use_tasks is a plain function that calls use_context: it is a custom Hook, so name it use_* \u2014 the linter's regex is ^_*use_\\w+$, and camelCase variants get a less accurate diagnostic. See Reusing Logic with Custom Hooks.

Pitfall

Because the provider is now a component, removing it from the tree unmounts everything below it and throws their state away. Do not write task_store(page()) if logged_in else page(). Keep the provider in the tree and vary only its value \u2014 verified: a conditional provider resets a leaf's state (its render counter returns to 1 and its draft text clears), while a stable key on the child does not prevent it. See Removing a provider resets the subtree below it.

Two task_store instances are two independent stores, not one shared store: verified, three presses on one side moved one=5 while the other stayed at its initial two=2. Context propagates down from the provider that happens to be above you. To genuinely share state, there must be one provider above both consumers \u2014 usually near the root.

Deep Dive

Should you also provide a derived value, such as a filtered list or a total? Compute it in the component that needs it. ReactPy re-renders the subtree whenever the provided state changes anyway, so a cached derivation saves nothing, and use_memo around it adds a dependency list to keep correct. Reserve the context value for what is genuinely shared: the state object and dispatch. For the caching rules, see You Might Not Need an Effect.

"},{"location":"learn/scaling-up-with-reducer-and-context/#recap","title":"Recap","text":"
  • A reducer owns the transitions; context carries (state, dispatch) past components that would only forward them. Both are needed together \u2014 context without a reducer leaves the update logic scattered, a reducer without context leaves the props.
  • Create the context beside the reducer, at module level, with a usable default and a Context[...] annotation so the value type is not narrowed by the default.
  • Provide (state, dispatch) from one component that owns use_reducer; read it in each component that needs it.
  • dispatch is identity-stable across renders in ReactPy (verified: one id over five renders), which is what makes sharing it through context sound.
  • A module that exports a provider component and a use_* accessor lets the UI import two names and never touch the reducer.
  • Keep the provider in the tree. Removing it remounts the subtree and discards state; a key does not save it (verified).
  • One provider per shared store. Two providers are two stores (verified).
  • Context removes parameters, not renders \u2014 ReactPy has no render bailout.
"},{"location":"learn/scaling-up-with-reducer-and-context/#related","title":"Related","text":"
  • Extracting State Logic into a Reducer \u2014 the reducer rules this pattern depends on, including the frozen-first-reducer rule
  • Passing Data Deeply with Context \u2014 the context mechanics, and the provider-removal trap
  • Use Context \u2014 parameters, caveats, and error messages
  • Use Reducer \u2014 parameter-level reference and troubleshooting
  • Reusing Logic with Custom Hooks \u2014 naming and rules for the use_tasks accessor
  • Choosing the State Structure \u2014 what belongs in state before you decide who provides it
"},{"location":"learn/separating-events-from-effects/","title":"Separating Events from Effects","text":""},{"location":"learn/separating-events-from-effects/#overview","title":"Overview","text":"

Event handlers and Effects run at different times and for different reasons. Most Effect bugs are not Effect bugs \u2014 they are interaction logic that was put in the wrong place. Move it back and the Effect disappears.

You will learn

  • Why interaction logic belongs in a handler, not an Effect
  • Why handler logic is not reactive \u2014 and why that is a feature
  • How ReactPy picks which render's handler actually runs
  • What use_callback does in ReactPy, and what it does not buy you
  • How to read the newest value from a callback you cannot re-create
"},{"location":"learn/separating-events-from-effects/#choosing-between-event-handlers-and-effects","title":"Choosing between event handlers and Effects","text":"

The question is never \"which hook?\" but \"when should this logic run?\"

Event handler Effect Runs when the interaction happens after a render, whenever a dependency changed Re-runs when only if the user repeats the interaction any dependency changes \u2014 including one you forgot to list Right for submit, click, input, drag, \"do this now\" subscribing, polling, syncing to a server

Both cost one render per click in the examples below \u2014 the difference is not performance, it is what triggers the work. An Effect that sets state it also lists as a dependency re-triggers itself; measured, that loop reached 86 renders before it was stopped. A handler has no such feedback path, which is most of why interaction logic belongs there.

If the logic should run when the user does the thing, it goes in the handler. If it should run whenever a value is a certain way, it goes in an Effect.

"},{"location":"learn/separating-events-from-effects/#logic-in-an-effect-driven-by-click-only-state","title":"Logic in an Effect, driven by click-only state","text":"

Here the shipping quote is computed by an Effect watching a flag that only a click sets:

app.py Run
def calculate_shipping(city, country):\n    \"\"\"Plain function: takes what it needs, returns the result.\"\"\"\n    if not city or not country:\n        return \"\"\n    return f\"{city}, {country} \u2014 ${15 + 5}\"\n\n\n@component\ndef shipping_form():\n    city, set_city = use_state(\"\")\n    country, set_country = use_state(\"\")\n    # state that exists purely to make the Effect run\n    quoted, set_quoted = use_state(False)\n    shipping_info, set_shipping_info = use_state(\"\")\n    clicks, set_clicks = use_state(0)\n\n    async def submit(event):\n        set_clicks(clicks + 1)\n        set_quoted(True)\n\n    # the \"intent\" is buried in an Effect watching a flag a click merely sets\n    def recalculate():\n        if quoted:\n            result = calculate_shipping(city, country)\n            set_shipping_info(result)\n            if result:\n                print(f\"quoted {result}\")\n\n    use_effect(recalculate, dependencies=[quoted])\n\n    async def handle_city_change(e):\n        set_city(await e.target.value)\n\n    async def handle_country_change(e):\n        set_country(await e.target.value)\n\n    return html.div(\n        html.label(\n            \"City: \",\n            html.input({\"value\": city, \"onChange\": handle_city_change}),\n        ),\n        html.label(\n            \"Country: \",\n            html.input(\n                {\n                    \"value\": country,\n                    \"onChange\": handle_country_change,\n                }\n            ),\n        ),\n        html.button({\"onClick\": submit}, \"Get quote\"),\n        html.p(shipping_info or \"\u2014\"),\n        f\"Clicks: {clicks}\",\n    )\n\n\n@component\ndef app():\n    return shipping_form()\n
# TODO\n

This is where the ReactJS version of this example stops matching reality. Verified behaviour: type a city and country, then press Get quote three times.

  • Click 1 logs quoted Portland, US \u2014 $20.
  • Clicks 2 and 3 increment the counter and log nothing at all.

set_quoted(True) sets the same value every time, so ReactPy sees no change, and the dependency list says nothing re-synchronized. The user gets a button that works once. Nothing about this is obvious from the code \u2014 the Effect looks correct.

"},{"location":"learn/separating-events-from-effects/#the-same-logic-in-the-handler","title":"The same logic in the handler","text":"app.py Run
def calculate_shipping(city, country):\n    \"\"\"Plain function: takes what it needs, returns the result.\"\"\"\n    if not city or not country:\n        return \"\"\n    return f\"{city}, {country} \u2014 ${15 + 5}\"\n\n\n@component\ndef shipping_form():\n    city, set_city = use_state(\"\")\n    country, set_country = use_state(\"\")\n    shipping_info, set_shipping_info = use_state(\"\")\n    clicks, set_clicks = use_state(0)\n\n    async def submit(event):\n        set_clicks(clicks + 1)\n        result = calculate_shipping(city, country)\n        set_shipping_info(result)\n        if result:\n            print(f\"quoted {result}\")\n\n    async def handle_city_change(e):\n        set_city(await e.target.value)\n\n    async def handle_country_change(e):\n        set_country(await e.target.value)\n\n    return html.div(\n        html.label(\n            \"City: \",\n            html.input({\"value\": city, \"onChange\": handle_city_change}),\n        ),\n        html.label(\n            \"Country: \",\n            html.input(\n                {\n                    \"value\": country,\n                    \"onChange\": handle_country_change,\n                }\n            ),\n        ),\n        html.button({\"onClick\": submit}, \"Get quote\"),\n        html.p(shipping_info or \"\u2014\"),\n        f\"Clicks: {clicks}\",\n    )\n\n\n@component\ndef app():\n    return shipping_form()\n
# TODO\n

No trigger state, no Effect, no use_effect import. Verified: every click logs quoted Portland, US \u2014 $20, at the same one-render-per-click cost as a plain button.

The general rule: if the only thing that sets a piece of state is an event handler, and the only thing that reads it is an Effect, delete both and call the function directly.

When an Effect really is the right home

Keep the Effect when the logic must also run for reasons other than the interaction: the value changed while the user was idle, the component just mounted, another component changed something. \"The user pressed Save\" is a handler; \"the document is dirty and should be persisted\" is an Effect.

"},{"location":"learn/separating-events-from-effects/#reactive-values-and-reactive-logic","title":"Reactive values and reactive logic","text":"

Every value in the component body belongs to one render. Logic placed in the component body or in an Effect re-runs when those values change. Logic inside a handler does not \u2014 it runs when called, on whatever render produced it.

"},{"location":"learn/separating-events-from-effects/#logic-inside-event-handlers-is-not-reactive","title":"Logic inside event handlers is not reactive","text":"
async def submit(event):\n    print(f\"submitting {city}, {country}\")   # reads one render's values\n    send(city, country)\n

Nothing about this code will re-run when city changes. That is not a defect: a click should do the thing once, using the values the user saw when they clicked. Making handler logic \"reactive\" is what produces double-submits and the click-once bug above.

"},{"location":"learn/separating-events-from-effects/#logic-inside-effects-is-reactive","title":"Logic inside Effects is reactive","text":"
def reconnect():\n    open_socket(room)\n\nuse_effect(reconnect, dependencies=[room])\n

This one re-runs on its own whenever room changes, including changes you did not expect. That is the whole point of an Effect \u2014 and the whole reason logic you don't want repeated does not belong there.

"},{"location":"learn/separating-events-from-effects/#extracting-non-reactive-logic-out-of-effects","title":"Extracting non-reactive logic out of Effects","text":"

Once intent lives in the handler, the shared part is usually an ordinary function. Give it its inputs as parameters and both sides can use it:

def calculate_shipping(city, country):\n    if not city or not country:\n        return \"\"\n    return f\"{city}, {country} \u2014 ${15 + 5}\"\n\n\n@component\ndef shipping_form():\n    ...\n\n    async def submit(event):\n        set_shipping_info(calculate_shipping(city, country))\n

No hook needed. A function that only reads values and returns something does not need to be a hook, and making it one costs a render.

ReactPy has no useEffectEvent, no useOptimistic, and no useTransition \u2014 verified against reactpy at main. That is not a gap to route around with an Effect: the handler-is-not-reactive property that Effect Events exist to give you in React is what you already have here for free.

"},{"location":"learn/separating-events-from-effects/#which-handler-actually-runs","title":"Which handler actually runs","text":"

ReactPy keeps one handler per target, registered when the component renders (core/layout.py, _event_handlers), and looks it up at dispatch time \u2014 when the event is processed, not when it arrived.

Two things follow, both measured.

A normal click sees current values. Click after click with the connection settling between them, and the handler always reads the latest state:

added item, discount=0\nadded item, discount=10\nadded item, discount=20\n

Events that arrive before the next render all see the same snapshot. Deliver two clicks without a render in between and both run the same closure:

app.py Run
@component\ndef basket():\n    items, set_items = use_state([])\n    discount, set_discount = use_state(0)\n\n    async def add_naive(event):\n        # reads `items` and `discount` from whichever render created this handler\n        print(f\"naive: {len(items)} items, discount={discount}\")\n        set_items([*items, f\"item-{len(items) + 1}@{discount}\"])\n\n    async def add_safe(event):\n        # the updater receives the *current* state at the moment it is applied\n        def append(current):\n            print(f\"safe: {len(current)} items, discount={discount}\")\n            return [*current, f\"item-{len(current) + 1}@{discount}\"]\n\n        set_items(append)\n\n    async def handle_discount_click(e):\n        set_discount(discount + 10)\n\n    return html.div(\n        html.button({\"onClick\": add_naive}, \"Add (naive)\"),\n        html.button({\"onClick\": add_safe}, \"Add (safe)\"),\n        html.button({\"onClick\": handle_discount_click}, f\"Coupon {discount}%\"),\n        html.p(\", \".join(items) or \"empty basket\"),\n    )\n\n\n@component\ndef app():\n    return basket()\n
# TODO\n

Verified with two clicks delivered back-to-back:

Handler log Final list Add (naive) 0 items twice item-1@0 \u2014 the second click is gone Add (safe) 0 items, then 1 items item-1@0, item-2@0

This is not a hypothetical. On a real connection, events are queued per target (core/layout.py, _process_event_queue) and a double-click, a fast key repeat, or any latency between the browser and a busy server produces exactly this. The naive set_items(items + [...]) reads a snapshot; the updater set_items(lambda current: ...) is applied against the state at the moment it lands.

So the handler-staleness story in ReactPy is narrower than in React, and it is worth being precise:

  • You generally do not need use_callback to \"get fresh values\" in a handler \u2014 the handler that runs is the current one.
  • You do need the functional updater form whenever an update depends on the current state and two events can arrive in the same round trip.
"},{"location":"learn/separating-events-from-effects/#handlers-which-await","title":"Handlers which await","text":"

Every handler is a coroutine, and it may set state after an await:

async def submit(event):\n    set_status(\"saving\")\n    result = await save(document)\n    set_status(\"saved\")          # works \u2014 the render happens after the await\n

Verified: the page shows status=saving immediately, then status=saved once the await resolves \u2014 two renders, both after the click.

But if the component unmounts during the await, the update is discarded and the only trace is a debug line \u2014 Did not render component with model state ID ... - component already unmounted. Nothing errors, nothing warns, and the user who navigated away is unaffected. Do not treat \"the setter did not raise\" as evidence the write landed; if it matters, assert it in a test that keeps the component mounted.

"},{"location":"learn/separating-events-from-effects/#keeping-a-callbacks-identity-stable","title":"Keeping a callback's identity stable","text":"

use_callback exists and memoizes through use_memo, so it shares the dependency semantics described on Lifecycle of Reactive Effects \u2014 strictly_equal, not id.

app.py Run
@component\ndef toolbar():\n    count, set_count = use_state(0)\n    theme, set_theme = use_state(\"light\")\n    previous = use_ref(object())\n\n    async def stable_handler(event):\n        set_count(count + 1)\n\n    # inferred dependencies: `count` is the only free variable\n    on_click = use_callback(stable_handler)\n\n    reused = previous.current is on_click\n    previous.current = on_click\n\n    async def handle_theme_click(e):\n        set_theme(\"dark\" if theme == \"light\" else \"light\")\n\n    return html.div(\n        html.button({\"onClick\": on_click}, f\"Count {count}\"),\n        html.button(\n            {\"onClick\": handle_theme_click},\n            f\"Theme {theme}\",\n        ),\n        html.p(\"handler reused on this render\" if reused else \"handler recreated\"),\n    )\n\n\n@component\ndef app():\n    return toolbar()\n
# TODO\n

Verified: pressing Theme (not a dependency) reports handler reused on this render; pressing Count (a dependency) reports handler recreated.

Be clear about what that buys you. ReactPy has no render bailout: there is no memo (absent from both reactpy and reactpy.reactjs), and a child re-renders whenever its parent does. Measured \u2014 a child whose props and handler never changed ran its body 4 times across one mount and three parent re-renders. So a stable callback does not make anything re-render less. What it does is keep a value out of an Effect's or use_memo's dependency list without re-triggering it.

There is no useEvent, so the nearest equivalent to React's Effect Event \u2014 a stable callable that always reads the newest values \u2014 is a ref updated during render:

app.py Run
@component\ndef checkout():\n    coupon, set_coupon = use_state(\"\")\n    total, set_total = use_state(0)\n    coupon_ref = use_ref(\"\")\n    # updated during render, so any callback reads the value as of the click\n    coupon_ref.current = coupon\n\n    # pinned to nothing: the identity never changes, yet it reads `coupon_ref`\n    async def apply_coupon_handler(event):\n        print(f\"applying coupon={coupon_ref.current!r}\")\n\n    apply_coupon = use_callback(apply_coupon_handler, dependencies=[])\n\n    async def handle_coupon_change(e):\n        # The await comes first: `.upper()` runs on the string which comes back,\n        # not on the proxy. Without the parens you would call `.upper()` on the\n        # proxy itself and silently get the un-uppercased value.\n        set_coupon((await e.target.value).upper())\n\n    async def handle_total_click(e):\n        set_total(total + 10)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": coupon,\n                \"onChange\": handle_coupon_change,\n            }\n        ),\n        html.button({\"onClick\": apply_coupon}, \"Apply coupon\"),\n        html.button({\"onClick\": handle_total_click}, f\"Total {total}\"),\n        html.p(f\"page shows coupon={coupon!r} \u2014 compare with the console\"),\n    )\n\n\n@component\ndef app():\n    return checkout()\n
# TODO\n

Verified: Apply coupon logs the value currently in the box even though the callback is pinned to dependencies=[] and is never re-created.

A callback pinned to a local function never updates

Same trap as Effects, and it is worse here because the callback is supposed to be stable. dependencies=[describe], where describe is a local def, compares code-equal forever:

app.py Run
@component\ndef search():\n    query, set_query = use_state(\"\")\n    runs, set_runs = use_state(0)\n\n    def describe():\n        return f\"query={query!r}\"\n\n    async def run(event):\n        # `describe` is re-created every render but compares code-equal, so this\n        # callback is still the one from the very first render. The functional\n        # updater keeps the counter honest; only `describe()` is frozen.\n        print(f\"handler sees {describe()}\")\n        set_runs(lambda current: current + 1)\n\n    on_click = use_callback(run, dependencies=[describe])\n\n    async def handle_query_change(e):\n        set_query(await e.target.value)\n\n    return html.div(\n        html.input({\"value\": query, \"onChange\": handle_query_change}),\n        html.button({\"onClick\": on_click}, f\"Run ({runs})\"),\n        html.p(f\"page shows query={query!r} \u2014 compare with the console\"),\n    )\n\n\n@component\ndef app():\n    return search()\n
# TODO\n

Verified: type shoes, press Run, type shoes2, press Run again. The page shows the new query both times; the console says handler sees query='' both times. The counter keeps counting (Run (2)), which is what makes this so easy to miss \u2014 the callback looks alive while reading a snapshot from mount.

Depend on the scalar values the callback actually reads, and never on a function defined inside the component.

"},{"location":"learn/separating-events-from-effects/#recap","title":"Recap","text":"
  • Ask when the logic should run. Interaction \u2192 handler. Synchronization \u2192 Effect.
  • State that only a handler sets and only an Effect reads is a smell: call the function from the handler and delete both. Verified symptom: the button works once.
  • Handler logic is deliberately not reactive; that is why you do not need React's Effect Events here.
  • ReactPy stores one handler per target and looks it up at dispatch, so a normal click sees current values \u2014 but events delivered before the next render share one snapshot. Use the functional updater for read-modify-write.
  • An async handler may set state after await; after unmount that update is silently dropped.
  • use_callback pins identity, not freshness, and identity buys you nothing for render cost in ReactPy. Reach for a render-updated ref when you need both.
  • Never list a locally defined function as a dependency of a callback or Effect \u2014 it compares code-equal and freezes the closure.
"},{"location":"learn/separating-events-from-effects/#related","title":"Related","text":"
  • Lifecycle of Reactive Effects \u2014 dependency semantics, strictly_equal, and silent Effect failures
  • You Might Not Need an Effect \u2014 logic that belongs in the render
  • Synchronizing with Effects \u2014 the patterns that genuinely need an Effect
  • Use Callback and Use Ref \u2014 parameter-level reference
"},{"location":"learn/sharing-state-between-components/","title":"Sharing State Between Components","text":""},{"location":"learn/sharing-state-between-components/#overview","title":"Overview","text":"

Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as lifting state up, and it's one of the most common things you will do writing ReactPy code.

You will learn

  • How to share state between two components
  • How to coordinate components by lifting state to their common parent
  • What controlled and uncontrolled components are
"},{"location":"learn/sharing-state-between-components/#lifting-state-up-by-example","title":"Lifting state up by example","text":"

In this example, a parent accordion() component renders two separate panel()s:

  • accordion()
    • panel()
    • panel()

Each panel() component has a boolean is_active state that determines whether its content is visible.

Press the Show button for both panels:

app.pystyles.css Run
@component\ndef panel(title, *children):\n    is_active, set_is_active = use_state(False)\n\n    async def handle_is_active_click(event):\n        set_is_active(True)\n\n    return html.section(\n        {\"className\": \"panel\"},\n        html.h3(title),\n        (\n            html.p(*children)\n            if is_active\n            else html.button({\"onClick\": handle_is_active_click}, \"Show\")\n        ),\n    )\n\n\n@component\ndef accordion():\n    return html(\n        html.h2(\"Almaty, Kazakhstan\"),\n        panel(\n            \"About\",\n            \"With a population of about 2 million, Almaty is Kazakhstan's \"\n            \"largest city. From 1929 to 1997, it was its capital city.\",\n        ),\n        panel(\n            \"Etymology\",\n            \"The name comes from \u0430\u043b\u043c\u0430, the Kazakh word for \u201capple\u201d and is \"\n            \"often translated as \u201cfull of apples\u201d.\",\n        ),\n    )\n
h3,\np {\n    margin: 5px 0;\n}\n\n.panel {\n    padding: 10px;\n    border: 1px solid #aaa;\n}\n
# TODO\n

Notice how pressing one panel's button does not affect the other panel \u2014 they are independent.

But now let's say you want to change it so that only one panel is expanded at any given time. With that design, expanding the second panel should collapse the first one. How would you do that?

To coordinate these two panels, you need to \"lift their state up\" to a parent component in three steps:

  1. Remove state from the child components.
  2. Pass hardcoded data from the common parent.
  3. Add state to the common parent and pass it down together with the event handlers.

This will allow the accordion() component to coordinate both panel()s and only expand one at a time.

"},{"location":"learn/sharing-state-between-components/#step-1-remove-state-from-the-child-components","title":"Step 1: Remove state from the child components","text":"

You will give control of the panel()'s is_active to its parent component. This means that the parent component will pass is_active to panel() as a prop instead. Start by removing this line from the panel() component:

is_active, set_is_active = use_state(False)\n

And instead, add is_active to the panel()'s list of props:

@component\ndef panel(title, is_active, *children):\n

Now the panel()'s parent component can control is_active by passing it down as a prop. Conversely, the panel() component now has no control over the value of is_active \u2014 it's now up to the parent component!

"},{"location":"learn/sharing-state-between-components/#step-2-pass-hardcoded-data-from-the-common-parent","title":"Step 2: Pass hardcoded data from the common parent","text":"

To lift state up, you must locate the closest common parent component of both of the child components that you want to coordinate:

  • accordion() (closest common parent)
    • panel()
    • panel()

In this example, it's the accordion() component. Since it's above both panels and can control their props, it will become the \"source of truth\" for which panel is currently active. Make the accordion() component pass a hardcoded value of is_active (for example, True) to both panels:

app.pystyles.css Run
@component\ndef panel(title, is_active, *children):\n    return html.section(\n        {\"className\": \"panel\"},\n        html.h3(title),\n        html.p(*children) if is_active else html.button(\"Show\"),\n    )\n\n\n@component\ndef accordion():\n    return html(\n        html.h2(\"Almaty, Kazakhstan\"),\n        panel(\"About\", True, \"With a population of about 2 million...\"),\n        panel(\"Etymology\", True, \"The name comes from \u0430\u043b\u043c\u0430...\"),\n    )\n
h3,\np {\n    margin: 5px 0;\n}\n\n.panel {\n    padding: 10px;\n    border: 1px solid #aaa;\n}\n
# TODO\n

Try editing the hardcoded is_active values in the accordion() component and see the result on the screen.

"},{"location":"learn/sharing-state-between-components/#step-3-add-state-to-the-common-parent","title":"Step 3: Add state to the common parent","text":"

Lifting state up often changes the nature of what you're storing as state.

In this case, only one panel should be active at a time. This means that the accordion() common parent component needs to keep track of which panel is the active one. Instead of a bool value, it could use a number as the index of the active panel() for the state variable:

active_index, set_active_index = use_state(0)\n

When the active_index is 0, the first panel is active, and when it's 1, it's the second one.

Clicking the \"Show\" button in either panel() needs to change the active index in accordion(). A panel() can't set the active_index state directly because it's defined inside the accordion(). The accordion() component needs to explicitly allow the panel() component to change its state by passing an event handler down as a prop:

async def show_about(event):\n    set_active_index(0)\n\n\npanel(\n    \"About\",\n    active_index == 0,\n    show_about,\n    ...\n)\n

A handler must be a coroutine, and a coroutine cannot be a lambda, so each panel gets a named handler. That is what the real example below does, twice.

The html.button() inside the panel() will now use the on_show prop as its click event handler:

app.pystyles.css Run
@component\ndef panel(title, is_active, on_show, *children):\n    return html.section(\n        {\"className\": \"panel\"},\n        html.h3(title),\n        (html.p(*children) if is_active else html.button({\"onClick\": on_show}, \"Show\")),\n    )\n\n\n@component\ndef accordion():\n    active_index, set_active_index = use_state(0)\n\n    async def show_about(event):\n        set_active_index(0)\n\n    async def show_etymology(event):\n        set_active_index(1)\n\n    return html(\n        html.h2(\"Almaty, Kazakhstan\"),\n        panel(\n            \"About\",\n            active_index == 0,\n            show_about,\n            \"With a population of about 2 million, Almaty is Kazakhstan's \"\n            \"largest city. From 1929 to 1997, it was its capital city.\",\n        ),\n        panel(\n            \"Etymology\",\n            active_index == 1,\n            show_etymology,\n            \"The name comes from \u0430\u043b\u043c\u0430, the Kazakh word for \u201capple\u201d and is \"\n            \"often translated as \u201cfull of apples\u201d.\",\n        ),\n    )\n
h3,\np {\n    margin: 5px 0;\n}\n\n.panel {\n    padding: 10px;\n    border: 1px solid #aaa;\n}\n
# TODO\n

This completes lifting state up! Moving state into the common parent component allowed you to coordinate the two panels. Using the active index instead of two \"is shown\" flags ensured that only one panel is active at a given time. And passing down the event handler to the child allowed the child to change the parent's state.

"},{"location":"learn/sharing-state-between-components/#controlled-and-uncontrolled-components","title":"Controlled and uncontrolled components","text":"

It is common to call a component with some local state \"uncontrolled\". For example, the original panel() component with an is_active state variable is uncontrolled because its parent cannot influence whether the panel is active or not.

In contrast, you might say a component is \"controlled\" when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final panel() component with the is_active prop is controlled by the accordion() component.

Uncontrolled components are easier to use within their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.

In practice, \"controlled\" and \"uncontrolled\" aren't strict technical terms \u2014 each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.

When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later.

"},{"location":"learn/sharing-state-between-components/#a-single-source-of-truth-for-each-state","title":"A single source of truth for each state","text":"

In a ReactPy application, many components will have their own state. Some state may \"live\" close to the leaf components (components at the bottom of the tree) like inputs. Other state may \"live\" closer to the top of the app. For example, a router implemented in ReactPy would keep the current route in the state of a component near the root, and pass it down by props.

For each unique piece of state, you will choose the component that \"owns\" it. This principle is also known as having a \"single source of truth\". It doesn't mean that all state lives in one place \u2014 but that for each piece of state, there is a specific component that holds that piece of information. Instead of duplicating shared state between components, lift it up to their common shared parent, and pass it down to the children that need it.

Your app will change as you work on it. It is common that you will move state down or back up while you're still figuring out where each piece of the state \"lives\". This is all part of the process!

To see what this feels like in practice with a few more components, read Thinking in React.

"},{"location":"learn/sharing-state-between-components/#summary","title":"Summary","text":"
  • When you want to coordinate two components, move their state to their common parent.
  • Then pass the information down through props from their common parent.
  • Finally, pass the event handlers down so that the children can change the parent's state.
  • It's useful to consider components as \"controlled\" (driven by props) or \"uncontrolled\" (driven by state).
"},{"location":"learn/state-a-components-memory/","title":"State: A Component's Memory","text":""},{"location":"learn/state-a-components-memory/#overview","title":"Overview","text":"

Components often need to change what's on the screen as a result of an interaction. Typing into a form should update the input field, clicking \"next\" on an image carousel should change which image is displayed, clicking \"buy\" should put a product in the shopping cart. Components need to \"remember\" things: the current input value, the current image, the shopping cart. In ReactPy, this kind of component-specific memory is called state.

You will learn

  • How to add a state variable with use_state
  • What pair of values use_state returns
  • How to add more than one state variable
  • Why state is called local
"},{"location":"learn/state-a-components-memory/#when-a-regular-variable-isnt-enough","title":"When a regular variable isn't enough","text":"

Here's a component that renders a sculpture image. Clicking \"Next\" should show the next sculpture by changing index to 1, then 2, and so on. However, this won't work (you can try it!):

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index = 0\n\n    async def handle_next(event):\n        # `nonlocal` is what makes this assignment legal Python at all; without\n        # it the nested function gets its own `index` and the read on the right\n        # hand side raises UnboundLocalError. JS closures need no such keyword.\n        nonlocal index\n        index = index + 1\n\n    sculpture = sculptures[index]\n    return html.div(\n        html.button({\"onClick\": handle_next}, \"Next\"),\n        html.h2(html.i(sculpture[\"name\"]), \" by \", sculpture[\"artist\"]),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n        html.p(sculpture[\"description\"]),\n    )\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n

The handle_next event handler is updating a local variable, index. But two things prevent that change from being visible:

  1. Local variables don't persist between renders. When ReactPy renders this component a second time, it renders it from scratch \u2014 it doesn't consider any changes to the local variables.
  2. Changes to local variables won't trigger renders. ReactPy doesn't realize it needs to render the component again with the new data.

Both were checked against this exact component: clicking \"Next\" three times leaves the served VDOM byte-identical, and a later render caused by real state shows index back at 0 even though the handler had bumped it to 3.

To update a component with new data, two things need to happen:

  1. Retain the data between renders.
  2. Trigger ReactPy to render the component with new data (re-rendering).

use_state provides those two things:

  1. A state variable to retain the data between renders.
  2. A state setter function to update the variable and trigger ReactPy to render the component again.
"},{"location":"learn/state-a-components-memory/#adding-a-state-variable","title":"Adding a state variable","text":"

To add a state variable, import use_state from reactpy at the top of the file:

from reactpy import component, html, use_state\n

Then replace this line:

index = 0\n

with

index, set_index = use_state(0)\n

index is a state variable and set_index is the setter function.

Python gives you the destructuring for free

React's const [index, setIndex] = useState(0) needs array destructuring syntax. In Python this is ordinary tuple unpacking, so there is nothing new to learn here \u2014 and unlike JavaScript, leaving out a name is a syntax error rather than a silent undefined.

This is how they work together in handle_next:

async def handle_next(event):\n    set_index(index + 1)\n

The handler needs nonlocal to read its own variable

If you keep the plain-variable version, the assignment inside the nested function is not legal Python without a declaration:

async def handle_next(event):\n    nonlocal index\n    index = index + 1\n

Without it, Python treats index as a new local of handle_next, and reading it on the right-hand side raises UnboundLocalError. This is a Python scoping rule, not a ReactPy one \u2014 JavaScript closures just work. With use_state you never need it, because you read index and call set_index instead of assigning.

Now clicking the \"Next\" button switches the current sculpture:

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index, set_index = use_state(0)\n\n    async def handle_next(event):\n        set_index(index + 1)\n\n    sculpture = sculptures[index]\n    return html.div(\n        html.button({\"onClick\": handle_next}, \"Next\"),\n        html.h2(html.i(sculpture[\"name\"]), \" by \", sculpture[\"artist\"]),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n        html.p(sculpture[\"description\"]),\n    )\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n
"},{"location":"learn/state-a-components-memory/#meet-your-first-hook","title":"Meet your first Hook","text":"

In ReactPy, use_state, as well as any other function starting with use, is called a Hook.

Hooks are special functions that are only available while ReactPy is rendering (which we'll get into in more detail on the next page). They let you \"hook into\" different ReactPy features.

State is just one of those features, but you will meet the other Hooks later.

Call Hooks only at the top level

Hooks \u2014 functions starting with use \u2014 can only be called at the top level of your components or your own Hooks. You can't call Hooks inside conditions, loops, or other nested functions. Hooks are functions, but it's helpful to think of them as unconditional declarations about your component's needs. You \"use\" ReactPy features at the top of your component similar to how you \"import\" modules at the top of your file.

The check is not just advice: the reactpy-flake8 plugin (the ReactPy analogue of eslint-plugin-react-hooks, and part of this repo's own lint config) reports REACTPY102 for a Hook under a branch or in a loop:

@component\ndef bad(flag):\n    a, _ = use_state(\"A\")\n    if flag:                      # REACTPY102 hook 'use_state' used inside if statement\n        b, _ = use_state(\"B\")\n        return html.p(b)\n    return html.p(a)\n
"},{"location":"learn/state-a-components-memory/#anatomy-of-use_state","title":"Anatomy of use_state","text":"

When you call use_state, you are telling ReactPy that you want this component to remember something:

index, set_index = use_state(0)\n

In this case, you want ReactPy to remember index.

The convention is to name this pair like something, set_something. You could name it anything you like, but conventions make things easier to understand across projects.

The only argument to use_state is the initial value of your state variable. In this example, the index's initial value is set to 0 with use_state(0).

Every time your component renders, use_state gives you a pair containing two values:

  1. The state variable (index) with the value you stored.
  2. The state setter function (set_index) which can update the state variable and trigger ReactPy to render the component again.

Here's how that happens in action:

index, set_index = use_state(0)\n
  1. Your component renders the first time. Because you passed 0 to use_state as the initial value for index, it returns (0, set_index). ReactPy remembers 0 is the latest state value.
  2. You update the state. When a user clicks the button, it calls set_index(index + 1). index is 0, so it's set_index(1). This tells ReactPy to remember index is 1 now and triggers another render.
  3. Your component's second render. ReactPy still sees use_state(0), but because ReactPy remembers that you set index to 1, it returns (1, set_index) instead.
  4. And so on!

Pass a function for an expensive initial value

use_state accepts a callable and calls it only when the state is first created: use_state(lambda: json.loads(big_blob)). Passing the result of an expensive call instead \u2014 use_state(json.loads(big_blob)) \u2014 reparses on every render, because Python evaluates the argument before use_state ever runs.

"},{"location":"learn/state-a-components-memory/#giving-a-component-multiple-state-variables","title":"Giving a component multiple state variables","text":"

You can have as many state variables of as many types as you like in one component. This component has two state variables, a number index and a boolean show_more that's toggled when you click \"Show details\":

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index, set_index = use_state(0)\n    show_more, set_show_more = use_state(False)\n\n    async def handle_next(event):\n        set_index(index + 1)\n\n    async def handle_more(event):\n        set_show_more(not show_more)\n\n    sculpture = sculptures[index]\n    return html.div(\n        html.button({\"onClick\": handle_next}, \"Next\"),\n        html.h2(html.i(sculpture[\"name\"]), \" by \", sculpture[\"artist\"]),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.button(\n            {\"onClick\": handle_more}, \"Hide details\" if show_more else \"Show details\"\n        ),\n        html.p(sculpture[\"description\"]) if show_more else None,\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n    )\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n

It is a good idea to have multiple state variables if their state is unrelated, like index and show_more in this example. But if you find that you often change two state variables together, it might be easier to combine them into one. For example, if you have a form with many fields, it's more convenient to have a single state variable that holds a dict than a state variable per field. Read Choosing the State Structure for more tips.

"},{"location":"learn/state-a-components-memory/#state-is-isolated-and-private","title":"State is isolated and private","text":"

State is local to a particular invocation of a component \u2014 like a local variable. Render the same component twice and each copy gets its own state.

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index, set_index = use_state(0)\n    sculpture = sculptures[index]\n\n    async def handle_index_click(event):\n        set_index(index + 1)\n\n    return html.section(\n        html.button(\n            {\"onClick\": handle_index_click},\n            \"Next\",\n        ),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n    )\n\n\n@component\ndef page():\n    # Two separate mounts of the same component. Each call site is a different\n    # position in the tree, so each gallery keeps its own index.\n    return html.div(gallery(), gallery())\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n

Click \"Next\" in one gallery and the other doesn't budge. Each call to gallery() is a separate component instance with its own state.

This is the same mechanism behind Passing Props to a Component: the person prop is local to one invocation of Profile(), so two Profile() calls don't share it. State works the same way.

Because state belongs to the instance and not the function, two galleries can drift apart \u2014 which is sometimes what you want, and sometimes not. Sharing State Between Components covers the case where they must move together.

How does ReactPy know which state to return?

You might have noticed that the use_state call does not receive any information about which state variable it refers to. There is no \"identifier\" passed to use_state, so how does it know which state to return? Does it rely on some magic like parsing your functions? The answer is no.

Instead, to enable their concise syntax, Hooks rely on a stable call order on every render of the same component. ReactPy appends each Hook's state to a list on the component's first render, then walks that list by position on every render after (LifeCycleHook.use_state in core/_life_cycle_hook.py). This works well in practice because if you follow the rule above (\"only call Hooks at the top level\"), Hooks are always called in the same order \u2014 and reactpy-flake8 catches most mistakes.

What happens when the order does change is worth knowing, because one of the two outcomes fails silently:

  • A Hook appears where there was none before \u2192 IndexError: list index out of range, the render aborts, and the error is logged.
  • A Hook stops being called \u2192 the Hooks after it shift up one slot and quietly receive the wrong values. Nothing is logged, and the component keeps rendering.

Measured with two use_state calls that vanish behind a branch, the surviving Hook got the first slot's value while its own was left orphaned. That is far harder to debug than a crash, which is exactly why the top-level rule is enforced rather than merely recommended.

"},{"location":"learn/state-a-components-memory/#recap","title":"Recap","text":"
  • Components need to \"remember\" things, and that memory is called state.
  • A state variable is declared with use_state, which returns the current value and a setter function.
  • Updating a local variable doesn't re-render, and re-renders throw local variables away. State is what survives.
  • Hooks must be called at the top level, in the same order every render; reactpy-flake8 raises REACTPY102 otherwise.
  • State is local to each invocation of a component, so two instances never share it.
"},{"location":"learn/state-as-a-snapshot/","title":"State as a Snapshot","text":""},{"location":"learn/state-as-a-snapshot/#overview","title":"Overview","text":"

State variables might look like regular Python variables that you can read and write to. However, state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render.

You will learn

  • How setting state triggers renders
  • Why variable and prop values are fixed when a render is started
  • Why event handlers see old values of state
"},{"location":"learn/state-as-a-snapshot/#setting-state-triggers-renders","title":"Setting state triggers renders","text":"

You might think of your user interface as changing directly in response to the user event like a click. In ReactPy, it works a little differently from this mental model. You have already seen that setting state requests a re-render. This means that for an interface to react to an event, you need to update the state.

In this example, when you press \"send\", set_is_sent(True) tells ReactPy to re-render the UI:

app.pystyles.css Run
@component\ndef form():\n    is_sent, set_is_sent = use_state(False)\n    message, set_message = use_state(\"Hi!\")\n\n    if is_sent:\n        return html.h1(\"Your message is on its way!\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        set_is_sent(True)\n        send_message(message)\n\n    async def handle_message_change(event):\n        set_message(await event.target.value)\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.textarea(\n            {\n                \"placeholder\": \"Message\",\n                \"value\": message,\n                \"onChange\": handle_message_change,\n            }\n        ),\n        html.button({\"type\": \"submit\"}, \"Send\"),\n    )\n\n\ndef send_message(message):\n    print(f\"Sending: {message}\")\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n\nlabel,\ntextarea {\n    display: block;\n    margin-bottom: 10px;\n}\n
# TODO\n

Here's what happens when you click the button:

  1. The onSubmit event handler executes.
  2. set_is_sent(True) sets is_sent to True and queues a new render.
  3. ReactPy re-renders the component according to the new is_sent value.

Let's take a closer look at the relationship between state and rendering.

"},{"location":"learn/state-as-a-snapshot/#rendering-takes-a-snapshot-in-time","title":"Rendering takes a snapshot in time","text":"

\"Rendering\" means that ReactPy is calling your component, which is a function. The VDOM you return from that function is like a snapshot of the UI in time. Its props, event handlers, and local variables were all calculated using its state at the time of the render.

Unlike a photograph or a movie frame, the UI \"snapshot\" you return is interactive. It includes logic like event handlers that specify what happens in response to inputs. ReactPy updates the screen to match this snapshot and connects the event handlers. As a result, pressing a button triggers the click handler from that snapshot.

When ReactPy re-renders a component:

  1. ReactPy calls your function again.
  2. Your function returns a new VDOM snapshot.
  3. ReactPy then updates the screen to match the snapshot you've returned.

As a component's memory, state is not like a regular variable that disappears after your function returns. State actually \"lives\" in ReactPy itself \u2014 as if on a shelf! \u2014 outside of your function. When ReactPy calls your component, it gives you a snapshot of the state for that particular render. Your component returns a snapshot of the UI with a fresh set of props and event handlers, all calculated using the state values from that render!

Here's a little experiment to show you how this works. Clicking the \"+3\" button calls set_number(number + 1) three times, so you might expect three increments.

app.pystyles.css Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(number + 1)\n        set_number(number + 1)\n        set_number(number + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+3\"),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n\nlabel,\ntextarea {\n    display: block;\n    margin-bottom: 10px;\n}\n
# TODO\n

Notice that number only increments once per click \u2014 verified by dispatching a click and reading the result.

Setting state only changes it for the next render. During the first render, number was 0. This is why, in that render's handle_click, the value of number is still 0 even after set_number(number + 1) was called:

Here is what this button's click handler tells ReactPy to do:

  1. set_number(number + 1): number is 0, so set_number(0 + 1).
    • ReactPy prepares to change number to 1 on the next render.
  2. set_number(number + 1): number is 0, so set_number(0 + 1).
    • ReactPy prepares to change number to 1 on the next render.
  3. set_number(number + 1): number is 0, so set_number(0 + 1).
    • ReactPy prepares to change number to 1 on the next render.

Even though you called set_number(number + 1) three times, in this render's event handler number is always 0, so you set the state to 1 three times. This is why, after your event handler finishes, ReactPy re-renders the component with number equal to 1 rather than 3.

You can also visualize this by mentally substituting state variables with their values in your code. Since number is 0 for this render, its event handler effectively reads:

async def handle_click(event):\n    set_number(0 + 1)\n    set_number(0 + 1)\n    set_number(0 + 1)\n

For the next render number is 1, so that render's click handler reads set_number(1 + 1) three times. This is why clicking again takes the counter to 2, then 3, and so on.

This is Python's own closure rule, not a ReactPy feature

There is no magic here. handle_click is a closure over the local name number created during one specific call to counter(). That name is bound to the value ReactPy handed to that call, and no later assignment elsewhere can change what it refers to. ReactPy's contribution is simply that each render is a fresh call, so each render gets a fresh handle_click closing over fresh values.

It is worth stating plainly, because the usual Python instinct is wrong here: set_number does not write to number. It writes into ReactPy's hook state, on the shelf outside your function.

"},{"location":"learn/state-as-a-snapshot/#state-over-time","title":"State over time","text":"

Try to guess what this button's second column will show.

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n    seen, set_seen = use_state([])\n\n    async def handle_click(event):\n        set_number(number + 5)\n        # `number` here is still the value from THIS render.\n        set_seen([*seen, number])\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+5\"),\n        html.p(\"Handler saw: \", \", \".join(map(str, seen)) or \"-\"),\n    )\n
# TODO\n

Using the substitution method, number is 0, so the first click records 0 \u2014 even though set_number(number + 5) ran one line earlier. Click again and it records 5, because in that render number is 5.

But what if you delay that read, so it only happens after the component re-renders? Would it say 0 or 5? Have a guess!

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n    seen, set_seen = use_state([])\n\n    async def handle_click(event):\n        set_number(number + 5)\n\n        def delayed():\n            # Runs long after this render, but still sees this render's value.\n            set_seen([*seen, number])\n\n        asyncio.get_running_loop().call_later(3, delayed)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+5\"),\n        html.p(\"Handler saw (3s later): \", \", \".join(map(str, seen)) or \"-\"),\n    )\n
# TODO\n

Surprised? If you use the substitution method, you can see the \"snapshot\" of the state that was captured.

set_number(0 + 5)\nget_running_loop().call_later(3, delayed)   # delayed() closes over number == 0\n

The state stored in ReactPy may have changed by the time the callback runs, but the callback was scheduled using a snapshot of the state at the time the user interacted with it.

The updater form rescues only the value it updates

The scheduled callback above works because set_seen is stable and the closure captures a value, not a live binding. The trap is the reverse: a callback that reaches for the current value gets the one from whichever render created it. An updater looks like the fix, but it is narrower than it appears. ReactPy applies an updater to the current value of that one state variable the instant you call the setter (core/hooks.py, _CurrentState.dispatch calls new(self.value)), so set_seen(lambda previous: [*previous, number]) hands you the freshest seen \u2014 while number inside it is still the name frozen when the callback was built. Reach for the updater when the value you need is the value you are setting. For anything else, read it before scheduling and pass it in, or keep it in a use_ref box and read box.current when the callback finally runs.

A state variable's value never changes within a render, even if its event handler's code is asynchronous. Inside that render's handle_click, the value of number continues to be 0 even after set_number(number + 5) was called. Its value was \"fixed\" when ReactPy \"took the snapshot\" of the UI by calling your component.

Here is an example of how that makes your event handlers less prone to timing mistakes. Below is a form that sends a message with a five-second delay. Imagine this scenario:

  1. You press the \"Send\" button, sending \"Hello\" to Alice.
  2. Before the five-second delay ends, you change the value of the \"To\" field to \"Bob\".

What do you expect the status line to display? Would it display, \"You said Hello to Alice\"? Or would it display, \"You said Hello to Bob\"? Make a guess based on what you know, and then try it:

app.pystyles.css Run
@component\ndef form():\n    to, set_to = use_state(\"Alice\")\n    message, set_message = use_state(\"Hello\")\n    status, set_status = use_state(\"Not sent yet\")\n\n    async def handle_submit(event):\n        # This handler closes over `to` and `message` from ITS OWN render.\n        set_status(\"Sending...\")\n        await asyncio.sleep(5)\n        set_status(f\"You said {message} to {to}\")\n\n    async def handle_to_change(event):\n        set_to(await event.target.value)\n\n    async def handle_message_change(event):\n        set_message(await event.target.value)\n\n    return html.div(\n        html.label(\n            \"To: \",\n            html.input(\n                {\n                    \"value\": to,\n                    \"onChange\": handle_to_change,\n                }\n            ),\n        ),\n        html.label(\n            \"Message: \",\n            html.input(\n                {\n                    \"value\": message,\n                    \"onChange\": handle_message_change,\n                }\n            ),\n        ),\n        html.button({\"onClick\": handle_submit}, \"Send\"),\n        html.p(status),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n\nlabel,\ntextarea {\n    display: block;\n    margin-bottom: 10px;\n}\n
# TODO\n

If you press Send and then change \"To\" before the five seconds elapse, you still get \"You said Hello to Alice\". Verified: pressing Send and changing the recipient to \"Bob\" mid-flight produced exactly that string.

That is the payoff of snapshots. handle_submit was created by the render in which to was \"Alice\", so it keeps saying \"Alice\" no matter what later renders decide. Had the handler read the state through some live reference instead, changing the field mid-request would have sent the message to Alice while reporting that it went to Bob.

React's own version of this demo uses setTimeout and a browser alert. ReactPy has no alert on the server, so the equivalent here is a status paragraph \u2014 and asyncio.sleep rather than a timer callback. If you want the browser to alert, that is the inlineJavaScript escape hatch, not a Python function call.

"},{"location":"learn/state-as-a-snapshot/#recap","title":"Recap","text":"
  • Setting state does not change the state variable you already have; it requests a render.
  • Every render captures its own snapshot of state, props, and handlers. Values are fixed for the duration of that render.
  • This follows Python's ordinary closure rules: each render is a fresh call, so each handler closes over fresh values.
  • Reading state inside a delayed callback or an await gives you the value from the render that created that callback, which is usually what you want.
  • When you genuinely need the latest value inside a callback, use the updater form set_x(lambda previous: ...).
"},{"location":"learn/synchronizing-with-effects/","title":"Synchronizing with Effects","text":""},{"location":"learn/synchronizing-with-effects/#overview","title":"Overview","text":"

Some components need to synchronize with an external system: send an analytics log when a page appears, keep a third-party object in step with state, or hold a connection open while a view is on screen. Effects let you run code after a render so a component can talk to something outside of ReactPy.

You will learn

  • What an Effect is, and how it differs from an event handler
  • How to declare one, and when it re-runs
  • Why ReactPy infers dependencies instead of requiring an array
  • How to write a clean-up function, and what happens if you skip it
  • How to read the newest state from a long-lived Effect

Note

Capitalized \"Effect\" means the ReactPy-specific idea above: a side effect caused by rendering. \"side effect\" keeps its ordinary programming meaning.

"},{"location":"learn/synchronizing-with-effects/#what-are-effects-and-how-are-they-different-from-events","title":"What are Effects and how are they different from events?","text":"

Every ReactPy component contains two kinds of logic:

  • Rendering code (introduced in Your First Component) sits at the top level of the component body. It takes props and state, does the math, and returns VDOM. Rendering code must be pure. It may only calculate; it may not do.

  • Event handlers (introduced in Responding to Events) are functions defined inside the component that ReactPy runs only when the user does something. They contain side effects \u2014 posting a form, updating a field \u2014 triggered by a specific interaction.

That covers most needs. But some work belongs to neither bucket. A chat view must connect to the server whenever it appears on screen, no matter which click or keystroke caused it to appear. There is no event for \"this component was rendered\", and putting the connection in the render body would break purity.

An Effect declares side effects caused by rendering itself, rather than by a particular event. It runs after the render is finished, in the connection's own task.

"},{"location":"learn/synchronizing-with-effects/#you-might-not-need-an-effect","title":"You might not need an Effect","text":"

Do not reach for an Effect first. Effects exist to step outside ReactPy and synchronize with something that is not ReactPy: a network connection, a queue, a third-party library, the filesystem. If your logic only adjusts one piece of state in response to another, or computes a value from state you already have, it belongs in the render \u2014 see You Might Not Need an Effect.

"},{"location":"learn/synchronizing-with-effects/#how-to-write-an-effect","title":"How to write an Effect","text":"

Three steps:

  1. Declare it. ReactPy registers the Effect for the render that created it.
  2. Say when it re-runs. By default ReactPy infers this from your closure, which is not what React does.
  3. Add a clean-up if it needs one. Everything that opens, subscribes, or schedules needs the matching close, unsubscribe, or cancel.
"},{"location":"learn/synchronizing-with-effects/#step-1-declare-an-effect","title":"Step 1: Declare an Effect","text":"

Import the hook and call it at the top level of your component:

from reactpy import use_effect\n

Pass it a plain function that takes no arguments:

@component\ndef my_component():\n    def effect():\n        print(\"on screen\")\n\n    use_effect(effect, dependencies=[])\n    ...\n

Or use it as a decorator, which is equivalent:

@use_effect\ndef effect():\n    print(\"on screen\")\n

Here is a working component. Open the server console and press Re-check: the log line appears once, on mount, and never again \u2014 the Effect does not depend on visit_count:

app.py Run
@component\ndef status_page():\n    visit_count, set_visit_count = use_state(0)\n\n    def effect():\n        # Runs after this component is on screen. ReactPy runs it in the\n        # connection's own task, after the render that scheduled it.\n        print(\"status_page is on screen\")\n\n    use_effect(effect, dependencies=[])\n\n    async def handle_visit_count_click(event):\n        set_visit_count(visit_count + 1)\n\n    return html.div(\n        html.h1(\"Service status\"),\n        html.p(f\"This page has been opened {visit_count} times.\"),\n        html.button(\n            {\"onClick\": handle_visit_count_click},\n            \"Re-check\",\n        ),\n    )\n
# TODO\n

Two things about timing are worth stating precisely, because they differ from what a React tutorial will tell you:

  • An Effect is scheduled after your component's render is complete, not after the browser has painted. The task is created at the end of the render (core/_life_cycle_hook.py, affect_layout_did_render), and the update message has not necessarily reached the socket yet. Measured directly, an Effect's first line ran before layout.render() returned to the backend. So never assume the user can already see the result.
  • The Effect does not delay the render. layout.render() does not await your effect body; the two interleave on the event loop. If you need ordering, express it in state: have the Effect set state and let the next render depend on it.

Effects run on the server

There is no document and no window. An Effect cannot measure an element, focus a field, or attach a scroll listener, because there is no DOM where it runs. For browser-side behaviour see Manipulating the DOM with Refs and Creating VDOM Event Handlers.

use_effect rejects async def

TypeError: `use_effect` does not support async functions. Use use_async_effect instead.\n

The failure is loud with REACTPY_DEBUG on: the model comes back as {\"tagName\": \"\", \"error\": \"TypeError: ...\"} and the region renders empty (with debug off you get the empty region and a traceback in the server log only). Anything await-based belongs in use_async_effect, whose effect takes zero arguments \u2014 the 1.x async def effect(stop) shape fails silently there instead.

"},{"location":"learn/synchronizing-with-effects/#step-2-specify-the-effect-dependencies","title":"Step 2: Specify the Effect dependencies","text":"

Pass the dependencies argument to control when the Effect re-runs \u2014 and note that ReactPy gives this argument four meanings where React has two:

Value When the Effect runs (verified) dependencies=[a, b] On mount, then whenever a or b changes. dependencies=[] Once, on mount. dependencies=None After every render. omitted Inferred from the free variables of your closure.

That last row is the one that bites people porting code from React. In React, omitting the array means \"run after every render\". In ReactPy it means \"re-run when the names my closure reads change\" \u2014 core/hooks.py, _try_to_infer_closure_values reads func.__closure__ and uses those cells as the dependency list. Here the Effect reads count and nothing else, so Rename re-renders the component without re-running the Effect:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    label, set_label = use_state(\"clicks\")\n    runs, set_runs = use_state([])\n\n    def effect():\n        # This closure reads `count` and nothing else, so `count` is the\n        # Effect's only dependency. It never reads `label`, so renaming\n        # re-renders the component WITHOUT re-running this Effect.\n        set_runs(lambda previous: [*previous, f\"effect ran with count={count}\"])\n\n    use_effect(effect)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_label_click(event):\n        set_label(label + \"!\")\n\n    return html.div(\n        html.h1(f\"{label}: {count}\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button({\"onClick\": handle_label_click}, \"Rename\"),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(runs)]),\n    )\n
# TODO\n

Press Rename twice and no new log line appears; press Add 1 and one appears per increment. Verified.

Dependencies are not compared by identity

The docstrings for these hooks say dependencies are compared by id. They are not. core/hooks.py, strictly_equal gates on the type first, then uses ==, and for two locally defined functions it compares their bytecode. In practice: a freshly built [1, 2] does not re-trigger an Effect that listed the previous equal list, and neither does a structurally identical local def \u2014 which means an Effect listing a local function as its dependency keeps the closure it captured first and reads stale state permanently. Both were measured. List the values, not wrappers around them.

Write the list out

Inference is invisible, so an Effect that happens to read one extra local variable quietly gains a dependency, and one that reads nothing re-runs never. If the Effect runs more or less often than you meant, look at which names the body actually reads. An explicit list removes the guesswork, and dependencies=[] and dependencies=None are honest too.

Declare state before the Effects that read it

use_effect(effect)  # effect reads `count`\ncount, set_count = use_state(0)\n

ReactPy reads the closure's cells while resolving dependencies, and count's cell is not filled yet: ValueError: Cell is empty. The component renders as a blank region, with the error in the model (debug on) or in the server log (debug off). State first, Effects after.

"},{"location":"learn/synchronizing-with-effects/#step-3-add-clean-up-if-needed","title":"Step 3: Add clean-up if needed","text":"

Return a function from the Effect. ReactPy runs it before the next run of that same Effect and again when the component unmounts:

app.py Run
@component\ndef chat_room(room):\n    status, set_status = use_state(\"offline\")\n\n    def effect():\n        # ReactPy runs the returned function before the *next* run of this\n        # same Effect, and again when the component unmounts. A connection\n        # without a clean-up is a permanent leak in a long-lived server.\n        close = chat_server.connect(room)\n        set_status(f\"connected to {room}\")\n\n        def cleanup():\n            close()\n            set_status(\"offline\")\n\n        return cleanup\n\n    use_effect(effect, dependencies=[room])\n\n    return html.p(status)\n\n\n@component\ndef app():\n    room, set_room = use_state(\"lobby\")\n    connected, set_connected = use_state(True)\n\n    async def handle_room_click(event):\n        set_room(\"dev\" if room == \"lobby\" else \"lobby\")\n\n    async def handle_connected_click(event):\n        set_connected(not connected)\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_room_click},\n            f\"Switch room (now {room})\",\n        ),\n        html.button(\n            {\"onClick\": handle_connected_click},\n            \"Leave\" if connected else \"Join\",\n        ),\n        *([] if not connected else [chat_room(room)]),\n    )\n
# TODO\n

Switch rooms and the server log reads connect lobby, close lobby, connect dev \u2014 the teardown for the old room runs before the new one starts. Press Leave and the last connection closes. Verified.

This matters more in ReactPy than in React because of process lifetime. A closed browser tab destroys everything with it. A ReactPy server does not: a subscription you forgot to release is still live after the user went home, still holding your component, still firing callbacks into a view nobody can see. The counter-proof, measured in a Layout:

Effect body Subscribers after 4 renders One publish fires After unmount returns nothing 4 4 callbacks 4 still attached returns unsubscribe 1 1 callback 0

Same component, same re-renders. Without the clean-up, one message ran the handler four times.

Clean-up is what makes restarts free

Whenever a dependency changes, ReactPy stops the old Effect and starts a new one. The rule of thumb is that nobody should be able to tell whether the Effect ran once or ran setup \u2192 cleanup \u2192 setup. Write it that way and restarts cost nothing; skip the clean-up and every dependency change leaks.

"},{"location":"learn/synchronizing-with-effects/#reading-the-newest-value-from-a-long-lived-effect","title":"Reading the newest value from a long-lived Effect","text":"

Here is the trap that bites hardest with use_async_effect. An Effect's body closes over the render that created it. If that Effect then lives a long time \u2014 a polling loop, a subscription \u2014 it acts on values that are already stale.

The fix is to keep the value in a ref, assigning it during render. The ref object is the same one on every render, so a reader that wakes up later sees the newest write:

app.py Run
@component\ndef connection_monitor():\n    threshold, set_threshold = use_state(HIGH_THRESHOLD)\n    alerts, set_alerts = use_state([])\n    # Write the newest value during render so a reader that wakes up later\n    # gets this render's number rather than the one captured at mount.\n    latest_threshold = use_ref(threshold)\n    latest_threshold.current = threshold\n\n    async def watch():\n        while True:\n            await asyncio.sleep(0.2)\n            # SLOW_LATENCY stands in for a real measurement.\n            if SLOW_LATENCY > latest_threshold.current:\n                set_alerts(append_alert)\n\n    # `dependencies=[]` keeps ONE task alive for the component's lifetime.\n    # Reading `threshold` directly here would freeze it at its first value.\n    use_async_effect(watch, dependencies=[])\n\n    async def handle_threshold_click(event):\n        set_threshold(LOW_THRESHOLD if threshold == HIGH_THRESHOLD else HIGH_THRESHOLD)\n\n    return html.div(\n        html.p(f\"threshold: {threshold}\"),\n        html.button(\n            {\"onClick\": handle_threshold_click},\n            \"Toggle threshold\",\n        ),\n        html.p(f\"alerts: {len(alerts)}\"),\n    )\n
# TODO\n

Toggle the threshold from 50 to 10 and alerts start appearing; toggle back and they stop. The single long-lived task never restarts and always reads the current number. Verified.

The alternative \u2014 usually the better one \u2014 is to put the value in the dependency list so a change tears the Effect down and builds a new one with fresh values. Use the ref when restarting is expensive: an open socket, a loop with accumulated state.

"},{"location":"learn/synchronizing-with-effects/#common-patterns","title":"Common patterns","text":""},{"location":"learn/synchronizing-with-effects/#synchronizing-with-a-non-reactpy-object","title":"Synchronizing with a non-ReactPy object","text":"

Third-party objects are imperative: you construct them, call setters, destroy them. State is the wrong home for one (constructing it would re-run each render, and setting it would trigger a render), so keep it in a ref and let Effects drive its lifecycle:

app.py Run
@component\ndef report(name):\n    page_size, set_page_size = use_state(\"letter\")\n    # The engine lives in a ref: it has to survive renders, and changing it\n    # must not itself trigger a render.\n    engine_box: Ref[ReportEngine | None] = use_ref(None)\n\n    def create_engine():\n        engine = ReportEngine(name)\n        engine_box.current = engine\n\n        def destroy():\n            engine.destroy()\n            engine_box.current = None\n\n        return destroy\n\n    use_effect(create_engine, dependencies=[name])\n\n    def apply_page_size():\n        if engine_box.current is not None:\n            engine_box.current.set_page_size(page_size)\n\n    use_effect(apply_page_size, dependencies=[page_size])\n\n    async def handle_page_size_click(event):\n        set_page_size(\"a4\" if page_size == \"letter\" else \"letter\")\n\n    return html.div(\n        html.h2(name),\n        html.p(f\"page size: {page_size}\"),\n        html.button(\n            {\"onClick\": handle_page_size_click},\n            \"Toggle page size\",\n        ),\n    )\n
# TODO\n

Two Effects, two dependency lists: one creates and destroys the object when name changes, the other pushes page_size into it. Verified against the object's own log: created q3, set_page_size letter, set_page_size a4, destroyed q3 on unmount \u2014 one instance, kept in step, cleaned up.

Two Effects in the same component run in declaration order, which is why create_engine is registered first: the second Effect can assume the object exists. That ordering holds within a component; across components it does not. Children's Effects run before their parent's, and siblings run in reverse order \u2014 measured ['child effect b', 'child effect a', 'parent effect']. Never write code that needs a parent to have run first.

"},{"location":"learn/synchronizing-with-effects/#subscribing-to-a-stream","title":"Subscribing to a stream","text":"

The shape is always the same: subscribe inside the Effect, unsubscribe in the clean-up, name the topic in the dependency list:

app.py Run
@component\ndef scroll_log(topic):\n    lines, set_lines = use_state([])\n\n    def effect():\n        # The clean-up is what keeps the subscriber count at 1 across\n        # re-subscriptions. Forget it and every render adds another callback.\n        def on_message(message):\n            set_lines(lambda previous: [*previous, message])\n\n        unsubscribe = ticker.subscribe(topic, on_message)\n        return unsubscribe\n\n    use_effect(effect, dependencies=[topic])\n\n    return html.div(\n        html.h2(topic),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(lines)]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n    watching, set_watching = use_state(True)\n\n    async def handle_topic_click(event):\n        set_topic(\"sports\" if topic == \"news\" else \"news\")\n\n    async def handle_watching_click(event):\n        set_watching(not watching)\n\n    async def handle_click(event):\n        ticker.publish(topic, \"ping\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_topic_click},\n            \"Switch topic\",\n        ),\n        html.button(\n            {\"onClick\": handle_watching_click},\n            \"Stop watching\" if watching else \"Watch\",\n        ),\n        html.button({\"onClick\": handle_click}, \"Ping\"),\n        *([] if not watching else [scroll_log(topic)]),\n    )\n
# TODO\n

Verified through the bus's own log: subscribe news, unsubscribe news, subscribe sports, unsubscribe sports, subscribe news, unsubscribe news across a full switch-and-leave cycle \u2014 exactly one subscriber live at any moment.

Note that the callback is defined inside the Effect, so it closes over this Effect's values and the subscription is rebuilt when the dependencies change. Defining it in the component body instead would leave the subscription holding the first version of it.

"},{"location":"learn/synchronizing-with-effects/#fetching-data","title":"Fetching data","text":"

Fetching belongs in use_async_effect, and cancellation is what makes it safe:

app.py Run
@component\ndef report_view(name):\n    rows, set_rows = use_state(\"nothing loaded yet\")\n\n    async def fetch():\n        set_rows(f\"loading {name} ...\")\n        result = await catalog.fetch(name)\n        # This line is reached only if nobody cancelled the task. When `name`\n        # changes, ReactPy cancels the running task before this line runs, so\n        # an answer for the OLD report can never overwrite the new one.\n        set_rows(result)\n\n    use_async_effect(fetch, dependencies=[name])\n\n    return html.div(html.h2(name), html.p(rows))\n\n\n@component\ndef app():\n    name, set_name = use_state(\"orders\")\n\n    async def handle_name_click(event):\n        set_name(\"users\" if name == \"orders\" else \"orders\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_name_click},\n            \"Toggle report\",\n        ),\n        report_view(name),\n    )\n
# TODO\n

The example is deliberately lopsided: orders takes 0.8 s and users takes 0.05 s. Press Toggle report while the slow one is in flight and the screen settles on users: 500 rows and stays there. The data source recorded that it computed only the users answer \u2014 the orders task was cancelled inside its await and never reached its set_rows. Verified.

That is ReactPy's version of React's ignore flag, and it is stronger: the cancelled run's remaining lines never execute at all. Two consequences:

  • Code after an await may never run. Anything that must happen \u2014 closing a handle, releasing a lock \u2014 goes in a try / except asyncio.CancelledError pair, and re-raises so asyncio can finish cancelling.
  • An Effect that sets state it depends on loops forever. If a component appears to spin, read what the Effect awaits, what it depends on, and what it sets.

Good alternatives to fetching in an Effect

Manual fetching in an Effect works, and carries the same drawbacks here as in React: no deduplication, no cache shared between components, and a request that cannot start until the component exists. ReactPy has a better option available by default \u2014 fetch during a parent's render, or at startup, and pass the result down as a prop. The child then renders with data instead of loading it. Keep the Effect for data that genuinely depends on something only the connection knows, such as use_location.

"},{"location":"learn/synchronizing-with-effects/#sending-analytics","title":"Sending analytics","text":"

Report a page view when the page changes, not when the component happens to render:

app.py Run
@component\ndef page_view(path):\n    draft, set_draft = use_state(\"\")\n\n    def effect():\n        # Depends on `path` only: editing the field re-renders the component\n        # but does not report another visit.\n        analytics.log_visit(path)\n\n    use_effect(effect, dependencies=[path])\n\n    async def handle_draft_change(event):\n        set_draft(await event.target.value)\n\n    return html.div(\n        html.h2(path),\n        html.p(f\"total reports sent: {len(analytics.sent)}\"),\n        html.input(\n            {\n                \"placeholder\": \"notes\",\n                \"onChange\": handle_draft_change,\n            }\n        ),\n    )\n\n\n@component\ndef app():\n    path, set_path = use_state(\"/home\")\n\n    async def handle_path_click(event):\n        set_path(\"/about\" if path == \"/home\" else \"/home\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_path_click},\n            \"Navigate\",\n        ),\n        page_view(path),\n    )\n
# TODO\n

The Effect depends on path only. Type into the field and the report count stays put; press Navigate and it climbs by one. Verified: two typing re-renders produced zero reports, two navigations produced two.

"},{"location":"learn/synchronizing-with-effects/#not-an-effect-initializing-the-application","title":"Not an Effect: initializing the application","text":"

Logic that should run once for the whole process does not belong in a component at all \u2014 a component's Effect runs once per connection, and a server serves many:

# module level: runs when the module is imported, once\nconfigure_logging()\nPOOL = create_pool()\n

Put it in the ASGI app's startup, or in the module that builds the ReactPy executor. Reach for a dependencies=[] Effect only when the work is genuinely per-connection. Never guard it with my_component.started = True: the function object is module-scope, so the first visitor flips the flag for everyone (VDOM Mutations).

"},{"location":"learn/synchronizing-with-effects/#not-an-effect-buying-a-product","title":"Not an Effect: buying a product","text":"

An Effect is not the place for something that must happen exactly once per user action, because you cannot promise how many times an Effect runs \u2014 dependencies change, components unmount, processes restart. Charging a card belongs in an event handler, where a click is the only trigger:

async def handle_buy(event):\n    set_status(\"buying...\")\n    order = place_order(sku)\n    set_status(f\"ordered {order.id}\")\n
"},{"location":"learn/synchronizing-with-effects/#deep-dive-each-render-has-its-own-effects","title":"Deep dive: each render has its own Effects","text":"

An Effect belongs to the render that created it. Its closure holds that render's props and state, and it never sees a later one. The dependency list chooses which render's Effect survives; it does not update the one that did:

app.py Run
@component\ndef audit_log(room, message):\n    entries, set_entries = use_state([])\n\n    def effect():\n        # This closure sees the `message` from the render that CREATED this\n        # Effect -- not the newest one. The dependency list says when to make\n        # a new Effect; the values it reads are frozen from that render.\n        set_entries(lambda previous: [*previous, f\"{room} <- {message}\"])\n\n    use_effect(effect, dependencies=[room])\n\n    return html.ul(\n        [html.li({\"key\": index}, line) for index, line in enumerate(entries)]\n    )\n\n\n@component\ndef app():\n    room, set_room = use_state(\"lobby\")\n    message, set_message = use_state(\"hello\")\n\n    async def handle_message_click(event):\n        set_message(message + \"!\")\n\n    async def handle_room_click(event):\n        set_room(\"dev\" if room == \"lobby\" else \"lobby\")\n\n    return html.div(\n        html.p(f\"room={room} message={message!r}\"),\n        html.button({\"onClick\": handle_message_click}, \"Type !\"),\n        html.button(\n            {\"onClick\": handle_room_click},\n            \"Switch room\",\n        ),\n        audit_log(room, message),\n    )\n
# TODO\n

Press Type ! twice, then Switch room. The log gains lobby <- hello from the mount, and then dev <- hello!! \u2014 the Effect that ran for dev was created by the render whose message was already 'hello!!'. Verified. No entry ever records an intermediate message, because only the renders whose dependencies changed created an Effect.

This is why \"my Effect reads an old value\" has exactly two correct answers: add the value to the dependency list, or read it through a ref.

"},{"location":"learn/synchronizing-with-effects/#there-is-no-double-invoke-in-development","title":"There is no double-invoke in development","text":"

React remounts every component twice under Strict Mode to expose Effects that are not restart-safe. ReactPy has no Strict Mode and never calls your component twice \u2014 verified: a component that increments a counter in its own body counted exactly one render per render.

Two consequences:

  • Nothing will find your unclean Effects for you. The restart-safety check React gives away for free is one you have to run yourself: render the component, unmount it, mount it again, and assert the external system is back where it started. A reactpy.core.layout.Layout in a pytest makes that a ten-line test.
  • Do not add artificial guards to compensate. if already_ran.current: return inside an Effect is evidence the dependency list is wrong, not a fix. Write the clean-up so a restart is free and the question stops mattering.
"},{"location":"learn/synchronizing-with-effects/#recap","title":"Recap","text":"
  • An Effect is a side effect caused by rendering; an event handler is one caused by a user action. Prefer the handler.
  • Effects are scheduled after the render completes, in the connection's own task. They do not block the render, and the browser may not have the update yet.
  • They run on the server: no DOM, no window, no element to focus.
  • use_effect takes a plain def; use_async_effect takes a zero-argument async def. One mismatch fails loudly, the other (the old async def effect(stop) shape) fails silently.
  • dependencies has four settings. Omitted means inferred from the closure, not \"every render\" \u2014 and the comparison is ==, not identity.
  • Return a clean-up function. It runs before the Effect's next run and on unmount. Skipping it leaks, and a leak in a long-lived server process leaks forever.
  • A long-lived Effect sees a frozen snapshot; read current values from a ref, or restart the Effect by depending on them.
"},{"location":"learn/synchronizing-with-effects/#related","title":"Related","text":"
  • Use Effect and Use Async Effect \u2014 full parameter reference
  • You Might Not Need an Effect \u2014 the cases that belong back in the render
  • Separating Events from Effects \u2014 reading the newest state without restarting an Effect
  • Removing Effect Dependencies \u2014 when to omit a dependency, safely
  • Referencing Values with Refs \u2014 the box that survives renders
"},{"location":"learn/thinking-in-react/","title":"Thinking in React","text":""},{"location":"learn/thinking-in-react/#overview","title":"Overview","text":"

React can change how you think about the designs you look at and the apps you build. When you build a user interface with React, you will first break it apart into pieces called components. Then, you will describe the different visual states for each of your components. Finally, you will connect your components together so that the data flows through them. In this tutorial, we\u2019ll guide you through the thought process of building a searchable product data table with React.

"},{"location":"learn/thinking-in-react/#start-with-the-mockup","title":"Start with the mockup","text":"

Imagine that you already have a JSON API and a mockup from a designer.

The JSON API returns some data that looks like this:

[\n    {\n        \"category\": \"Fruits\",\n        \"price\": \"$1\",\n        \"stocked\": true,\n        \"name\": \"Apple\"\n    },\n    {\n        \"category\": \"Fruits\",\n        \"price\": \"$1\",\n        \"stocked\": true,\n        \"name\": \"Dragonfruit\"\n    },\n    {\n        \"category\": \"Fruits\",\n        \"price\": \"$2\",\n        \"stocked\": false,\n        \"name\": \"Passionfruit\"\n    },\n    {\n        \"category\": \"Vegetables\",\n        \"price\": \"$2\",\n        \"stocked\": true,\n        \"name\": \"Spinach\"\n    },\n    {\n        \"category\": \"Vegetables\",\n        \"price\": \"$4\",\n        \"stocked\": false,\n        \"name\": \"Pumpkin\"\n    },\n    {\n        \"category\": \"Vegetables\",\n        \"price\": \"$1\",\n        \"stocked\": true,\n        \"name\": \"Peas\"\n    }\n]\n

The mockup looks like this:

To implement a UI in React, you will usually follow the same five steps.

"},{"location":"learn/thinking-in-react/#step-1-break-the-ui-into-a-component-hierarchy","title":"Step 1: Break the UI into a component hierarchy","text":"

Start by drawing boxes around every component and subcomponent in the mockup and naming them. If you work with a designer, they may have already named these components in their design tool. Ask them!

Depending on your background, you can think about splitting up a design into components in different ways:

  • Programming--use the same techniques for deciding if you should create a new function or object. One such technique is the single responsibility principle, that is, a component should ideally only do one thing. If it ends up growing, it should be decomposed into smaller subcomponents.
  • CSS--consider what you would make class selectors for. (However, components are a bit less granular.)
  • Design--consider how you would organize the design's layers.

If your JSON is well-structured, you'll often find that it naturally maps to the component structure of your UI. That's because UI and data models often have the same information architecture--that is, the same shape. Separate your UI into components, where each component matches one piece of your data model.

There are five components on this screen:

  1. filterable_product_table (grey) contains the entire app.
  2. search_bar (blue) receives the user input.
  3. product_table (lavender) displays and filters the list according to the user input.
  4. product_category_row (green) displays a heading for each category.
  5. product_row (yellow) displays a row for each product.

If you look at product_table (lavender), you'll see that the table header (containing the \"Name\" and \"Price\" labels) isn't its own component. This is a matter of preference, and you could go either way. For this example, it is a part of product_table because it appears inside the product_table's list. However, if this header grows to be complex (e.g., if you add sorting), you can move it into its own product_table_header component.

Now that you've identified the components in the mockup, arrange them into a hierarchy. Components that appear within another component in the mockup should appear as a child in the hierarchy:

  • filterable_product_table
    • search_bar
    • product_table
      • product_category_row
      • product_row
"},{"location":"learn/thinking-in-react/#step-2-build-a-static-version-in-react","title":"Step 2: Build a static version in React","text":"

Now that you have your component hierarchy, it's time to implement your app. The most straightforward approach is to build a version that renders the UI from your data model without adding any interactivity... yet! It's often easier to build the static version first and add interactivity later. Building a static version requires a lot of typing and no thinking, but adding interactivity requires a lot of thinking and not a lot of typing.

To build a static version of your app that renders your data model, you'll want to build components that reuse other components and pass data using props. Props are a way of passing data from parent to child. (If you're familiar with the concept of state, don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.)

You can either build \"top down\" by starting with building the components higher up in the hierarchy (like filterable_product_table) or \"bottom up\" by working from components lower down (like product_row). In simpler examples, it\u2019s usually easier to go top-down, and on larger projects, it\u2019s easier to go bottom-up.

app.pystyles.css Run
from reactpy import component, html\n\n\n@component\ndef product_category_row(category):\n    return html.tr(html.th({\"colSpan\": \"2\"}, category))\n\n\n@component\ndef product_row(product):\n    if product[\"stocked\"]:\n        name = product[\"name\"]\n    else:\n        name = html.span({\"style\": {\"color\": \"red\"}}, product[\"name\"])\n    return html.tr(html.td(name), html.td(product[\"price\"]))\n\n\n@component\ndef product_table(products):\n    rows = []\n    last_category = None\n    for product in products:\n        if product[\"category\"] != last_category:\n            rows.append(\n                product_category_row(product[\"category\"], key=product[\"category\"])\n            )\n        rows.append(product_row(product, key=product[\"name\"]))\n        last_category = product[\"category\"]\n\n    return html.table(\n        html.thead(html.tr(html.th(\"Name\"), html.th(\"Price\"))), html.tbody(rows)\n    )\n\n\n@component\ndef search_bar():\n    return html.form(\n        html.input({\"type\": \"text\", \"placeholder\": \"Search...\"}),\n        html.label(html.input({\"type\": \"checkbox\"}), \"Only show products in stock\"),\n    )\n\n\n@component\ndef filterable_product_table(products):\n    return html.div(search_bar(), product_table(products))\n\n\nPRODUCTS = [\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Apple\"},\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Dragonfruit\"},\n    {\"category\": \"Fruits\", \"price\": \"$2\", \"stocked\": False, \"name\": \"Passionfruit\"},\n    {\"category\": \"Vegetables\", \"price\": \"$2\", \"stocked\": True, \"name\": \"Spinach\"},\n    {\"category\": \"Vegetables\", \"price\": \"$4\", \"stocked\": False, \"name\": \"Pumpkin\"},\n    {\"category\": \"Vegetables\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Peas\"},\n]\n\n\n@component\ndef app():\n    return filterable_product_table(PRODUCTS)\n
body {\n    padding: 5px;\n}\nlabel {\n    display: block;\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\nth {\n    padding-top: 10px;\n}\ntd {\n    padding: 2px;\n    padding-right: 40px;\n}\n
# TODO\n

(If this code looks intimidating, go through the Quick Start first!)

After building your components, you'll have a library of reusable components that render your data model. Because this is a static app, the components will only return non-interactive HTML. The component at the top of the hierarchy (filterable_product_table) will take your data model as a prop. This is called one-way data flow because the data flows down from the top-level component to the ones at the bottom of the tree.

Pitfall

At this point, you should not be using any state values. That\u2019s for the next step!

"},{"location":"learn/thinking-in-react/#step-3-find-the-minimal-but-complete-representation-of-ui-state","title":"Step 3: Find the minimal but complete representation of UI state","text":"

To make the UI interactive, you need to let users change your underlying data model. You will use state for this.

Think of state as the minimal set of changing data that your app needs to remember. The most important principle for structuring state is to keep it DRY (Don't Repeat Yourself). Figure out the absolute minimal representation of the state your application needs and compute everything else on-demand. For example, if you're building a shopping list, you can store the items as an array in state. If you want to also display the number of items in the list, don't store the number of items as another state value--instead, read the length of your array.

Now think of all of the pieces of data in this example application:

  1. The original list of products
  2. The search text the user has entered
  3. The value of the checkbox
  4. The filtered list of products

Which of these are state? Identify the ones that are not:

  • Does it remain unchanged over time? If so, it isn't state.
  • Is it passed in from a parent via props? If so, it isn't state.
  • Can you compute it based on existing state or props in your component? If so, it definitely isn't state!

What's left is probably state.

Let's go through them one by one again:

  1. The original list of products is passed in as props, so it's not state.
  2. The search text seems to be state since it changes over time and can't be computed from anything.
  3. The value of the checkbox seems to be state since it changes over time and can't be computed from anything.
  4. The filtered list of products isn't state because it can be computed by taking the original list of products and filtering it according to the search text and value of the checkbox.

This means only the search text and the value of the checkbox are state! Nicely done!

Deep Dive

Props vs State

There are two types of \"model\" data in ReactPy: props and state. The two are very different:

  • Props are like arguments you pass to a function. They let a parent component pass data to a child component and customize its appearance. For example, a search_bar() component can pass a color prop to a badge() component you wrote. This is about your components: html.form and html.button are tag constructors, and what you hand them is an attribute dictionary, not props a component reads.
  • State is like a component\u2019s memory. It lets a component keep track of some information and change it in response to interactions. For example, a badge() component you wrote might keep track of is_hovered state. Tags cannot hold state \u2014 html.button is a function that returns a dictionary, and a dictionary has no memory between renders.

Props and state are different, but they work together. A parent component will often keep some information in state (so that it can change it), and pass it down to child components as their props. It's okay if the difference still feels fuzzy on the first read. It takes a bit of practice for it to really stick!

"},{"location":"learn/thinking-in-react/#step-4-identify-where-your-state-should-live","title":"Step 4: Identify where your state should live","text":"

After identifying your app\u2019s minimal state data, you need to identify which component is responsible for changing this state, or owns the state. Remember: React uses one-way data flow, passing data down the component hierarchy from parent to child component. It may not be immediately clear which component should own what state. This can be challenging if you\u2019re new to this concept, but you can figure it out by following these steps!

For each piece of state in your application:

  1. Identify every component that renders something based on that state.
  2. Find their closest common parent component\u2014a component above them all in the hierarchy.
  3. Decide where the state should live:
    1. Often, you can put the state directly into their common parent.
    2. You can also put the state into some component above their common parent.
    3. If you can't find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common parent component.

In the previous step, you found two pieces of state in this application: the search input text, and the value of the checkbox. In this example, they always appear together, so it makes sense to put them into the same place.

Now let's run through our strategy for them:

  1. Identify components that use state:
    • product_table needs to filter the product list based on that state (search text and checkbox value).
    • search_bar needs to display that state (search text and checkbox value).
  2. Find their common parent: The first parent component both components share is filterable_product_table.
  3. Decide where the state lives: We'll keep the filter text and checked state values in filterable_product_table.

So the state values will live in filterable_product_table.

Add state to the component with the use_state() Hook. Hooks are special functions that let you \"hook into\" React. Add two state variables at the top of filterable_product_table and specify their initial state:

@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = use_state(\"\")\n    in_stock_only, set_in_stock_only = use_state(False)\n

Then, pass filter_text and in_stock_only to product_table and search_bar as props:

html.div(\n    search_bar(filter_text=filter_text, in_stock_only=in_stock_only),\n    product_table(\n        products=products, filter_text=filter_text, in_stock_only=in_stock_only\n    ),\n)\n

You can start seeing how your application will behave. Edit the filter_text initial value from use_state('') to use_state('fruit') in the sandbox code below. You'll see both the search input text and the table update:

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = use_state(\"\")\n    in_stock_only, set_in_stock_only = use_state(False)\n\n    return html.div(\n        search_bar(filter_text=filter_text, in_stock_only=in_stock_only),\n        product_table(\n            products=products, filter_text=filter_text, in_stock_only=in_stock_only\n        ),\n    )\n\n\n@component\ndef product_category_row(category):\n    return html.tr(\n        html.th({\"colspan\": 2}, category),\n    )\n\n\n@component\ndef product_row(product):\n    if product[\"stocked\"]:\n        name = product[\"name\"]\n    else:\n        name = html.span({\"style\": {\"color\": \"red\"}}, product[\"name\"])\n\n    return html.tr(\n        html.td(name),\n        html.td(product[\"price\"]),\n    )\n\n\n@component\ndef product_table(products, filter_text, in_stock_only):\n    rows = []\n    last_category = None\n\n    for product in products:\n        if filter_text.lower() not in product[\"name\"].lower():\n            continue\n        if in_stock_only and not product[\"stocked\"]:\n            continue\n        if product[\"category\"] != last_category:\n            rows.append(\n                product_category_row(product[\"category\"], key=product[\"category\"])\n            )\n        rows.append(product_row(product, key=product[\"name\"]))\n        last_category = product[\"category\"]\n\n    return html.table(\n        html.thead(\n            html.tr(\n                html.th(\"Name\"),\n                html.th(\"Price\"),\n            ),\n        ),\n        html.tbody(rows),\n    )\n\n\n@component\ndef search_bar(filter_text, in_stock_only):\n    return html.form(\n        html.input({\"type\": \"text\", \"value\": filter_text, \"placeholder\": \"Search...\"}),\n        html.label(\n            html.input({\"type\": \"checkbox\", \"checked\": in_stock_only}),\n            \"Only show products in stock\",\n        ),\n    )\n\n\nPRODUCTS = [\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Apple\"},\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Dragonfruit\"},\n    {\"category\": \"Fruits\", \"price\": \"$2\", \"stocked\": False, \"name\": \"Passionfruit\"},\n    {\"category\": \"Vegetables\", \"price\": \"$2\", \"stocked\": True, \"name\": \"Spinach\"},\n    {\"category\": \"Vegetables\", \"price\": \"$4\", \"stocked\": False, \"name\": \"Pumpkin\"},\n    {\"category\": \"Vegetables\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Peas\"},\n]\n\n\n@component\ndef app():\n    return filterable_product_table(PRODUCTS)\n
body {\n    padding: 5px;\n}\nlabel {\n    display: block;\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\nth {\n    padding-top: 5px;\n}\ntd {\n    padding: 2px;\n}\n
# TODO\n

Notice that editing the form doesn't work yet.

In the code above, product_table and search_bar read the filter_text and in_stock_only props to render the table, the input, and the checkbox. For example, here is how search_bar populates the input value:

@component\ndef search_bar(filter_text, in_stock_only):\n    return html.form(\n        html.input(\n            {\n                \"type\": \"text\",\n                \"value\": filter_text,\n                \"placeholder\": \"Search...\",\n            }\n        ),\n        html.p(\n            html.input({\"type\": \"checkbox\", \"checked\": in_stock_only}),\n            \"Only show products in stock\",\n        ),\n    )\n

However, you haven't added any code to respond to the user actions like typing yet. This will be your final step.

"},{"location":"learn/thinking-in-react/#step-5-add-inverse-data-flow","title":"Step 5: Add inverse data flow","text":"

Currently your app renders correctly with props and state flowing down the hierarchy. But to change the state according to user input, you will need to support data flowing the other way: the form components deep in the hierarchy need to update the state in filterable_product_table.

React makes this data flow explicit, but it requires a little more typing than two-way data binding. If you try to type or check the box in the example above, you'll see that React ignores your input. This is intentional. By writing html.input({\"value\": filter_text}), you've set the value attribute of the html.input to always be equal to the filter_text state passed in from filterable_product_table. Since filter_text state is never set, the input never changes.

You want to make it so whenever the user changes the form inputs, the state updates to reflect those changes. The state is owned by filterable_product_table, so only it can call set_filter_text and set_in_stock_only. To let search_bar update the filterable_product_table's state, you need to pass these functions down to search_bar:

@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = hooks.use_state(\"\")\n    in_stock_only, set_in_stock_only = hooks.use_state(False)\n\n    return html.div(\n        search_bar(\n            filter_text=filter_text,\n            in_stock_only=in_stock_only,\n            set_filter_text=set_filter_text,\n            set_in_stock_only=set_in_stock_only,\n        )\n    )\n

Inside the search_bar, you will add the onChange event handlers and set the parent state from them:

async def handle_filter_text_change(event):\n    set_filter_text(await event.target.value)\n\n\nhtml.input(\n    {\n        \"type\": \"text\",\n        \"value\": filter_text,\n        \"placeholder\": \"Search...\",\n        \"onChange\": handle_filter_text_change,\n    }\n)\n

Now the application fully works!

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = use_state(\"\")\n    in_stock_only, set_in_stock_only = use_state(False)\n\n    return html.div(\n        search_bar(\n            filter_text=filter_text,\n            in_stock_only=in_stock_only,\n            set_filter_text=set_filter_text,\n            set_in_stock_only=set_in_stock_only,\n        ),\n        product_table(\n            products=products, filter_text=filter_text, in_stock_only=in_stock_only\n        ),\n    )\n\n\n@component\ndef product_category_row(category):\n    return html.tr(\n        html.th({\"colspan\": 2}, category),\n    )\n\n\n@component\ndef product_row(product):\n    if product[\"stocked\"]:\n        name = product[\"name\"]\n    else:\n        name = html.span({\"style\": {\"color\": \"red\"}}, product[\"name\"])\n\n    return html.tr(\n        html.td(name),\n        html.td(product[\"price\"]),\n    )\n\n\n@component\ndef product_table(products, filter_text, in_stock_only):\n    rows = []\n    last_category = None\n\n    for product in products:\n        if filter_text.lower() not in product[\"name\"].lower():\n            continue\n        if in_stock_only and not product[\"stocked\"]:\n            continue\n        if product[\"category\"] != last_category:\n            rows.append(\n                product_category_row(product[\"category\"], key=product[\"category\"])\n            )\n        rows.append(product_row(product, key=product[\"name\"]))\n        last_category = product[\"category\"]\n\n    return html.table(\n        html.thead(\n            html.tr(\n                html.th(\"Name\"),\n                html.th(\"Price\"),\n            ),\n        ),\n        html.tbody(rows),\n    )\n\n\n@component\ndef search_bar(filter_text, in_stock_only, set_filter_text, set_in_stock_only):\n    async def handle_filter_text_change(event):\n        set_filter_text(await event.target.value)\n\n    async def handle_in_stock_only_change(event):\n        set_in_stock_only(await event.target.checked)\n\n    return html.form(\n        html.input(\n            {\n                \"type\": \"text\",\n                \"value\": filter_text,\n                \"placeholder\": \"Search...\",\n                \"onChange\": handle_filter_text_change,\n            }\n        ),\n        html.label(\n            html.input(\n                {\n                    \"type\": \"checkbox\",\n                    \"checked\": in_stock_only,\n                    \"onChange\": handle_in_stock_only_change,\n                }\n            ),\n            \"Only show products in stock\",\n        ),\n    )\n\n\nPRODUCTS = [\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Apple\"},\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Dragonfruit\"},\n    {\"category\": \"Fruits\", \"price\": \"$2\", \"stocked\": False, \"name\": \"Passionfruit\"},\n    {\"category\": \"Vegetables\", \"price\": \"$2\", \"stocked\": True, \"name\": \"Spinach\"},\n    {\"category\": \"Vegetables\", \"price\": \"$4\", \"stocked\": False, \"name\": \"Pumpkin\"},\n    {\"category\": \"Vegetables\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Peas\"},\n]\n\n\n@component\ndef app():\n    return filterable_product_table(PRODUCTS)\n
body {\n    padding: 5px;\n}\nlabel {\n    display: block;\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\nth {\n    padding: 4px;\n}\ntd {\n    padding: 2px;\n}\n
# TODO\n

You can learn all about handling events and updating state in the Adding Interactivity section.

"},{"location":"learn/thinking-in-react/#where-to-go-from-here","title":"Where to go from here","text":"

This was a very brief introduction to how to think about building components and applications with React. You can start a React project right now or dive deeper on all the syntax used in this tutorial.

"},{"location":"learn/tools-and-packages/","title":"Tools, Libraries, and Packages","text":"

Use external ReactPy packages and tools to help you build your projects.

"},{"location":"learn/tools-and-packages/#first-party-packages","title":"First party packages","text":"

The Reactive Python organization provides several packages and tools to help you build your projects.

Package Description ReactPy Router URL router for ReactPy to help you build single-page applications. ReactPy Django A Django integration for ReactPy. Comes with several Django-exclusive features."},{"location":"learn/tools-and-packages/#third-party-packages","title":"Third party packages","text":"

There are also a number of third-party packages that can help you build your projects.

You can find a list of these packages on the ReactPy GitHub Discussions.

"},{"location":"learn/tutorial-material-ui/","title":"Tutorial material ui","text":"

Planned / Undeveloped

This tutorial is planned, but is missing a key feature before this page can be written.

See this issue for more details.

"},{"location":"learn/tutorial-react-bootstrap/","title":"Tutorial react bootstrap","text":"

Planned / Undeveloped

This tutorial is planned, but is missing a key feature before this page can be written.

See this issue for more details.

"},{"location":"learn/tutorial-tic-tac-toe/","title":"Tutorial: Tic-Tac-Toe","text":""},{"location":"learn/tutorial-tic-tac-toe/#overview","title":"Overview","text":"

You will build a small tic-tac-toe game. This tutorial assumes no prior ReactPy knowledge, and the techniques it covers \u2014 components, props, state, and lifting state up \u2014 are the ones you will use in every app you write.

Note

This tutorial is for people who prefer to learn by doing. If you would rather meet each concept one at a time, start with Your First Component instead.

The tutorial is divided into four sections:

  • Setup for the tutorial gives you a starting point.
  • The basics teaches you the fundamentals: components, props, and state.
  • Completing the game teaches you the most common techniques.
  • Adding time travel shows why immutability matters.
"},{"location":"learn/tutorial-tic-tac-toe/#what-are-you-building","title":"What are you building?","text":"

You will build an interactive tic-tac-toe game. Here is the finished result:

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef square(value, on_click):\n    return html.button(\n        {\"className\": \"square\", \"onClick\": on_click},\n        value,\n    )\n\n\n@component\ndef board(x_is_next, squares, on_play):\n    def handle_click(i):\n        async def inner(event):\n            \"\"\"\n            Due to a quirk of Python, if your event handler needs args other than\n            `event`, you will need to create a wrapper function as seen above.\n            Ref: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/cell-var-from-loop.html\n            \"\"\"\n            if calculate_winner(squares) or squares[i]:\n                return\n\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            on_play(next_squares)\n\n        return inner\n\n    winner = calculate_winner(squares)\n    status = (\n        f\"Winner: {winner}\" if winner else \"Next player: \" + (\"X\" if x_is_next else \"O\")\n    )\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n\n\n@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n    current_move, set_current_move = use_state(0)\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    def handle_play(next_squares):\n        next_history = history[: current_move + 1]\n        next_history.append(next_squares)\n        set_history(next_history)\n        set_current_move(len(next_history) - 1)\n\n    def jump_to(next_move):\n        async def go_to_move(event):\n            set_current_move(next_move)\n\n        return go_to_move\n\n    moves = []\n    for move, _squares in enumerate(history):\n        description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n\n        moves.append(\n            html.li(\n                {\"key\": move},\n                html.button({\"onClick\": jump_to(move)}, description),\n            )\n        )\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol(moves)),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for line in lines:\n        a, b, c = line\n        if not squares:\n            continue\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

If the code does not make sense yet, do not worry \u2014 that is the point of the tutorial. Play the game before you read it. You will notice a list to the right of the board that records every move and lets you jump back to any of them. That list is what the last section builds toward.

Note

Throughout this tutorial, app.py is the file you edit, and the styles.css tab shows the stylesheet it relies on. ReactPy serves your components, not files on your disk, so the stylesheet is loaded into the page in your ASGI entry module. Setup for the tutorial shows how.

"},{"location":"learn/tutorial-tic-tac-toe/#setup-for-the-tutorial","title":"Setup for the tutorial","text":"

The code below is your starting point: a single square, with no interactivity.

app.pystyles.css Run
@component\ndef square():\n    return html.button({\"className\": \"square\"}, \"X\")\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

It should render one empty-looking square containing an X.

Note

You can follow along in your own editor instead. You will need:

  1. Python 3.11 or newer, with pip install reactpy[asgi] uvicorn[standard]
  2. A file app.py containing the example above
  3. A file styles.css containing the stylesheet above
  4. A file main.py that mounts app.py and injects the stylesheet:

    from pathlib import Path\n\nfrom app import square\n\nfrom reactpy import html\nfrom reactpy.executors.asgi import ReactPy\n\n# ReactPy serves the components, not the files on your disk, so the stylesheet\n# is read here and injected into the page's <head>.\nCSS = Path(\"styles.css\").read_text()\n\nmy_app = ReactPy(square, html_head=html.head(html.style(CSS)))\n
  5. uvicorn main:my_app, then open the URL it prints

If the setup fights you, do not let it stop you \u2014 follow along online and try the local setup again later. See Creating a Standalone React App for the details.

"},{"location":"learn/tutorial-tic-tac-toe/#the-basics-components-props-and-state","title":"The basics: components, props, and state","text":""},{"location":"learn/tutorial-tic-tac-toe/#inspecting-the-starter-code","title":"Inspecting the starter code","text":"

The example defines a component. In ReactPy, a component is a function that returns what part of the interface should look like. Read it line by line:

@component\ndef square():\n    return html.button({\"className\": \"square\"}, \"X\")\n

The first two lines are boilerplate: @component turns the function below it into a component, and html gives you a function for every HTML tag.

The def square(): line defines a function called square. Unlike React, ReactPy component names are snake_case and lowercase \u2014 there is no capital-letter rule, because ReactPy tells a component from an HTML tag by whether you call it, not by how it is spelled.

The return line gives back a button. html.button(...) is a call to a function, not an element literal: children come after the attribute dictionary as positional arguments, so \"X\" is the text inside the button. {\"className\": \"square\"} is that attribute dictionary, and className is the DOM attribute name \u2014 attribute keys are forwarded to the browser verbatim, so they keep React's camelCase spelling.

"},{"location":"learn/tutorial-tic-tac-toe/#building-the-board","title":"Building the board","text":"

The board needs nine squares, not one. Copying the button twice is tempting:

@component\ndef square():\n    return html.button({\"className\": \"square\"}, \"X\"), html.button({\"className\": \"square\"}, \"X\")\n

That does not fail the way React's \"adjacent JSX elements\" error does. The comma builds a two-item tuple; ReactPy does not know you meant two children, coerces it with str(), and the page shows the VDOM dictionaries as literal text:

({'children': ['X'], 'attributes': {'className': 'square'}, 'tagName': 'button'}, ...)\n

Verified through Layout.render(): the model's only child is that string. If the page ever shows you a Python repr, a missing call or a stray comma is the usual cause. A component returns exactly one thing. To return several children, wrap them in a fragment with html.fragment(...), which groups children without adding an element of its own:

@component\ndef square():\n    return html.fragment(\n        html.button({\"className\": \"square\"}, \"X\"),\n        html.button({\"className\": \"square\"}, \"X\"),\n    )\n

Now copy-paste your way to nine squares and... they come out in a single line, not a grid. Group them into rows with divs carrying the className the stylesheet already defines, and number the squares so you can tell them apart:

app.pystyles.css Run
@component\ndef square():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"1\"),\n            html.button({\"className\": \"square\"}, \"2\"),\n            html.button({\"className\": \"square\"}, \"3\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"4\"),\n            html.button({\"className\": \"square\"}, \"5\"),\n            html.button({\"className\": \"square\"}, \"6\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"7\"),\n            html.button({\"className\": \"square\"}, \"8\"),\n            html.button({\"className\": \"square\"}, \"9\"),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

You now have a tic-tac-toe board. But you have a naming problem: the component is called square and renders a whole board. Rename it to board.

At this point your code should look like this:

app.pystyles.css Run
@component\ndef board():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"1\"),\n            html.button({\"className\": \"square\"}, \"2\"),\n            html.button({\"className\": \"square\"}, \"3\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"4\"),\n            html.button({\"className\": \"square\"}, \"5\"),\n            html.button({\"className\": \"square\"}, \"6\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"7\"),\n            html.button({\"className\": \"square\"}, \"8\"),\n            html.button({\"className\": \"square\"}, \"9\"),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Note

That is a lot of typing. Copying code from this page is fine, but you will learn more by typing each snippet at least once yourself.

"},{"location":"learn/tutorial-tic-tac-toe/#passing-data-through-props","title":"Passing data through props","text":"

Nine hardcoded buttons means nine places to change when a square's content becomes dynamic. Instead, extract a square component and give it the value to display \u2014 a prop:

@component\ndef square(value):\n    return html.button({\"className\": \"square\"}, value)\n

There is no JSX, so props are simply Python parameters. Call the component and pass the argument:

square(\"A\")\n

Update board to call square nine times, passing a different letter each time so you can see the values arrive:

app.pystyles.css Run
@component\ndef square(value):\n    return html.button({\"className\": \"square\"}, value)\n\n\n@component\ndef board():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(\"A\"),\n            square(\"B\"),\n            square(\"C\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(\"D\"),\n            square(\"E\"),\n            square(\"F\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(\"G\"),\n            square(\"H\"),\n            square(\"I\"),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Notice how board and square are both called the same way, with no ceremony, while html.div and html.button are calls into the html object. Components are just functions you wrote; tags are functions ReactPy provides.

Props are read-only. square cannot assign to value to change what its parent passed, and ReactPy has no mechanism to help it try. Data flows down; to change it, the component that owns it must change it.

"},{"location":"learn/tutorial-tic-tac-toe/#making-an-interactive-component","title":"Making an interactive component","text":"

Now make a square fill itself when clicked. Add a handler inside square and register it under onClick:

@component\ndef square():\n    async def handle_click(event):\n        print(\"clicked!\")\n\n    return html.button({\"className\": \"square\", \"onClick\": handle_click})\n

A handler is a coroutine, and ReactPy calls it with exactly one argument \u2014 the event. Name it event, even if you ignore it.

Unlike React, there is no browser console to open: ReactPy components run on the server, so print lands in the terminal running your app, next to the URL. Click a square several times and you will see the line repeat there.

Pitfall

The key must be camelCase onClick. The React-looking on_click is accepted and shipped to the browser like any other on* key \u2014 and then nothing listens for it, because no browser event is named on_click. Verified: the server-side model holds a registered on_click handler while clicking the button leaves the component's state at 0. The button renders, looks normal, and never fires, with no error and no log line anywhere. If a control does nothing, check the spelling of the key first. See Responding to Events.

Next, make the square remember that it was clicked. ReactPy gives components memory with the use_state hook, which returns the current value and the function that changes it:

app.pystyles.css Run
# None is what an unfilled square holds; ReactPy renders None as nothing.\nEMPTY_SQUARE: str | None = None\n\n\n@component\ndef square():\n    value, set_value = use_state(EMPTY_SQUARE)\n\n    async def handle_click(event):\n        set_value(\"X\")\n\n    return html.button({\"className\": \"square\", \"onClick\": handle_click}, value)\n\n\n@component\ndef board():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(),\n            square(),\n            square(),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(),\n            square(),\n            square(),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(),\n            square(),\n            square(),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

value, set_value = use_state(EMPTY_SQUARE) unpacks a pair: the state you read and the setter you call. Its argument is the starting value, used only on the component's first render. Calling set_value asks ReactPy to render the component again, and that render sees the new value.

The value is None, meaning \"empty\", and ReactPy renders None as nothing, so a fresh board is blank. It lives in a module-level constant annotated str | None, which is what lets a type checker accept both None and \"X\" in that square \u2014 without it, the type is inferred from None alone and assigning a mark is flagged. See Use State.

Note that ReactPy renders True and False as the words True and False, so None is the only value that disappears \u2014 React's habit of writing cond && <X/> has no working Python equivalent. See Conditional Rendering.

Click each square and watch them fill independently. Nine square() calls contain nine separate use_state calls, and each one keeps its own value.

That is a defect, not a feature. Each square's value lives inside that square, so no other part of the program can see the whole board \u2014 which means nothing can answer \"has someone won?\". The next section fixes this by moving the state to where the whole board is visible.

"},{"location":"learn/tutorial-tic-tac-toe/#completing-the-game","title":"Completing the game","text":"

Right now the game has a serious limitation: nobody can win.

"},{"location":"learn/tutorial-tic-tac-toe/#lifting-state-up","title":"Lifting state up","text":"

To detect a winner you need the whole board in one place. The fix is to move the state out of square and into board, which already renders all nine squares \u2014 a move called lifting state up.

board owns a list of nine values and passes each one down:

app.pystyles.css Run
@component\ndef square(value, on_click):\n    return html.button({\"className\": \"square\", \"onClick\": on_click}, value)\n\n\n@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n\n    def handle_click(i):\n        async def inner(event):\n            if squares[i]:\n                return\n            next_squares = squares.copy()\n            next_squares[i] = \"X\"\n            set_squares(next_squares)\n\n        return inner\n\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

square is now stateless: it takes value to display and on_click to call, and remembers nothing. That is the shape of most components you will write \u2014 they take data in and report events out, and the state lives above them.

Note the spelling distinction, because both forms are correct in different places. on_click above is a Python parameter \u2014 a prop name you invented, so it is snake_case like every other identifier you define. onClick is the attribute dictionary key the browser needs, so it keeps its camelCase spelling. Naming a prop onClick or writing the attribute key on_click are both mistakes, in opposite directions.

Two details are worth pausing on.

First, the handler needs to know which square was clicked, but ReactPy hands a handler exactly one argument: the event. The answer is a factory, as handle_click(i) does here \u2014 it returns the handler that closes over i:

def handle_click(i):\n    async def inner(event):\n        ...\n\n    return inner\n\n\nhtml.button({\"onClick\": handle_click(i)}, squares[i])\n

A lambda is not available for this. It cannot be a coroutine, so ReactPy refuses it as a handler; functools.partial(handle_click, i) over a two-parameter handler is the other shape which works \u2014 but prefer the factory. ReactPy unwraps a partial before reading your code, yet it then looks for preventDefault() and friends on your function's first parameter, and a partial has already filled that one in. So a preventDefault() inside a partial-bound handler goes undetected and has to be declared with @event(prevent_default=True). The factory has no such problem: its inner async def inner(event) still leads with the event, so detection finds what you wrote.

The factory has a sharp edge inside a loop \u2014 the closure captures the variable, not the value \u2014 which Picking a key demonstrates.

Second, board renders three rows by hand, so square(...) appears nine times with nine different indices. A loop is shorter, and once you write one you will need keys \u2014 see Rendering Lists.

"},{"location":"learn/tutorial-tic-tac-toe/#why-immutability-is-important","title":"Why immutability is important","text":"

Look closely at what the handler does before it calls the setter:

next_squares = squares.copy()\nnext_squares[i] = \"X\"\nset_squares(next_squares)\n

It copies the list, changes the copy, and hands the copy to the setter. There are two ways to change data: mutate it in place, or replace it with a modified copy. The obvious Python code takes the first route, and it fails in a way that is hard to diagnose:

app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n\n    def handle_click(i):\n        # Mutating in place and handing back the same object: ReactPy compares\n        # the new value with the old one, sees the same object, and schedules no\n        # render at all. The button looks fine and never responds.\n        async def inner(event):\n            squares[i] = \"X\"\n            set_squares(squares)\n\n        return inner\n\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Click a square. Nothing happens \u2014 no error, no log line, no warning. The handler runs; the board just never changes.

Verified against this example: after three clicks on that square, the board is still nine empty cells. The cause is that set_squares(squares) passes back the very same object that is already in state. ReactPy compares the new value against the current one, finds them equal, and skips the render \u2014 an optimisation that turns an in-place mutation into a silent no-op.

Pitfall

Mutate-then-set-the-same-object is the most common ReactPy bug and the quietest one. squares[i] = \"X\" followed by set_squares(squares) schedules no render at all. Neither does mutating without calling the setter. Always build a new list \u2014 squares.copy(), a list comprehension, or slicing \u2014 and pass that. See Updating Arrays in State.

Immutability earns its keep twice here. It is what makes the board update at all, and it is what lets the last section keep every past move around: because old lists are never modified, they stay valid and can be re-displayed later.

Deep Dive

React's version of this section argues that immutability lets React skip re-rendering parts of the tree that did not change. That argument does not apply to ReactPy: there is no memo and no render bailout, so a child component's body re-runs whenever its parent renders, even with identical props. Immutable data is still required \u2014 for the reason above, and so equality checks mean what you expect \u2014 but do not expect it to save you renders. See You Might Not Need an Effect.

"},{"location":"learn/tutorial-tic-tac-toe/#taking-turns","title":"Taking turns","text":"

Players can fill squares, but only with X. Add a second piece of state to board for whose turn it is, and flip it after every move:

app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n    x_is_next, set_x_is_next = use_state(True)\n\n    def handle_click(i):\n        async def inner(event):\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            set_squares(next_squares)\n            set_x_is_next(not x_is_next)\n\n        return inner\n\n    status = \"Next player: \" + (\"X\" if x_is_next else \"O\")\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Squares now alternate X and O, and the status line above the board tracks the next player.

One defect remains: click a filled square and the other player overwrites it. That happens because the handler never checks whether the square is already taken. Return early instead of playing over it:

async def inner(event):\n    if squares[i]:\n        return\n    next_squares = squares.copy()\n    ...\n
app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n    x_is_next, set_x_is_next = use_state(True)\n\n    def handle_click(i):\n        async def inner(event):\n            if squares[i]:\n                return\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            set_squares(next_squares)\n            set_x_is_next(not x_is_next)\n\n        return inner\n\n    status = \"Next player: \" + (\"X\" if x_is_next else \"O\")\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Verified: clicking square 0 twice leaves X in place rather than replacing it with O, and the other squares still play.

The guard is the first line of the handler, before any state is built or set. Putting it after set_squares would let the invalid move through and try to undo it \u2014 an expensive, error-prone shape. Return early, then proceed.

"},{"location":"learn/tutorial-tic-tac-toe/#declaring-a-winner","title":"Declaring a winner","text":"

Add a helper that checks the eight winning lines. It knows nothing about ReactPy, so read it as plain Python:

def calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n

It returns \"X\", \"O\", or None. Where you put it in the file does not matter; keeping it at the bottom means you do not scroll past it every time.

Call it in two places \u2014 to stop play after a win, and to write the status line:

app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n    x_is_next, set_x_is_next = use_state(True)\n\n    def handle_click(i):\n        async def inner(event):\n            if calculate_winner(squares) or squares[i]:\n                return\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            set_squares(next_squares)\n            set_x_is_next(not x_is_next)\n\n        return inner\n\n    winner = calculate_winner(squares)\n    status = (\n        f\"Winner: {winner}\" if winner else \"Next player: \" + (\"X\" if x_is_next else \"O\")\n    )\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Get three in a row and the status changes to Winner: X, after which no square responds \u2014 the guard returns early because calculate_winner(squares) is truthy.

Notice that the winner is not stored in state. It is computed from squares during every render, which is both simpler and impossible to get out of sync. If a value can be derived from state you are already storing, do not add a second piece of state for it: two sources of truth drift, and one number cannot. See Reacting to Input with State.

The game is playable. What it cannot do is show you how it got here \u2014 which is where immutability finally pays off.

"},{"location":"learn/tutorial-tic-tac-toe/#adding-time-travel","title":"Adding time travel","text":"

Because every move produced a new list instead of modifying the old one, the previous boards still exist. All that is left is to keep them.

The history is a list of boards, one per move:

[\n    # before the first move\n    [None] * 9,\n    # after the first move\n    [None, None, None, None, \"X\", None, None, None, None],\n    # after the second move\n    [None, None, None, None, \"X\", None, None, None, \"O\"],\n]\n
"},{"location":"learn/tutorial-tic-tac-toe/#lifting-state-up-again","title":"Lifting state up, again","text":"

Add a game component above board to hold that history. It will also decide which move you are looking at, so board must stop owning state and start receiving what to draw.

Start by having game render board inside the layout the stylesheet expects, and keep the history and the current move as its own state. board keeps its own handle_click, but now reports the finished move upward through an on_play prop instead of storing it:

@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n\n    def handle_play(next_squares):\n        set_history([*history, next_squares])\n\n    current_move = len(history) - 1\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol()),\n    )\n

x_is_next and current_squares are not state. They are computed from history during the render, which is why they cannot drift out of sync with it.

app.pystyles.css Run
@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n\n    def handle_play(next_squares):\n        set_history([*history, next_squares])\n\n    current_move = len(history) - 1\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol()),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

The game plays exactly as it did before \u2014 only now the whole history lives in one component that can see all of it.

"},{"location":"learn/tutorial-tic-tac-toe/#showing-the-past-moves","title":"Showing the past moves","text":"

You have a list of moves in state; render it as a list of buttons. Where React reaches for map, Python reaches for a comprehension or a loop:

moves = []\nfor move in range(len(history)):\n    description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n    moves.append(html.li(html.button({\"onClick\": jump_to(move)}, description)))\n

Then render html.ol(moves) inside the game-info div. Add a current_move state variable and an empty jump_to(move) that does nothing yet, so the list has somewhere to go:

app.pystyles.css Run
@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n    current_move, set_current_move = use_state(0)\n\n    def handle_play(next_squares):\n        next_history = deepcopy(history[: current_move + 1])\n        next_history.append(next_squares)\n        set_history(next_history)\n        set_current_move(len(next_history) - 1)\n\n    def jump_to(move):\n        async def inner(event):\n            # TODO: make the board show move\n            pass\n\n        return inner\n\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    moves = []\n    for move in range(len(history)):\n        description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n        moves.append(html.li(html.button({\"onClick\": jump_to(move)}, description)))\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol(moves)),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Make a few moves and the list grows: Go to game start, Go to move #1, Go to move #2. Clicking one does nothing, because jump_to is still the placeholder \u2014 that is the next step.

Pitfall

With REACTPY_DEBUG on, the server logs Key not specified for child in list ... for every row. ReactPy is telling you the same thing React does: a list you will re-render needs a key on each item. It is a warning, not an error, so a list can ship without one \u2014 and misbehave later in ways that are hard to trace back. The fix is Picking a key.

A related trap: pass a list, not a generator. html.ol(html.li(...) for ... in ...) skips the key check entirely, with the warning Did not verify key-path integrity of children in generator ... - pass a sequence, and an iterator stored in state is worse still \u2014 it is single-use, so the first render shows the rows and every later render shows nothing.

"},{"location":"learn/tutorial-tic-tac-toe/#picking-a-key","title":"Picking a key","text":"

When you re-render a list, ReactPy has to work out which item is which. It cannot read your intent, so you tell it: give each item a key.

html.li({\"key\": move}, html.button({\"onClick\": jump_to(move)}, description))\n

key is a reserved name, and it is spelled two different ways depending on what you are creating \u2014 mixing them up is a hard error:

You are creating Spell it Passing it the other way raises an element inside the attribute dict: html.li({\"key\": move}, ...) TypeError: Vdom.__call__() got an unexpected keyword argument 'key' a component as a keyword: row(item, key=item[\"id\"]) silently meaningless, since the dict is the attribute dict

A key identifies an item across renders. Change a component's key and ReactPy destroys it and builds a fresh one, discarding its state \u2014 which is a useful trick on its own, and the subject of Preserving and Resetting State. Keys need only be unique among siblings, not globally.

For the move list, the move's index is a legitimate key: moves are never reordered, inserted in the middle, or deleted, so index and identity agree. That is the exception. When items can be added or removed, the array index stops identifying anything, and state quietly lands on the wrong row \u2014 use a real identifier instead. See Rendering Lists.

"},{"location":"learn/tutorial-tic-tac-toe/#implementing-time-travel","title":"Implementing time travel","text":"

jump_to(move) only has to say which move you are looking at:

def jump_to(move):\n    async def inner(event):\n        set_current_move(move)\n\n    return inner\n

And the values derived during render must follow current_move rather than the end of the list:

x_is_next = current_move % 2 == 0\ncurrent_squares = history[current_move]\n

Playing a move now has one more rule. If you jump back and play from there, the moves after that point are no longer the future \u2014 they are discarded:

def handle_play(next_squares):\n    next_history = history[: current_move + 1]\n    next_history.append(next_squares)\n    set_history(next_history)\n    set_current_move(len(next_history) - 1)\n

The slice history[: current_move + 1] keeps everything up to and including the move you jumped back to, dropping the rest, and set_current_move puts you at the new end.

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef square(value, on_click):\n    return html.button(\n        {\"className\": \"square\", \"onClick\": on_click},\n        value,\n    )\n\n\n@component\ndef board(x_is_next, squares, on_play):\n    def handle_click(i):\n        async def inner(event):\n            \"\"\"\n            Due to a quirk of Python, if your event handler needs args other than\n            `event`, you will need to create a wrapper function as seen above.\n            Ref: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/cell-var-from-loop.html\n            \"\"\"\n            if calculate_winner(squares) or squares[i]:\n                return\n\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            on_play(next_squares)\n\n        return inner\n\n    winner = calculate_winner(squares)\n    status = (\n        f\"Winner: {winner}\" if winner else \"Next player: \" + (\"X\" if x_is_next else \"O\")\n    )\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n\n\n@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n    current_move, set_current_move = use_state(0)\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    def handle_play(next_squares):\n        next_history = history[: current_move + 1]\n        next_history.append(next_squares)\n        set_history(next_history)\n        set_current_move(len(next_history) - 1)\n\n    def jump_to(next_move):\n        async def go_to_move(event):\n            set_current_move(next_move)\n\n        return go_to_move\n\n    moves = []\n    for move, _squares in enumerate(history):\n        description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n\n        moves.append(\n            html.li(\n                {\"key\": move},\n                html.button({\"onClick\": jump_to(move)}, description),\n            )\n        )\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol(moves)),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for line in lines:\n        a, b, c = line\n        if not squares:\n            continue\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Verified against this file: play three moves to put X in the corners, click Go to game start, and the board is empty again with Next player: X. Jump to Go to move #1, play a different square, and the list retracts to two entries \u2014 the abandoned branch is gone.

None of that would work if the handlers mutated squares in place. Every stored board would be the same list, and rewinding would show the current game. This is the payoff for the rule from Why immutability is important.

Pitfall

Do not store one board and a separate list of moves that points into it. The history must own the boards, and the boards must never change once stored. If you find yourself reaching for deepcopy here, that is a sign something is still being mutated \u2014 with the code above, the boards are never modified after they are created, so a plain slice is enough.

"},{"location":"learn/tutorial-tic-tac-toe/#final-cleanup","title":"Final cleanup","text":"

The finished game holds its state in three places, each as low as it can be while still seeing what it needs:

  • game owns history and current_move, because it renders both the board and the move list.
  • board owns nothing. It receives x_is_next, squares, and on_play.
  • square owns nothing. It receives value and on_click.

That is the pattern this tutorial was built around. State belongs to the component that needs it and to every component above it \u2014 no lower, because then a sibling cannot see it, and no higher, because then unrelated renders redo the work. When two components need the same data, lift it to their nearest common ancestor.

"},{"location":"learn/tutorial-tic-tac-toe/#wrapping-up","title":"Wrapping up","text":"
  • A component is a @component function returning one thing; several children go inside html.fragment(...) or a list.
  • Props are ordinary Python parameters, and they are read-only.
  • use_state gives a component memory; calling the setter re-renders it.
  • Handlers take exactly one argument, the event. Extra context is bound on the Python side, usually with a factory function.
  • Never mutate a list you have put in state \u2014 copy it, or the setter silently does nothing.
  • Derive what you can during render instead of storing it: winner, x_is_next, current_squares.
  • Lift state to the lowest component that can see everything that needs it.
  • Give list items a key \u2014 in the attribute dict for elements, as a keyword for components.
  • Time travel is free once your data is immutable, and impossible when it is not.
"},{"location":"learn/tutorial-tic-tac-toe/#related","title":"Related","text":"
  • Quick Start \u2014 the same building blocks at a faster pace
  • Your First Component \u2014 components and props in more depth
  • State: A Component's Memory \u2014 how use_state behaves
  • Adding Interactivity \u2014 events, handlers, and @event options
  • Updating Arrays in State \u2014 replace, never mutate
  • Sharing State Between Components \u2014 lifting state up
  • Rendering Lists \u2014 keys, and when the index is the wrong choice
  • Extracting State Logic into a Reducer \u2014 the next step once a component has too many state variables
"},{"location":"learn/updating-arrays-in-state/","title":"Updating Arrays in State","text":""},{"location":"learn/updating-arrays-in-state/#overview","title":"Overview","text":"

Lists are mutable in Python, but you should treat them as immutable when you store them in state. Just like with objects, when you want to update a list stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new list.

You will learn

  • How to add, remove, or change items in a list in ReactPy state
  • How to update an object inside of a list
  • Why a mutating update is dropped without any error
"},{"location":"learn/updating-arrays-in-state/#updating-lists-without-mutation","title":"Updating lists without mutation","text":"

In JavaScript you copy arrays with the spread syntax, and mutating methods like push() and pop() are easy to reach for by mistake. Python's equivalent methods \u2014 append(), extend(), insert(), remove(), pop(), sort(), reverse() \u2014 are in-place and return None, so the mistake looks different and fails harder:

async def handle_add(event):\n    items.append({\"id\": next_id, \"text\": text})   # mutates, returns None\n    set_items(items)                              # same object -> ignored\n

Nothing happens. ReactPy compares the new value against the old one with == (core/hooks.py, strictly_equal), sees nothing changed, and schedules no render. There is no error, no warning, and nothing in the browser console. Verified directly: mutating then setting the same list produces no render at all. Nor does setting a new list that happens to compare equal \u2014 which is why set_rows([*rows, 4]) works while mutating rows and passing it back does not.

So whenever you want to update a list, pass a new list to the setter. Python gives you two tools for that:

Instead of (mutates) Use (new list) items.append(x) [*items, x] items.remove(x) [i for i in items if i is not x] items[i] = x [*items[:i], x, *items[i + 1:]] items.pop(i) [*items[:i], *items[i + 1:]] items.insert(i, x) [*items[:i], x, *items[i:]] items.reverse() list(reversed(items)) items.sort(key=f) sorted(items, key=f)

Never store an iterator in state

This one has no JavaScript analogue and will bite you. Python's filter() and map() return lazy, single-use iterators, not lists:

set_items(filter(lambda i: i[\"id\"] != id_to_delete, items))   # \ud83d\udea9\n

That renders correctly once, which is exactly what makes it so hard to diagnose. The iterator is consumed while ReactPy walks the children, so the next render of that same state sees an exhausted iterator and displays nothing. Verified: a list of three items filtered to \"a,c\" on the first render, then an unrelated state change rendered an empty line.

Always build a list \u2014 a comprehension is clearest, list(filter(...)) if you must:

set_items([i for i in items if i[\"id\"] != id_to_delete])   # correct\n
"},{"location":"learn/updating-arrays-in-state/#adding-to-a-list","title":"Adding to a list","text":"

The common case is adding an item. The handler creates a new list ending with the new item:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state([{\"id\": 0, \"text\": \"Walk the dog\"}])\n    text, set_text = use_state(\"\")\n\n    async def handle_add(event):\n        event.preventDefault()\n        if not text:\n            return\n        # A new list; `items.append(...)` would be silently ignored.\n        next_id = max((item[\"id\"] for item in items), default=-1) + 1\n        set_items([*items, {\"id\": next_id, \"text\": text}])\n        set_text(\"\")\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.form(\n            {\"onSubmit\": handle_add},\n            html.input(\n                {\n                    \"value\": text,\n                    \"placeholder\": \"To do...\",\n                    \"onChange\": handle_text_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Add\"),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

Type a task and submit; it appends while the existing ones stay put. Note that the new id is computed from the existing items rather than from a module-level counter. A global counter is shared by every visitor to the server, so two users would collide on the same key \u2014 derive ids from the state you already have, or use uuid4().

"},{"location":"learn/updating-arrays-in-state/#removing-from-a-list","title":"Removing from a list","text":"

The most common removal is filtering out one item:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n            {\"id\": 2, \"text\": \"Teach the fish to juggle\"},\n        ]\n    )\n\n    def handle_delete(id_to_keep):\n        async def handler(event):\n            # filter() gives a new list; items.remove(...) would mutate it.\n            set_items([item for item in items if item[\"id\"] != id_to_keep])\n\n        return handler\n\n    return html.ul(\n        [\n            html.li(\n                {\"key\": item[\"id\"]},\n                item[\"text\"],\n                html.button({\"onClick\": handle_delete(item[\"id\"])}, \"Delete\"),\n            )\n            for item in items\n        ]\n    )\n
# TODO\n

[item for item in items if item[\"id\"] != id_to_keep] produces a brand-new list containing everything except the deleted item, and the original is left alone.

"},{"location":"learn/updating-arrays-in-state/#transforming-every-item","title":"Transforming every item","text":"

To apply a change to every item, build a new list by transforming each element:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n        ]\n    )\n\n    async def handle_click(event):\n        # map() becomes a comprehension; the old items stay untouched.\n        set_items([{\"id\": item[\"id\"], \"text\": item[\"text\"] + \"!\"} for item in items])\n\n    return html.div(\n        html.button({\"onClick\": handle_click}, \"Add !\"),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

Each item becomes a new dict, because mutating item[\"text\"] in place would leave the previous state pointing at the same mutated object.

Comprehensions do not modify the original

React's docs point out that filter() doesn't touch the original array, and a Python comprehension has the same property \u2014 it reads the old list and returns a fresh one. The catch, stated above, is that the built-in filter() does not return a list at all.

"},{"location":"learn/updating-arrays-in-state/#replacing-an-item","title":"Replacing an item","text":"

Unlike most languages, Python has no setAt(i, x) method, and the obvious items[i] = x mutates. Build the list from slices instead:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n        ]\n    )\n    text, set_text = use_state(\"\")\n\n    async def handle_replace(event):\n        event.preventDefault()\n        if not text:\n            return\n        # Replace one item by building a whole new list. Mutating\n        # items[0][\"text\"] would be invisible to ReactPy.\n        set_items([{\"id\": 0, \"text\": text}, *items[1:]])\n        set_text(\"\")\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.form(\n            {\"onSubmit\": handle_replace},\n            html.input(\n                {\n                    \"value\": text,\n                    \"placeholder\": \"Replace the first item\",\n                    \"onChange\": handle_text_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Replace\"),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

[{\"id\": 0, \"text\": text}, *items[1:]] replaces the first item and copies the rest. For an arbitrary index, [*items[:i], new_item, *items[i + 1:]] is the general form.

"},{"location":"learn/updating-arrays-in-state/#inserting-into-a-list","title":"Inserting into a list","text":"

To insert an item somewhere other than the end, slice around the insertion point:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n        ]\n    )\n    text, set_text = use_state(\"\")\n\n    async def handle_insert(event):\n        event.preventDefault()\n        if not text:\n            return\n        # Slicing builds the new list; list.insert() would mutate in place.\n        set_items([*items[:1], {\"id\": 3, \"text\": text}, *items[1:]])\n        set_text(\"\")\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.form(\n            {\"onSubmit\": handle_insert},\n            html.input(\n                {\n                    \"value\": text,\n                    \"placeholder\": \"Insert at index 1\",\n                    \"onChange\": handle_text_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Insert\"),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

[*items[:1], new_item, *items[1:]] puts the new item at index 1. This is how React's \"insert into an array\" example translates; list.insert() mutates and is unusable here.

"},{"location":"learn/updating-arrays-in-state/#making-other-changes-to-a-list","title":"Making other changes to a list","text":"

The rule is uniform: copy first, then operate on the copy, or use a function that already returns a new list.

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n            {\"id\": 2, \"text\": \"Feed the fish\"},\n        ]\n    )\n    log, set_log = use_state([])\n\n    def record(label, next_items):\n        set_items(next_items)\n        set_log([*log, f\"{label}: {', '.join(i['text'] for i in next_items)}\"])\n\n    async def handle_reversed_click(event):\n        record(\"reversed\", list(reversed(items)))\n\n    async def handle_sorted_click(event):\n        record(\"sorted\", sorted(items, key=lambda i: i[\"text\"]))\n\n    async def handle_filtered_click(event):\n        record(\"filtered\", items[:2])\n\n    return html.div(\n        html.div(\n            html.button(\n                {\"onClick\": handle_reversed_click},\n                \"Reverse\",\n            ),\n            html.button(\n                {\"onClick\": handle_sorted_click},\n                \"Sort\",\n            ),\n            html.button(\n                {\"onClick\": handle_filtered_click},\n                \"Keep first two\",\n            ),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n        html.ul([html.li({\"key\": i}, line) for i, line in enumerate(log)]),\n    )\n
# TODO\n

reversed() returns an iterator, so wrap it in list(...); sorted() already returns a new list; items[:2] is a slice, which is already a new list.

list.sort() and list.reverse() return None

These two are the most expensive traps because the failure is invisible. items.sort() sorts in place and returns None, so set_items(items.sort()) stores None in state. Use sorted(items) and list(reversed(items)).

"},{"location":"learn/updating-arrays-in-state/#updating-objects-inside-lists","title":"Updating objects inside lists","text":"

The examples above replaced whole dicts, which is the safe default. When an item is a dict whose fields you want to change one at a time, you need two copies: a new list, and a new dict for the item that changed.

app.py Run
@component\ndef form():\n    artworks, set_artworks = use_state(initial_artworks())\n\n    def handle_toggle(id_to_toggle):\n        async def handler(event):\n            # Copy the LIST, and copy the one dict inside it that changed.\n            # A shallow copy would leave the dict shared with the old state.\n            set_artworks(\n                [\n                    (\n                        {**artwork, \"seen\": not artwork[\"seen\"]}\n                        if artwork[\"id\"] == id_to_toggle\n                        else artwork\n                    )\n                    for artwork in artworks\n                ]\n            )\n\n        return handler\n\n    return html.ul(\n        [\n            html.li(\n                {\"key\": artwork[\"id\"]},\n                html.label(\n                    html.input(\n                        {\n                            \"type\": \"checkbox\",\n                            \"checked\": artwork[\"seen\"],\n                            \"onChange\": handle_toggle(artwork[\"id\"]),\n                        }\n                    ),\n                    f\"{artwork['title']} - {artwork['artist']}\",\n                ),\n            )\n            for artwork in artworks\n        ]\n    )\n
# TODO\n

Tick one checkbox and only that artwork's \"seen\" flips \u2014 verified, the pair goes from [False, False] to [True, False]. The comprehension copies every item, and only the targeted one is rebuilt with the changed field.

A shallow copy of the list is not enough

It is tempting to write:

next_items = list(artworks)          # new list...\nnext_items[0][\"seen\"] = True         # ...but the SAME dicts inside\nset_items(next_items)\n

That is a new list containing the same dict objects. Two problems follow. The assignment mutates a dict the previous state still references, so the change is invisible to ReactPy's comparison. And anything else holding that dict \u2014 another component seeded from the same fixture, a cached render \u2014 sees it flip too.

Copying is shallow at every level in Python; there is no deep copy on assignment or unpacking. Build the changed dict explicitly, as the example does. See Updating a nested object for the object-side version of this.

Keep identity stable for unchanged items

Notice the else artwork branch: unchanged items are passed through as the same objects, not rebuilt. That is both cheaper and useful \u2014 if you rebuild everything on each keystroke, nothing can be recognised as unchanged later.

"},{"location":"learn/updating-arrays-in-state/#writing-concise-update-logic","title":"Writing concise update logic","text":"

React's docs finish this page with Immer, which lets you write draft.push(...) behind a proxy. ReactPy has no Immer and no equivalent. There is no proxy layer to record your mutations, and no use_immer to import.

What you have instead is what this page already used:

  • List unpacking and comprehensions \u2014 [*items, x], [f(i) for i in items], [*items[:i], x, *items[i:]]. Verbose but explicit, and it fails loudly rather than silently.
  • copy.deepcopy \u2014 for a structure too nested to copy by hand.
  • use_reducer \u2014 moves the update logic out of the component, which is where the verbosity actually starts to hurt. See Extracting State Logic into a Reducer.

A homegrown Immer is worth avoiding specifically because it would paper over the failure mode this page is about: mutations are more dangerous here than in the browser, because ReactPy decides whether to render by comparing the old and new values on the server \u2014 so a mutation that hands back the same object, or an equal one, is dropped without a trace.

"},{"location":"learn/updating-arrays-in-state/#recap","title":"Recap","text":"
  • Treat lists in state as read-only: never call append(), sort(), insert(), remove(), and friends on them.
  • Build a new list with [*items, x] or a comprehension, and pass that to the setter.
  • Mutating and passing the same list back is silently dropped \u2014 no render, no error.
  • Never store filter()/map() results in state; they are single-use iterators and the second render will be empty.
  • To change an object inside a list, copy the list and copy the changed dict.
  • There is no Immer in ReactPy; use comprehensions, deepcopy, or use_reducer.
"},{"location":"learn/updating-objects-in-state/","title":"Updating Objects in State","text":""},{"location":"learn/updating-objects-in-state/#overview","title":"Overview","text":"

State can hold any kind of value, including dicts and lists. But you shouldn't change objects that you hold in ReactPy state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.

You will learn

  • How to correctly update an object in ReactPy state
  • How to update a nested object without mutating it
  • What immutability is, and how not to break it
  • Why a mutating update can be dropped without any error
"},{"location":"learn/updating-objects-in-state/#whats-a-mutation","title":"What's a mutation?","text":"

To mutate something means to change it after it was created.

Objects and lists in Python are mutable: you can change their contents in place. The mutating operations are usually visible in the code you write \u2014 obj[\"key\"] = value, list.append(x), list.pop(), del list[i], d.update(other) \u2014 but there are more hiding behind method names that don't announce themselves (list.sort(), list.extend(), list.reverse(), dict.setdefault()).

In ReactPy, treat state as read-only. Every time your code renders, you work with the state that already exists, and you want to cause changes over time based on user input. Leaving all mutation to the setter keeps your code easy to understand. This applies to state in general, not just object state.

Mutating state doesn't just risk a bug \u2014 it can silently do nothing

In React, mutating state is a \"may render stale UI\" hazard. In ReactPy it is sharper, because the setter compares the new value against the old one before scheduling anything \u2014 and that comparison accepts equality, not just identity (core/hooks.py, strictly_equal). Anything that compares equal, including a brand-new object, schedules no render.

Measured behaviour, each case dispatched through reactpy.core.layout.Layout:

What the handler does Result Mutates a list in place, then set_x(same_list) no render at all Mutates in place, calls no setter no render set_x([*old, item]) \u2014 a new object renders

The failure is completely silent: no error, no warning, nothing in the browser console. Your handler ran, ReactPy agreed that nothing changed, and the UI never updates. This is why the read-only rule is not a style preference here.

"},{"location":"learn/updating-objects-in-state/#treat-state-as-read-only","title":"Treat state as read-only","text":"

Consider a form that lets the user change their profile. This handler reaches into the state object and assigns a field directly:

async def handle_first_name(event):\n    person[\"firstName\"] = await event.target.value   # \ud83d\udea9 Mutate!\n    set_person(person)                                # same object -> ignored\n

person is the very object ReactPy already holds, so the assignment changes it in place and then hands the same object back. ReactPy compares it against what it already has, finds nothing changed, and skips the render \u2014 the field appears frozen while your dict quietly holds the new value.

The fix is to always create a new object for the next state:

async def handle_first_name(event):\n    set_person({**person, \"firstName\": await event.target.value})\n

That is the pattern for every state update in ReactPy: build a new value, pass it to the setter.

A pointer-tracking dot shows the same rule with no form in sight. The handler builds a brand-new position dict on every move:

app.py Run
@component\ndef moving_dot():\n    position, set_position = use_state({\"x\": 0, \"y\": 0})\n\n    async def handle_move(event):\n        # Replace the whole object: ReactPy never merges a partial update.\n        set_position({\"x\": await event.clientX, \"y\": await event.clientY})\n\n    return html.div(\n        {\n            \"onPointerMove\": handle_move,\n            \"style\": {\n                \"position\": \"relative\",\n                \"width\": \"100%\",\n                \"height\": \"180px\",\n                \"background\": \"#f0f0f0\",\n            },\n        },\n        html.div(\n            {\n                \"style\": {\n                    \"position\": \"absolute\",\n                    \"background\": \"black\",\n                    \"borderRadius\": \"50%\",\n                    \"width\": \"20px\",\n                    \"height\": \"20px\",\n                    \"left\": \"-10px\",\n                    \"top\": \"-10px\",\n                    \"transform\": f\"translate({position['x']}px, {position['y']}px)\",\n                }\n            }\n        ),\n    )\n
# TODO\n

The \"onPointerMove\" handler is on the grey container, and the dot is positioned from the coordinates it reads. Move the pointer anywhere inside the box and the dot follows. await event.clientX and await event.clientY are ordinary proxy reads \u2014 the value is fetched from the browser at the await, not shipped up front \u2014 so React's pointer and drag demos port across unchanged.

There is no automatic merge

set_x(...) replaces the state. It does not merge a partial object into what was there. Verified: setting {\"a\": 9} over {\"a\": 1, \"b\": 2} leaves {'a': 9} \u2014 \"b\" is simply gone.

If you have read React tutorials, you may remember setState merging objects. That was the class API. React's own useState replaces, and so does ReactPy (core/hooks.py, _CurrentState.dispatch assigns self.value = new). Any ReactJS text saying \"React merges the object into your current state\" is describing an API that no longer applies \u2014 port those examples with an explicit {**old, ...}.

Local mutation is fine

Mutating a value you just created inside the current render is safe, because nothing else has ever seen it. The same applies to the rows = [] plus rows.append(...) loop you will write constantly when building children:

@component\ndef list_of_parts(parts):\n    # Built during this render, never handed out before.\n    sorted_parts = sorted(parts, key=lambda p: p[\"importance\"])\n    return html.ul(\n        [html.li({\"key\": p[\"id\"]}, p[\"title\"]) for p in sorted_parts]\n    )\n

What must not be mutated is state ReactPy gave you, and props you received. See Local mutation: Your component's little secret for the rendering-side version of this rule.

"},{"location":"learn/updating-objects-in-state/#copying-objects-with-the-spread-syntax","title":"Copying objects with the spread syntax","text":"

To update an object, create a new one from the old one so you can change any of its properties. Python has no ... spread operator, but {**obj, \"key\": value} is the same operation on dicts, and [*items, x] is its twin for lists.

Here the form keeps the other fields intact while replacing one:

app.py Run
@component\ndef form():\n    person, set_person = use_state(\n        {\n            \"firstName\": \"Jane\",\n            \"lastName\": \"Jacobs\",\n            \"email\": \"jane@example.com\",\n        }\n    )\n\n    async def handle_first_name(event):\n        # Copy everything, then override just this one field.\n        set_person({**person, \"firstName\": await event.target.value})\n\n    async def handle_last_name(event):\n        set_person({**person, \"lastName\": await event.target.value})\n\n    async def handle_email(event):\n        set_person({**person, \"email\": await event.target.value})\n\n    return html.div(\n        html.input({\"value\": person[\"firstName\"], \"onChange\": handle_first_name}),\n        html.input({\"value\": person[\"lastName\"], \"onChange\": handle_last_name}),\n        html.input({\"value\": person[\"email\"], \"onChange\": handle_email}),\n        html.hr(),\n        html.h1(f\"Hello, {person['firstName']} {person['lastName']}!\"),\n        html.a({\"href\": f\"mailto:{person['email']}\"}, person[\"email\"]),\n    )\n
# TODO\n

Type in any field and watch the heading update. Each handler spreads the current person into a fresh dict and overrides exactly one key.

An unpacked dict is a shallow copy

{**person} copies the top level only. Any nested dict or list inside it is still shared with the previous state \u2014 see Updating a nested object.

The destructuring equivalents

Where React's docs reach for const { a, ...rest } = obj, Python reads with indexing and writes with unpacking. For the \"everything but one key\" shape, the direct translation is a comprehension:

rest = {k: v for k, v in obj.items() if k != \"drop\"}\n
"},{"location":"learn/updating-objects-in-state/#using-a-single-event-handler-for-multiple-fields","title":"Using a single event handler for multiple fields","text":"

Repeating a near-identical handler per field gets tedious. One handler can take the field name:

app.py Run
@component\ndef form():\n    person, set_person = use_state(\n        {\"firstName\": \"Jane\", \"lastName\": \"Jacobs\", \"email\": \"jane@example.com\"}\n    )\n\n    # One handler for every field. ReactPy always calls a handler with the\n    # event as its single positional argument, so the field name is bound by\n    # the thin handlers below rather than delivered by the client.\n    async def handle_change(field, event):\n        set_person({**person, field: await event.target.value})\n\n    async def handle_first_name_change(event):\n        await handle_change(\"firstName\", event)\n\n    async def handle_last_name_change(event):\n        await handle_change(\"lastName\", event)\n\n    async def handle_email_change(event):\n        await handle_change(\"email\", event)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": person[\"firstName\"],\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": person[\"lastName\"],\n                \"onChange\": handle_last_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": person[\"email\"],\n                \"onChange\": handle_email_change,\n            }\n        ),\n        html.hr(),\n        html.h1(f\"Hello, {person['firstName']} {person['lastName']}!\"),\n    )\n
# TODO\n

Note how the field name is bound by the thin per-field handler, not delivered by the client. ReactPy invokes every event handler with the event as its single positional argument \u2014 verified, a handler declaring two required parameters fails with TypeError: ... missing 1 required positional argument: 'event'. There is no e.target.name shortcut either: name is readable, but reading it costs a round trip to the page, and it has to be read from the element which fired the event. Any extra context which is already known on the server is cheaper bound in Python \u2014 by a named handler, a closure, or a functools.partial.

Reach for use_reducer when handlers multiply

Several handlers that all edit one object is a sign the update logic wants its own home. See Extracting State Logic into a Reducer.

"},{"location":"learn/updating-objects-in-state/#updating-a-nested-object","title":"Updating a nested object","text":"

State often contains nested objects. To update a deeply nested value, copy the object all the way up from where the change happens.

app.py Run
@component\ndef form():\n    person, set_person = use_state(\n        {\n            \"firstName\": \"Jane\",\n            \"lastName\": \"Jacobs\",\n            \"email\": \"jane@example.com\",\n            \"shippingAddress\": {\n                \"street\": \"123 Maple Street\",\n                \"city\": \"Pretendville\",\n            },\n        }\n    )\n\n    async def handle_city(event):\n        address = {**person[\"shippingAddress\"], \"city\": await event.target.value}\n        set_person({**person, \"shippingAddress\": address})\n\n    return html.div(\n        html.label(\n            \"City: \",\n            html.input(\n                {\n                    \"value\": person[\"shippingAddress\"][\"city\"],\n                    \"onChange\": handle_city,\n                }\n            ),\n        ),\n        html.h1(person[\"firstName\"]),\n        html.p(person[\"shippingAddress\"][\"street\"]),\n        html.p(person[\"shippingAddress\"][\"city\"]),\n    )\n
# TODO\n

Two copies are required:

  1. Copy person[\"shippingAddress\"] with the new \"city\".
  2. Copy person, replacing \"shippingAddress\" with that new dict.

Writing person[\"shippingAddress\"][\"city\"] = ... would mutate the nested dict in place. Because that nested dict is the same object the previous state still refers to, the change is invisible to ReactPy's comparison and the update is dropped.

Objects are not really nested

Python makes this point sharper than JavaScript does. Given:

person = {\"name\": \"Jane\", \"theme\": {\"background\": \"white\"}}\ncopy = {**person}\n

copy[\"theme\"] and person[\"theme\"] are the same dict. Python never deep-copies on assignment or on unpacking \u2014 copying is shallow for dicts, lists, and everything else. To get a genuinely independent copy you either copy each level explicitly, as the example does, or use copy.deepcopy:

import copy\n\nset_people(copy.deepcopy(people))\n

deepcopy is the blunt instrument: correct, but slower, and it copies everything reachable, including objects you did not mean to duplicate. Prefer explicit copies for the paths you actually change, and keep state to plain dicts, lists, and primitives so deepcopy remains a safe fallback.

Shallow sharing is also why two components seeded from the same module-level fixture end up aliasing each other's data \u2014 see Updating Arrays in State for the list version of that bug.

"},{"location":"learn/updating-objects-in-state/#writing-concise-update-logic","title":"Writing concise update logic","text":"

React's docs reach for Immer here, which records your mutations through a proxy and turns them into copies. ReactPy has no Immer and no equivalent. There is no proxy layer, so nothing could observe your mutations, and there is no use_immer to import.

What you have instead:

  • Dict and list unpacking \u2014 {**obj, \"k\": v}, [*items, x]. This covers the overwhelming majority of updates and is what every example above uses.
  • copy.deepcopy \u2014 for the rare shape too deep to copy by hand.
  • use_reducer \u2014 does not shorten updates, but moves them out of the component so their verbosity costs you less. See Extracting State Logic into a Reducer.

Resist building your own Immer. A proxy that logs mutations would have to run on the server, after a websocket round trip, which is not the problem Immer solves in the browser. If update logic feels genuinely painful, that usually means the state shape is wrong \u2014 see Choosing the State Structure.

"},{"location":"learn/updating-objects-in-state/#recap","title":"Recap","text":"
  • Treat everything ReactPy hands you as state or props as read-only: never assign into it, never call a mutating method on it.
  • Build a new object with {**old, ...} and pass that to the setter.
  • Mutating in place and passing the same object back is silently dropped \u2014 ReactPy compares the new value with the old and schedules no render when they are equal. Nothing in the browser will tell you.
  • Copying is shallow at every level; copy all the way up from the field that changed, or use copy.deepcopy.
  • Mutating a value you created during this render is fine.
  • There is no Immer in ReactPy; use dict and list unpacking, or restructure the state.
"},{"location":"learn/vdom-mutations/","title":"VDOM Mutations","text":""},{"location":"learn/vdom-mutations/#overview","title":"Overview","text":"

A ReactPy element is a dict, and Python lets you assign into it. That is the whole topic of this page: what is safe to change, what is merely pointless, and what quietly corrupts the model that the server is holding on behalf of every connected browser. The rule that decides all of it is that a model is owned by exactly one render \u2014 before it, yours to shape; after it, ReactPy's.

You will learn

  • Why mutation before a render is fine and after it is not
  • Which parts of a built element are copies and which are shared
  • The module-level element that leaks one user's change into everyone's page
  • Safe shapes: build fresh, deepcopy, and transform at build time
"},{"location":"learn/vdom-mutations/#the-rule-a-model-belongs-to-its-render","title":"The rule: a model belongs to its render","text":"

ReactPy serializes your element to JSON and sends it. Once that has happened, the dictionary is no longer purely yours \u2014 the layout keeps live references to the same objects so it can patch a subtree later (core/layout.py, Layout._create_layout_update reuses model.current rather than rebuilding it).

Split your code on that line:

  • Before the element is returned \u2014 mutate freely. Building a model incrementally out of helper functions is normal Python and nothing is watching.
  • After it has been returned from a render \u2014 treat it as frozen. Changes now affect a model the client has already received, and the server has no idea they happened.

Nothing in ReactPy enforces this. There is no freeze, no copy-on-render, no warning. That is why the failure modes below are worth memorizing.

"},{"location":"learn/vdom-mutations/#what-is-safe-before-the-render","title":"What is safe before the render","text":"

VdomDict is a light dict subclass, and it is strict about its top-level keys \u2014 exactly tagName, children, attributes, eventHandlers, inlineJavaScript, importSource:

d = html.div({\"id\": \"pre\"}, \"x\")\n\nd[\"tagName\"] = \"section\"       # fine\nd[\"attributes\"][\"id\"] = \"a\"    # fine \u2014 but only because \"attributes\" exists\nd[\"children\"].append(\"tail\")   # fine\nd[\"key\"] = 1                   # KeyError: 'Invalid key: key'\nd[\"ref\"] = object()            # KeyError: 'Invalid key: ref'\n

Note the first line passes an attributes dict. html.div(\"x\") omits the attributes key entirely when there are none \u2014 verified, it returns {'children': ['x'], 'tagName': 'div'} \u2014 so d[\"attributes\"][\"id\"] = \"a\" on such an element raises KeyError: 'attributes' before any guard is reached. Use d.setdefault(\"attributes\", {})[\"id\"] = \"a\" when the element may not have had one.

The guard only covers the six top-level names. d[\"attributes\"][\"anything\"] is unvalidated, and so is every nested value \u2014 the validation happens at serialization, and only in debug mode.

Keys and refs are not top-level keys

The React shapes {\"key\": ...} and {\"ref\": ...} raise, because a VdomDict has no such slot. A key goes inside attributes for an element (html.li({\"key\": 1}, ...)) or is passed as a keyword to a component (Item(x, key=1)). A ref does not exist at all \u2014 see Manipulating the DOM with Refs.

"},{"location":"learn/vdom-mutations/#shallow-copy-deep-sharing","title":"Shallow copy, deep sharing","text":"

The constructors copy the top level, so the containers you passed in are not aliased into the element \u2014 but the copy is shallow, and nested values still point at your objects:

app.py Run
# The tag constructors copy the top level, so the element's own attributes and\n# children containers are separate objects from the ones you passed in.\nstyle = {\"color\": \"red\"}\nattributes = {\"id\": \"card\", \"style\": style}\nchildren = [\"hello\"]\n\nelement = html.div(attributes, *children)\n\nprint(element[\"attributes\"] is attributes)  # False -- a copy\nprint(element[\"children\"] is children)  # False -- a fresh list\n\n# But the copy is shallow: nested values are still the objects you handed over.\nprint(element[\"attributes\"].get(\"style\") is style)  # True\n\nstyle[\"color\"] = \"blue\"\nprint(element[\"attributes\"].get(\"style\"))  # {'color': 'blue'} -- changed after the fact\n
# TODO\n

The same applies to child elements: pass a model you built earlier and the parent stores that object, not a copy. Mutating the child later changes the parent. This is convenient for builders that assemble a tree in stages, and dangerous for anything that outlives the render \u2014 which is the subject of the next section.

"},{"location":"learn/vdom-mutations/#mutating-after-the-render","title":"Mutating after the render","text":"

There are two ways this goes wrong, and they look nothing alike.

"},{"location":"learn/vdom-mutations/#case-1-the-mutation-is-never-sent","title":"Case 1: the mutation is never sent","text":"

Change a rendered element and ask for no re-render, and the browser never learns. This is the same class of bug as mutating state in place, and it shares the cause: ReactPy decides what to send from state changes, not from the shape of your dictionaries.

There is no error, no log line, and no console message. The element in the browser keeps its old text and you keep staring at the assignment that \"obviously\" should have worked.

A mutation you make while handling an event is invisible until something else renders

This is the version that costs an afternoon. In a real browser, against the shared-element example below: click mutate and the page does not change; click bump, which changes unrelated state, and the mutation appears \u2014 as if it had been waiting. Because the re-render walks the model again, it re-reads the dictionary you edited and ships the change with the next patch. A user's action becomes visible on the next action, which is a bug that looks like a timing problem and is not one.

"},{"location":"learn/vdom-mutations/#case-2-the-mutation-escapes-to-other-users","title":"Case 2: the mutation escapes to other users","text":"

The more damaging case, and the one unique to a server-side framework.

A model built at module level is one object shared by every connection to the server. Each connection gets its own Layout and its own hook state, but they all render the same dictionary. One visitor's handler mutates the object that everyone else's next render reads:

app.py Run
# Built once, at module scope. Every visitor's Layout renders this same dict.\nSTATUS = html.p({\"id\": \"status\"}, \"idle\")\n\n\n@component\ndef panel():\n    n, set_n = use_state(0)\n\n    async def mark_dirty(event):\n        # Mutates the shared element and asks for no render: nothing changes\n        # on screen, and the server's model now disagrees with every client.\n        STATUS[\"children\"] = [f\"mutated by visitor {n}\"]\n\n    async def bump(event):\n        # The next render of ANY state in this component re-sends STATUS --\n        # including the mutation, whoever made it.\n        set_n(n + 1)\n\n    return html.div(\n        html.h1({\"id\": \"counter\"}, f\"n={n}\"),\n        html.button({\"id\": \"dirty\", \"onClick\": mark_dirty}, \"mutate\"),\n        html.button({\"id\": \"bump\", \"onClick\": bump}, \"bump\"),\n        STATUS,\n    )\n
# TODO\n

Verified in a real browser with two separate tabs and two separate websockets against one server: tab A clicks mutate, then bump; tab B, which never clicked anything, then clicks bump and sees mutated by visitor 0 in its own page. Neither user can make it go away, because the mutated module-level dict is now the source of truth for every future render.

Module-level elements are shared across all connections

This has no browser-side analogue \u2014 in React, module scope is per-tab. Here module scope is per-process, and the process is shared. The rule follows directly: build elements inside the component. A function that returns html.p(...) costs one dictionary construction and removes the whole class of bug:

app.py Run
# A function that builds a fresh element on every call: the safe shape.\ndef status_banner(text, tone):\n    return html.p(\n        {\"id\": \"status\", \"className\": f\"status-{tone}\"},\n        text,\n    )\n\n\n@component\ndef panel():\n    n, set_n = use_state(0)\n    tone = \"ok\" if n == 0 else \"warn\"\n\n    async def handle_n_click(event):\n        set_n(n + 1)\n\n    return html.div(\n        html.h1(f\"n={n}\"),\n        html.button({\"onClick\": handle_n_click}, \"bump\"),\n        status_banner(f\"{n} items\", tone),  # a new dict every render\n    )\n
# TODO\n

Module-level constants are still fine when they are genuinely inert \u2014 a style dict, a list of option labels, an html.head() passed once to ReactPy(html_head=...) \u2014 because nothing writes to them. The bug needs a write. If nothing in your program assigns into it, module scope is safe.

"},{"location":"learn/vdom-mutations/#the-two-mutation-habits-that-also-defeat-state","title":"The two mutation habits that also defeat state","text":"

These belong to Updating Objects in State and Updating Arrays in State, but they are VDOM mutation seen from the other side, so they are repeated here because the symptom \u2014 a control that stops responding with no error \u2014 is the one people hunt for longest.

use_state's setter compares the new value against the old with == (core/hooks.py, strictly_equal). So:

rows.append(x)\nset_rows(rows)          # same object -> compares equal -> no render, no error\n

And the corollary that surprises people: a new object that merely compares equal is dropped too, so set_rows([*rows]) when rows is unchanged also renders nothing. Replace state with new, non-equal objects; never edit in place.

The same comparison is what makes use_memo fail to recompute after a mutate-then-set \u2014 the dependency compares equal to itself.

"},{"location":"learn/vdom-mutations/#safe-ways-to-shape-a-model","title":"Safe ways to shape a model","text":"

Reach for these in order.

"},{"location":"learn/vdom-mutations/#build-it-do-not-edit-it","title":"Build it, do not edit it","text":"

The default. Compose with functions, keep elements as return values, and pass data in as arguments. Conditional structure is an expression, not a post-construction edit:

def row(item):\n    return html.li(\n        {\"key\": item[\"id\"], \"className\": \"row\" if item[\"done\"] else \"row pending\"},\n        item[\"text\"],\n    )\n
"},{"location":"learn/vdom-mutations/#deepcopy-for-a-genuine-fork","title":"deepcopy for a genuine fork","text":"

When you must derive a variant from a model you do not own \u2014 typically something that came back from string_to_reactpy() or a cached template \u2014 copy it first:

from copy import deepcopy\n\nTEMPLATE = html.div({\"className\": \"card\"}, html.p(\"placeholder\"))\n\n\ndef card(body_text):\n    model = deepcopy(TEMPLATE)\n    model[\"children\"][0][\"children\"] = [body_text]\n    return model\n

deepcopy preserves the VdomDict class and its key guard, and leaves the original intact \u2014 verified. It is the right tool for a template you parse or build once at module level; it is not a licence to mutate a model that has already been rendered.

Deep-copying a model with handlers duplicates the handler objects

deepcopy succeeds on a model that has eventHandlers. It builds a new EventHandler but shares the wrapped function, so both copies still invoke the same Python code. That is fine for a fork you intend to render; it is not a way to hand two elements independent handlers \u2014 build those separately.

"},{"location":"learn/vdom-mutations/#transform-at-parse-time","title":"Transform at parse time","text":"

For HTML that arrives as a string, pass *transforms to string_to_reactpy() and edit nodes before anyone has a reference to them \u2014 see Convert Between VDOM and HTML.

"},{"location":"learn/vdom-mutations/#turn-the-check-on-while-developing","title":"Turn the check on while developing","text":"

REACTPY_CHECK_VDOM_SPEC validates each rendered model against the JSON schema in core/vdom.py \u2014 but only catches structural damage (a child that is not a VDOM dict, a missing tagName), and it is off unless REACTPY_DEBUG is on:

from reactpy import config\n\nconfig.REACTPY_DEBUG.current = True   # parents REACTPY_CHECK_VDOM_SPEC on\n

It will not catch a className you changed to the wrong string, or a text node you rewrote \u2014 those are valid VDOM. Treat it as a schema check, not a mutation detector.

"},{"location":"learn/vdom-mutations/#what-you-may-still-touch-the-dom-briefly","title":"What you may still touch: the DOM, briefly","text":"

Nothing here applies to the browser's DOM, which ReactPy does not share with you. The one sanctioned route is an inline-JavaScript handler, and the same discipline applies there \u2014 do not mutate nodes ReactPy owns, since the next patch against a node you removed breaks the diff. That is covered in Manipulating the DOM with Refs.

"},{"location":"learn/vdom-mutations/#summary","title":"Summary","text":"
  • A model is yours until it is returned from a render; after that the layout holds the same objects.
  • VdomDict guards its six top-level keys \u2014 key belongs inside attributes, and there is no ref.
  • Constructors copy the top level but share nested values, so a child element you built is the object the parent holds.
  • Mutating a rendered element sends nothing; the change surfaces on the next unrelated render, which makes it look like a timing bug.
  • A module-level element is shared by every connection, so one user's mutation appears in everyone's page. Build elements inside components.
  • Mutate-then-set-the-same-object is dropped by use_state's equality check, with no error.
  • Prefer building fresh; use deepcopy for a real fork; use parse-time transforms for parsed HTML.
"},{"location":"learn/vdom-mutations/#related","title":"Related","text":"
  • Updating Objects in State and Updating Arrays in State \u2014 the state-side version of the same hazard
  • Creating HTML Tags \u2014 what the constructors do to your arguments
  • Convert Between VDOM and HTML \u2014 where deepcopy-and-edit is the natural pattern
  • Preserving and Resetting State \u2014 how identity actually drives re-mounting
  • Protocol Structure \u2014 why the server keeps holding on to those dictionaries
"},{"location":"learn/writing-markup-with-psx/","title":"Writing markup with psx","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

See this issue for more details.

"},{"location":"learn/you-might-not-need-an-effect/","title":"You Might Not Need an Effect","text":""},{"location":"learn/you-might-not-need-an-effect/#overview","title":"Overview","text":"

Most Effects are unnecessary. If your logic only adjusts one thing based on another thing you already have, it belongs in the render \u2014 not in an Effect. This page walks through the common cases where an Effect looks right and isn't, with the render counts each one costs.

You will learn

  • How to recognize an Effect that is doing a render's job
  • What to write instead, case by case
  • Why ReactPy cannot use React's \"set state during render\" escape hatch
  • Which Effects genuinely are necessary
"},{"location":"learn/you-might-not-need-an-effect/#how-to-remove-unnecessary-effects","title":"How to remove unnecessary Effects","text":"

Work through each Effect in your code and ask: does this code need to run after a render, or could it run while rendering?

An Effect is genuinely necessary when the component must reach something outside ReactPy \u2014 subscribing to a queue, opening a socket, starting a task. Everything below is the opposite case: the \"external system\" is a value your component already has.

If you're not sure what Effects are for in the first place, start with Synchronizing with Effects.

"},{"location":"learn/you-might-not-need-an-effect/#updating-state-based-on-props-or-state","title":"Updating state based on props or state","text":"

A component shows a name built from two fields. The tempting version stores the combination and keeps it in step with an Effect:

app.py Run
@component\ndef form():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n    # Wrong: this value is not state, it is a calculation of other state.\n    full_name, set_full_name = use_state(\"\")\n\n    def effect():\n        set_full_name(f\"{first_name} {last_name}\")\n\n    use_effect(effect, dependencies=[first_name, last_name])\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.h1(full_name or \"(nothing yet)\"),\n        html.input(\n            {\n                \"value\": first_name,\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": last_name,\n                \"onChange\": handle_last_name_change,\n            }\n        ),\n    )\n
# TODO\n

Remove the state variable and the Effect, and calculate it during render:

app.py Run
@component\ndef form():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n    # Right: calculated during rendering, so it is always in sync.\n    full_name = f\"{first_name} {last_name}\"\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.h1(full_name),\n        html.input(\n            {\n                \"value\": first_name,\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": last_name,\n                \"onChange\": handle_last_name_change,\n            }\n        ),\n    )\n
# TODO\n

The difference is measurable, and the first row of each is the part users actually see:

First paint Renders for two keystrokes Effect + stored state \"(nothing yet)\" 6 Derived during render \"Ada Lovelace\" 3

Verified in a Layout. The Effect version renders once with an empty name, sets state from the Effect, renders again \u2014 every keystroke costs two renders instead of one, and the very first paint is wrong. Nothing downstream can rescue that first frame, because it is a real render that a real client receives.

When something can be calculated from existing props or state, don't put it in state. Calculate it during render. Fewer state variables means less code, no way for two of them to drift apart, and half the renders. If this feels unfamiliar, Thinking in React covers what belongs in state.

How expensive is too expensive?

If you're not sure whether a calculation is costly enough to worry about, write it without caching first, then measure it: time the component body, or count how often it runs. In practice the work that is genuinely slow is usually obvious \u2014 parsing, sorting or filtering thousands of rows, regex over a large string, cryptographic hashes. String concatenation, len(), and a loop over a page's worth of items are not worth a cache, and a cache costs more than it saves when its dependency list is wrong.

"},{"location":"learn/you-might-not-need-an-effect/#caching-expensive-calculations","title":"Caching expensive calculations","text":"

When a calculation genuinely is expensive, the answer is still not state plus an Effect \u2014 it is use_memo, which caches a value without adding a render:

app.py Run
@component\ndef todo_list():\n    filter_name, set_filter_name = use_state(\"all\")\n    new_todo, set_new_todo = use_state(\"\")\n\n    def visible_todos():\n        # A real filter over a real dataset. It records every run so the\n        # effect of use_memo is countable.\n        filter_runs.append(filter_name)\n        if filter_name == \"all\":\n            return todos\n        if filter_name == \"active\":\n            return [todo for todo in todos if not todo[\"completed\"]]\n        return [todo for todo in todos if todo[\"completed\"]]\n\n    shown = use_memo(visible_todos)\n\n    async def handle_filter_name_click(event):\n        set_filter_name(\n            {\"all\": \"active\", \"active\": \"completed\", \"completed\": \"all\"}[filter_name]\n        )\n\n    async def handle_new_todo_change(event):\n        set_new_todo(await event.target.value)\n\n    return html.div(\n        html.p(f\"{len(shown)} of {len(todos)} shown (filter: {filter_name})\"),\n        html.ul([html.li({\"key\": todo[\"id\"]}, todo[\"text\"]) for todo in shown]),\n        html.button(\n            {\"onClick\": handle_filter_name_click},\n            \"Change filter\",\n        ),\n        html.input(\n            {\n                \"placeholder\": \"new todo\",\n                \"value\": new_todo,\n                \"onChange\": handle_new_todo_change,\n            }\n        ),\n    )\n
# TODO\n

The filter records each run so the result is countable. Verified: after two keystrokes the component has rendered three times and the filter has run once; pressing Change filter adds a render and a second run. The value is recomputed exactly when the inputs it reads change, and never on a render that didn't change them.

Two properties worth knowing, both verified:

  • use_memo infers its dependencies the same way use_effect does \u2014 from the names the function reads. Pass dependencies=[] to pin a value forever, or dependencies=None to recompute on every render. See Use Memo.
  • It caches the value, not the render. The component body still runs on every render; ReactPy has no component-level memo, so a child re-runs whenever its parent does. use_memo saves the computation, never the render \u2014 see Render and Commit.

Mutating a value in place and \"setting\" it back defeats use_memo too, for the same reason it defeats use_state: the new value compares equal to the old one. Build a new object instead (Updating Arrays in State).

"},{"location":"learn/you-might-not-need-an-effect/#resetting-all-state-when-a-prop-changes","title":"Resetting all state when a prop changes","text":"

A profile page holds a comment draft. When the user navigates to a different profile, the draft should not follow them:

app.py Run
@component\ndef profile(user_id):\n    # Any state declared here is thrown away whenever the key changes, along\n    # with the state of every component below it. No Effect needed.\n    comment, set_comment = use_state(\"\")\n\n    async def handle_comment_change(event):\n        set_comment(await event.target.value)\n\n    return html.div(\n        html.h2(f\"profile {user_id}\"),\n        html.textarea(\n            {\n                \"value\": comment,\n                \"placeholder\": \"leave a comment\",\n                \"onChange\": handle_comment_change,\n            }\n        ),\n        html.p(f\"draft: {comment or '(empty)'}\"),\n    )\n\n\n@component\ndef profile_page():\n    user_id, set_user_id = use_state(\"ada\")\n\n    async def handle_user_id_click(event):\n        set_user_id(\"grace\" if user_id == \"ada\" else \"ada\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_user_id_click},\n            \"Next profile\",\n        ),\n        # The key is what makes ReactPy treat this as a different profile.\n        profile(user_id, key=user_id),\n    )\n
# TODO\n

Verified: type a draft, press Next profile, and the draft is empty on the new profile \u2014 in a single extra render.

Passing key=user_id tells ReactPy that a different user_id means a different component instance. It tears down profile() and everything below it and builds them again, so all of their state starts fresh. One line, and it covers every piece of state in the whole subtree \u2014 no Effect, and no remembering to add one to each nested component.

The Effect version (set_comment(\"\") in an Effect depending on user_id) is worse in three ways: the subtree renders once with the previous user's draft still in it, every nested component needs its own copy, and a piece of state added later silently fails to reset. See Preserving and Resetting State for the full mechanism.

key is spelled differently for elements and components

Here it is a keyword argument to the component call: profile(user_id, key=user_id). On an element it goes in the attribute dict instead: html.li({\"key\": 1}, ...). Put it in the wrong place and you get TypeError: Vdom.__call__() got an unexpected keyword argument 'key', or a literal key=\"...\" attribute in the page.

"},{"location":"learn/you-might-not-need-an-effect/#adjusting-some-state-when-a-prop-changes","title":"Adjusting some state when a prop changes","text":"

Sometimes you don't want to throw state away \u2014 just invalidate part of it when an input changes. A list keeps a selection, and a new items prop should clear it. React's advice is to call the setter during render, and that escape hatch does not work in ReactPy. Measured, the render-phase-setter version takes four renders and the client receives this sequence:

<root>                     items: [a,b,c]     selected: None\n/children/0/children/1     items: [a,b,c]     selected: a\n<root>                     items: [a,b,c,i4]  selected: a     <-- stale, and delivered\n/children/0/children/1     items: [a,b,c,i4]  selected: None\n

React throws the discarded render away; ReactPy hands the model to the socket as soon as it exists, so the stale frame ships. Use a derived read instead. Store the value alongside what it belonged to, and let the read decide whether it is still valid:

app.py Run
@component\ndef list_box(items):\n    is_reverse, set_is_reverse = use_state(False)\n    # One state value that remembers which list the choice belonged to. The\n    # selection is *derived*: if the items changed, the old choice is not\n    # valid for the new list, so it reads back as None. No Effect, no\n    # intermediate render with a stale selection.\n    chosen, set_chosen = use_state({\"items\": None, \"value\": None})\n    selection = chosen[\"value\"] if chosen[\"items\"] == items else None\n\n    shown = items[::-1] if is_reverse else items\n\n    async def handle_is_reverse_click(event):\n        set_is_reverse(not is_reverse)\n\n    async def handle_chosen_click(event):\n        set_chosen({\"items\": items, \"value\": shown[0]})\n\n    return html.div(\n        html.p(f\"items: {shown}  selected: {selection}\"),\n        html.button({\"onClick\": handle_is_reverse_click}, \"Reverse\"),\n        html.button(\n            {\"onClick\": handle_chosen_click},\n            \"Select\",\n        ),\n    )\n\n\n@component\ndef app():\n    items, set_items = use_state([\"a\", \"b\", \"c\"])\n\n    async def handle_items_click(event):\n        set_items([*items, f\"item {len(items) + 1}\"])\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_items_click},\n            \"Add item\",\n        ),\n        list_box(items),\n    )\n
# TODO\n

Verified: Select costs one render and shows the selection; Add item costs one render and the selection is already gone in that same patch \u2014 three renders total, no stale frame.

The general shape is a guard on the read rather than a call to the setter:

is_valid = chosen[\"items\"] == items\nselection = chosen[\"value\"] if is_valid else None\n

Do not set state from an Effect that depends on it

The reflexive alternative \u2014 an Effect calling set_selection(None) with dependencies=[items] \u2014 is not merely slow. When the value it sets is one it also reads, it is a loop, and ReactPy has no detector for one. Measured: an Effect that set a counter it depended on rendered 86 times before the probe gave up. If a component visibly spins, read what the Effect awaits, what it depends on, and what it sets.

"},{"location":"learn/you-might-not-need-an-effect/#sharing-logic-between-event-handlers","title":"Sharing logic between event handlers","text":"

Two handlers should do the same thing on input. There is no reason for this to involve an Effect:

app.py Run
@component\ndef shipping_form():\n    shipping_city, set_shipping_city = use_state(\"\")\n    billing_city, set_billing_city = use_state(\"\")\n\n    async def handle_shipping_change(event):\n        city = await event.target.value\n        set_shipping_city(city)\n        # Shared logic: just call the other setter. Handlers are ordinary\n        # functions; there is nothing to schedule and nothing to re-sync.\n        set_billing_city(city)\n\n    async def handle_billing_city_change(event):\n        set_billing_city(await event.target.value)\n\n    return html.div(\n        html.p(f\"shipping: {shipping_city or '(empty)'}\"),\n        html.p(f\"billing:  {billing_city or '(empty)'}\"),\n        html.input(\n            {\"value\": shipping_city, \"onChange\": handle_shipping_change},\n        ),\n        html.input(\n            {\n                \"value\": billing_city,\n                \"onChange\": handle_billing_city_change,\n            }\n        ),\n    )\n
# TODO\n

Verified: type in the shipping field and the billing readout changes with it, in one render, because both setters were called from one handler.

Event handlers are ordinary functions \u2014 ordinary async defs, which is to say the coroutine ReactPy requires of anything in a handler slot. Call one from the other (with await, since it is a coroutine), extract the shared part into a plain function, or pass the other setter as a parameter. Whatever React's docs tell you here transfers unchanged, because none of it is rendering work.

"},{"location":"learn/you-might-not-need-an-effect/#sending-a-post-request","title":"Sending a POST request","text":"

Submitting a form is an event, not a consequence of rendering. Do the work in the handler:

app.py Run
@component\ndef signup():\n    email, set_email = use_state(\"\")\n    status, set_status = use_state(\"idle\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        # Wrong: set a \"pending\" flag and let an Effect notice it. That costs\n        # a state variable, a render, and a way to submit the same value twice.\n        #     set_pending(True)\n        #\n        # Right: do the work from the handler. It runs once per submission.\n        set_status(\"sending...\")\n        await send_email(email)\n        set_status(f\"sent to {email}\")\n\n    async def handle_email_change(event):\n        set_email(await event.target.value)\n\n    return html.div(\n        html.p(status),\n        html.form(\n            {\"onSubmit\": handle_submit},\n            html.input(\n                {\n                    \"value\": email,\n                    \"placeholder\": \"you@example.com\",\n                    \"onChange\": handle_email_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Sign up\"),\n        ),\n    )\n
# TODO\n

Verified: fill the field, submit, and the transport recorded exactly one send \u2014 ['ada@example.com'].

The version to avoid is a flag plus an Effect: set_pending(True), and an Effect that watches pending. It costs a state variable, an extra render per submission, and \u2014 because Effects re-run on dependency changes and remounts \u2014 a way to send twice. event.preventDefault() stops the page navigating, and must appear literally in the handler's body, since ReactPy detects it by bytecode inspection (Creating VDOM Event Handlers).

"},{"location":"learn/you-might-not-need-an-effect/#notifying-parent-components-about-state-changes","title":"Notifying parent components about state changes","text":"

A child should tell its parent something happened. Pass a callback and call it from the child's event handler \u2014 this works exactly as it does in React:

app.py Run
@component\ndef search_box(on_search):\n    query, set_query = use_state(\"\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        # Not an Effect: the search happens because the user submitted.\n        on_search(query)\n\n    async def handle_query_change(event):\n        set_query(await event.target.value)\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.input(\n            {\n                \"value\": query,\n                \"placeholder\": \"search\",\n                \"onChange\": handle_query_change,\n            }\n        ),\n        html.button({\"type\": \"submit\"}, \"Search\"),\n    )\n\n\n@component\ndef app():\n    results, set_results = use_state(\"Type something and submit.\")\n\n    def handle_search(query):\n        set_results(f\"searching for {query!r}...\" if query else \"empty query\")\n\n    return html.div(html.p(results), search_box(handle_search))\n
# TODO\n

Verified: type ada, submit, and the parent's paragraph reads searching for 'ada'....

What you must not do is call the parent's function from an Effect that watches a value. Handlers run once per user action; Effects run once per dependency change, which includes renders the user never caused. There is also no use_imperative_handle for the reverse direction \u2014 pass data down as props and callbacks up as arguments (Sharing State Between Components).

"},{"location":"learn/you-might-not-need-an-effect/#chains-of-computations","title":"Chains of computations","text":"

When several values feed each other, keep the whole chain in render. Each intermediate is a local variable, not a state variable:

@component\ndef report(rows, currency):\n    priced = [(*row, row[1] * RATES[currency]) for row in rows]\n    total = sum(item[-1] for item in priced)\n    heading = f\"{currency.upper()}: {total:,.2f}\"\n    return html.div(html.h1(heading))\n

An Effect at any link in that chain means every upstream change costs an extra full render \u2014 and, as above, each of those renders is visible to the client. If one link is genuinely expensive, wrap that one in use_memo rather than putting the whole chain in state.

"},{"location":"learn/you-might-not-need-an-effect/#initializing-the-application","title":"Initializing the application","text":"

Code that should run once per process does not belong in a component at all \u2014 a component's Effect runs once per connection, and a server serves many connections:

# module level, or the ASGI app's startup: runs once\nconfigure_logging()\nPOOL = create_pool()\n

Keep a dependencies=[] Effect for work that is genuinely per-connection, such as a subscription or a session-scoped task. Never guard a process-level side effect with my_component.started = True: the function object is module-scope, so the first visitor flips the flag for everyone (VDOM Mutations).

"},{"location":"learn/you-might-not-need-an-effect/#subscribing-to-an-external-store","title":"Subscribing to an external store","text":"

Here is the case where the Effect is correct. The value does not live in ReactPy at all, so there is nothing to derive it from \u2014 ReactPy has to be told when it changes:

app.py Run
@component\ndef clock_view():\n    # The value is owned by `clock`, not by this component, so it cannot be\n    # derived during render -- ReactPy has to be told when it changes.\n    ticks, set_ticks = use_state(clock.get())\n\n    def effect():\n        # Subscribe, and hand back the unsubscribe. This is what an Effect\n        # is for: the source of truth lives outside ReactPy.\n        def on_change():\n            set_ticks(clock.get())\n\n        return clock.subscribe(on_change)\n\n    use_effect(effect, dependencies=[])\n\n    return html.div(html.h1(f\"{ticks} ticks\"))\n\n\n@component\ndef app():\n    watching, set_watching = use_state(True)\n\n    async def handle_click(event):\n        clock.tick()\n\n    async def handle_watching_click(event):\n        set_watching(not watching)\n\n    return html.div(\n        html.button({\"onClick\": handle_click}, \"Tick\"),\n        html.button(\n            {\"onClick\": handle_watching_click},\n            \"Stop watching\" if watching else \"Watch\",\n        ),\n        *([] if not watching else [clock_view()]),\n    )\n
# TODO\n

Verified: pressing Tick once shows 1 ticks; pressing Stop watching unmounts the view and leaves 0 listeners registered on the store.

Three things make this one legitimate where the others were not:

  1. The source of truth is outside ReactPy. Deriving would mean reading clock.get() during render, which has no way to know when to run again.
  2. The subscription needs a lifetime, and the clean-up is what returns the store to zero listeners.
  3. The state exists to trigger a render \u2014 that is its whole purpose.

This is also where use_async_effect earns its place, when the external system is an async stream rather than a callback. See Synchronizing with Effects for cancellation and the four-way dependency table.

"},{"location":"learn/you-might-not-need-an-effect/#fetching-data-without-the-extra-effects","title":"Fetching data, without the extra Effects","text":"

Fetching genuinely needs an Effect \u2014 the network is external. Everything you would otherwise stack on top of it does not. Store the response, and shape it in render:

app.py Run
@component\ndef report_view(name):\n    # Store the response *with the name it belongs to*, so a render for a new\n    # report cannot display the old one's rows. The empty read is the\n    # \"loading\" state -- derived, not a second state variable.\n    loaded, set_loaded = use_state({\"name\": None, \"data\": {}})\n\n    async def load():\n        data = await catalog.fetch(name)\n        set_loaded({\"name\": name, \"data\": data})\n\n    use_async_effect(load, dependencies=[name])\n\n    raw = loaded[\"data\"] if loaded[\"name\"] == name else {}\n\n    if not raw:\n        return html.p(\"loading...\")\n\n    # Right: the shape the page needs is computed here, from the data that\n    # arrived. There is no second state variable to keep in step, so there is\n    # nothing for an Effect to synchronise.\n    rows = [{\"label\": key.title(), \"value\": value} for key, value in raw.items()]\n    total = sum(row[\"value\"] for row in rows)\n    lines = [\n        html.li({\"key\": row[\"label\"]}, f\"{row['label']}: {row['value']}\")\n        for row in rows\n    ]\n\n    return html.div(\n        html.h2(f\"{name}: {total} total\"),\n        html.ul(lines),\n    )\n\n\n@component\ndef app():\n    name, set_name = use_state(\"orders\")\n\n    async def handle_name_click(event):\n        set_name(\"users\" if name == \"orders\" else \"orders\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_name_click},\n            \"Toggle report\",\n        ),\n        # No `key` here on purpose: the derived read in report_view is what\n        # keeps the old report off the screen, so nothing has to be remounted.\n        report_view(name),\n    )\n
# TODO\n

Verified: the Effect stores the raw response; the labels, the per-row formatting, and the total are computed during render. One request per report name (['orders', 'users']), and no rows or total state variables that could fall out of step with the response.

The example carries one more lesson in its state shape. It stores the response together with the name that produced it and derives the read:

raw = loaded[\"data\"] if loaded[\"name\"] == name else {}\n

Storing the response alone looks fine and is wrong: switch from orders to users and the component re-renders immediately, before the new response arrives, so the client receives users: 55 total above the orders rows \u2014 55 being orders' total. Measured, and the derived read above is what fixes it, without remounting the component.

The anti-pattern is a second Effect that watches the response and sets three more state variables from it. Each of those is a value you can calculate, and each adds a render in which the page is half-updated.

Because ReactPy already runs on the server, there is usually a better option than fetching in the child at all: fetch in a parent during render, or at startup, and pass the result down as a prop. Then the child has no Effect and no loading state, and the response is shared rather than re-requested. Keep the Effect for data that depends on something only the connection knows, like use_location.

"},{"location":"learn/you-might-not-need-an-effect/#how-to-tell-whether-an-effect-is-necessary","title":"How to tell whether an Effect is necessary","text":"The Effect... Verdict reads state or props and sets other state unnecessary \u2014 compute it during render caches a calculation unnecessary \u2014 use_memo resets state when a prop changes unnecessary \u2014 pass a key invalidates state when a prop changes unnecessary \u2014 derive the read from what it belonged to runs logic shared by two handlers unnecessary \u2014 call a function reacts to a user action unnecessary \u2014 put it in the event handler calls a parent's callback when a value changes unnecessary \u2014 call it from the handler subscribes to a queue, socket, or store necessary, with a clean-up starts async work that outlives the render necessary, with use_async_effect starts per-connection work on mount necessary, with dependencies=[]

If you cannot name what the Effect is synchronizing with, it is probably doing a render's job.

Two failure modes that cost hours

  • An Effect that sets state it depends on loops silently. ReactPy has no loop detector. Measured: 86 renders and counting before the probe stopped.
  • Hooks do not work inside an effect body. use_context, use_state, and friends all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 effects run in their own task, with no hook on the stack. Read what you need during render and let the closure carry it in. The traceback appears only in the server log; the page just goes quiet.
"},{"location":"learn/you-might-not-need-an-effect/#recap","title":"Recap","text":"
  • An Effect runs after a render. If the work could happen during the render, it should.
  • Values calculated from props or state are not state. Compute them; do not store and re-sync them. Measured: the Effect version costs twice the renders and ships a wrong first frame.
  • use_memo caches expensive calculations without adding a render \u2014 but it caches values, not component renders.
  • Reset state with a key, not with an Effect.
  • ReactPy cannot use React's render-phase setter trick: the stale patch reaches the client. Derive the value instead.
  • Sharing logic between handlers, notifying a parent, and submitting a form are all handler work.
  • Subscriptions and async streams are the real use for Effects \u2014 with a clean-up, always.
"},{"location":"learn/you-might-not-need-an-effect/#related","title":"Related","text":"
  • Synchronizing with Effects \u2014 how Effects work, and their four-way dependency table
  • Choosing the State Structure \u2014 avoid redundant state in the first place
  • Preserving and Resetting State \u2014 resetting with key
  • Use Memo and Use Callback
  • Separating Events from Effects \u2014 the remaining case where reading fresh state matters
"},{"location":"learn/your-first-component/","title":"Your First Component","text":""},{"location":"learn/your-first-component/#overview","title":"Overview","text":"

Components are one of the core concepts of ReactPy. They are the foundation upon which you build user interfaces (UI), which makes them the perfect place to start your ReactPy journey!

You will learn

  • What a component is
  • What role components play in a ReactPy application
  • How to write your first ReactPy component
"},{"location":"learn/your-first-component/#components-ui-building-blocks","title":"Components: UI building blocks","text":"

On the Web, HTML lets us create rich structured documents with its built-in set of tags like <h1> and <li>:

<article>\n    <h1>My First Component</h1>\n    <ol>\n        <li>Components: UI Building Blocks</li>\n        <li>Defining a Component</li>\n        <li>Using a Component</li>\n    </ol>\n</article>\n

This markup represents this article <article>, its heading <h1>, and an (abbreviated) table of contents as an ordered list <ol>. Markup like this, combined with CSS for style, and Python for interactivity, lies behind every sidebar, avatar, modal, dropdown\u2014every piece of UI you see on the Web.

ReactPy lets you combine your markup, CSS, and Python into custom \"components\", reusable UI elements for your app. The table of contents code you saw above could be turned into a table_of_contents() component you could render on every page. Under the hood, it still uses the same HTML tags like html.article, html.h1, etc.

Just like with HTML tags, you can compose, order and nest components to design whole pages. For example, the documentation page you're reading is made out of ReactPy components:

html.div(\n    html.header(\n        search_bar(),\n        html.a({\"href\": \"/docs\"}, \"Docs\"),\n    ),\n    sidebar(),\n    html.main(\n        table_of_contents(),\n        documentation_text(),\n    ),\n)\n

As your project grows, you will notice that many of your designs can be composed by reusing components you already wrote, speeding up your development. Our table of contents above could be added to any screen with table_of_contents()! You can even jumpstart your project with components shared by the community.

"},{"location":"learn/your-first-component/#defining-a-component","title":"Defining a component","text":"

Traditionally when creating web pages, web developers marked up their content and then added interaction by sprinkling on some JavaScript. This worked great when interaction was a nice-to-have on the web. Now it is expected for many sites and all apps. ReactPy puts interactivity first while still using the same technology: a ReactPy component is a Python function that you can sprinkle with markup. Here's what that looks like:

app.pystyles.css Run
@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3Am.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n
img {\n    height: 200px;\n}\n
# TODO\n

And here's how to build a component:

"},{"location":"learn/your-first-component/#step-1-import-what-you-need","title":"Step 1: Import what you need","text":"

Every component needs the @component decorator and the html tag constructor:

from reactpy import component, html\n

Unlike ReactJS, there is no export statement. To use a component from another file you import it like any other Python object, which you will learn about in Importing and Exporting Components.

"},{"location":"learn/your-first-component/#step-2-define-the-function","title":"Step 2: Define the function","text":"

With def profile(): you define a Python function with the name profile. The @component decorator turns that function into something ReactPy can render.

Pitfall

ReactPy components are ordinary Python functions, so follow Python's naming conventions and write them in snake_case.

The ReactJS rule that \"component names must start with a capital letter\" does not apply. ReactJS needs that rule to tell components apart from HTML tags in its markup syntax. ReactPy has no markup syntax: HTML tags are reached through html. and components are called directly, so the difference is already unambiguous.

"},{"location":"learn/your-first-component/#step-3-add-markup","title":"Step 3: Add markup","text":"

The component returns an html.img element with src and alt attributes. There is no JSX or PSX in ReactPy \u2014 a tag is a normal Python call whose first argument is a dictionary of attributes and whose remaining arguments are its children.

Return statements can be written all on one line when the element is short:

return html.h1(\"Amazing scientists\")\n

For anything longer, wrap the element in parentheses rather than relying on a backslash continuation:

return html.img(\n    {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n)\n
"},{"location":"learn/your-first-component/#using-a-component","title":"Using a component","text":"

Now that you've defined your profile component, you can nest it inside other components. For example, you can define a gallery component that uses multiple profile components:

app.pystyles.css Run
@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n
img {\n    margin: 0 10px 10px 0;\n    height: 90px;\n}\n
# TODO\n
"},{"location":"learn/your-first-component/#what-the-browser-sees","title":"What the browser sees","text":"

Notice the difference in how the two kinds of calls are made:

  • html.section(...) and html.h1(...) are attributes on the html object, so ReactPy knows they are HTML tags.
  • profile() is a function you wrote and decorated with @component, so ReactPy knows it is a component.

And profile contains even more HTML: html.img. In the end, this is what the browser sees:

<section>\n    <h1>Amazing scientists</h1>\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n</section>\n
"},{"location":"learn/your-first-component/#nesting-and-organizing-components","title":"Nesting and organizing components","text":"

Components are regular Python functions, so you can keep multiple components in the same file. This is convenient when components are relatively small or tightly related to each other. If this file gets crowded, you can always move profile to a separate file. You will learn how to do this shortly on the page about imports.

Because the profile components are rendered inside gallery\u2014even several times!\u2014we can say that gallery is a parent component, rendering each profile as a \"child\". This is part of the magic of ReactPy: you can define a component once, and then use it in as many places and as many times as you like.

Pitfall

Components can render other components, but you must never nest their definitions:

from reactpy import component, html\n\n\n# Do not do this: a component defined inside another component.\n@component\ndef gallery():\n    @component\n    def profile():\n        return html.img({\"src\": \"https://i.imgur.com/MK3eW3As.jpg\"})\n\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n

Instead, define every component at the top level of your module:

@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n

Here is why. When ReactPy re-renders gallery, it matches each child against the child from the previous render by the identity of its function. A component defined inside another function is a brand new function object every time the parent runs, so ReactPy concludes the old child is gone: it unmounts the previous profile, throws away its state and Effects, and mounts a fresh one. Rendering still looks correct, which makes this bug easy to miss and hard to trace.

When a child component needs some data from a parent, pass it by props instead of nesting definitions.

Deep Dive

"},{"location":"learn/your-first-component/#components-all-the-way-down","title":"Components all the way down","text":"

Your ReactPy application begins at a \"root\" component. This is the component you hand to an executor \u2014 for example the hello_world component passed to ReactPy in Creating a Standalone React App.

Most ReactPy apps use components all the way down. This means that you won't only use components for reusable pieces like buttons, but also for larger pieces like sidebars, lists, and ultimately, complete pages! Components are a handy way to organize UI code and markup, even if some of them are only used once.

Because your components run on the server, ReactPy can also render them into the initial HTML response instead of waiting for a browser to do the work. See Add React to an Existing Project to render components inside pages you already have.

Still, many websites only use ReactPy to add interactivity to existing HTML pages. They have many root components instead of a single one for the entire page. You can use as much\u2014or as little\u2014ReactPy as you need.

"},{"location":"learn/your-first-component/#recap","title":"Recap","text":"

You've just gotten your first taste of ReactPy! Let's recap some key points.

  • ReactPy lets you create components, reusable UI elements for your app.
  • In a ReactPy app, every piece of UI is a component.
  • ReactPy components are regular Python functions except:

    1. They are decorated with @component.
    2. They return elements built with html.
  • Attributes are a dict, children are the remaining arguments, and both are ordinary Python \u2014 which means for loops, if statements, and variables work exactly as you would expect.

"},{"location":"learn/your-first-component/#next-steps","title":"Next Steps","text":"

Check out Importing and Exporting Components to organize components across files, or jump straight to Passing Props to a Component to make yours configurable.

"},{"location":"reference/client-api/","title":"Client API","text":""},{"location":"reference/client-api/#overview","title":"Overview","text":"

mountReactPy is the browser-side entry point: one exported function, one websocket, no build step. Everything else in the browser is the server's business.

import { mountReactPy } from \"/reactpy/static/index.js\";\n\nmountReactPy({\n  mountElement: document.getElementById(\"app\"),\n  pathPrefix: \"/reactpy/\",\n  componentPath: \"main.counter/\",\n});\n

The executors emit this call for you \u2014 see ReactPy and ReactPyMiddleware. You need to write it yourself only when you serve the document from your own route, or when the defaults do not fit.

"},{"location":"reference/client-api/#reference","title":"Reference","text":""},{"location":"reference/client-api/#mountreactpyoptions","title":"mountReactPy(options)","text":"
import { mountReactPy } from \"{pathPrefix}static/index.js\";\n

The bundle is served by ReactPy itself, under {pathPrefix}static/, alongside two chunks it imports by relative path. There is nothing to install and nothing to build: it is preact and the client, pre-bundled.

"},{"location":"reference/client-api/#parameters","title":"Parameters","text":"

A single object.

  • mountElement: The DOM node to render into. Not optional in effect \u2014 passing null, which is what getElementById returns for a missing id, dies with TypeError: Cannot read properties of null (reading '__k') before anything is logged.

  • pathPrefix: string. Must start and end with /. Concatenated verbatim with componentPath, so a missing slash on either side produces a URL that is wrong rather than invalid \u2014 which is worse, because nothing rejects it. A value with no leading slash (\"reactpy/\") or no pathPrefix at all may fail early with TypeError: Failed to construct 'URL': Invalid URL, but only when the page's host includes a port. See The URL is built by concatenation.

  • componentPath: string, optional. The dotted path of the root component, with a trailing slash: \"main.counter/\". ReactPyMiddleware requires it. ReactPy takes the opposite view and requires it be omitted \u2014 see The executors route differently.

  • reconnectInterval: number of ms. 750.

  • reconnectMaxInterval: number of ms. 60000.

  • reconnectMaxRetries: number. 150.

  • reconnectBackoffMultiplier: number. 1.25.

"},{"location":"reference/client-api/#returns","title":"Returns","text":"

Nothing.

There is no unmountReactPy

The bundle exports exactly one name. Verified by importing it and listing the keys: mountReactPy, nothing else. mountReactPy returns nothing and takes no callback, so you get no handle to keep and no supported way to stop a component.

Removing the mount element from the DOM does not stop it either. The subtree is detached, not unmounted: mountReactPy calls preact's render(...) into the node and never the matching unmount. Verified in a browser \u2014 after removing the node, a click dispatched into the detached subtree still reached the server and incremented its state, and the websocket stayed OPEN in both directions. The component keeps reconciling into an orphan nobody can see.

The only clean teardown is to navigate or reload. If you are building a single-page app that swaps components on the client, the closest available tool is onMessage's return value, which unsubscribes a handler \u2014 that stops the client applying updates, but leaves the socket and the server's layout alive.

Reconnect options reject zero, not just wrong types

The defaults are applied with JavaScript's ||, not ??, so any falsy value is replaced by the default. Passing reconnectMaxRetries: 0 to mean \"do not retry\" gets you the default 150 instead, and reconnectInterval: 0 gets you 750 ms.

To actually stop retrying, pass a negative reconnectMaxRetries. The retry test is retries >= maxRetries and negatives survive ||, so reconnectMaxRetries: -1 connects once and then gives up \u2014 measured at exactly one attempt, by running the client's own createReconnectingWebSocket against a stub socket.

The retry counter is also not what it appears to be. It counts consecutive failures, because a successful open resets it to zero along with the backoff interval. Measured against a stub that accepts each connection and closes it 2 ms later, reconnectMaxRetries: 2 with a 1 ms interval produced 342 attempts in 1.5 s: every reconnect succeeds, so the counter never reaches the limit. Only attempts that fail to open at all accumulate.

Which leads to the sharper edge: a handshake that is refused is never retried at all. If the socket never reaches OPEN, the close handler logs Failed to connect! and returns without scheduling anything. So ReactPyMiddleware answering 403 \u2014 a wrong componentPath, a wrong path_prefix \u2014 gives you one attempt, one line in the console, and a permanently empty element. Nothing recovers without a reload. Retries exist only for connections that opened and then dropped.

"},{"location":"reference/client-api/#what-mountreactpy-does","title":"What mountReactPy does","text":"

mountReactPy does five things (static/index.js):

  1. Picks the websocket scheme from the page: wss: under https:, otherwise ws:.
  2. Builds the URL as scheme // host + pathPrefix + componentPath, then appends path=window.location.pathname and, only if the page has a query string, qs=window.location.search.
  3. Sets jsModulesPath to origin + pathPrefix + \"modules/\", the base from which JavaScript modules named by an element's importSource are imported.
  4. Constructs the client, which wires up a reconnecting websocket.
  5. Renders the layout into mountElement.

Steps 2 and 3 are the only places the browser's own URL is read. Once the socket is open, the server's view of the URL is whatever those two parameters said \u2014 see Use Location.

"},{"location":"reference/client-api/#the-url-is-built-by-concatenation","title":"The URL is built by concatenation","text":"

Nothing inserts a slash for you, so both halves have to arrive correctly formed. The four ways to get it wrong, each verified from a page with the resulting socket URL (against ReactPyMiddleware on a host with a port):

pathPrefix componentPath Socket path Result \"/reactpy/\" \"main.counter/\" /reactpy/main.counter/ Connects. \"/reactpy\" \"main.counter/\" /reactpymain.counter/ 403, then Failed to connect! \"/reactpy/\" \"main.counter\" /reactpy/main.counter 403, then Failed to connect! \"/reactpy/\" omitted /reactpy/ 403, then Failed to connect!

ReactPyMiddleware matches the websocket path against ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$. Rows two through four fail that pattern, so they fall through to your own ASGI app, which answers 403.

Read the browser's own error line, not ReactPy's. The client emits a bare Failed to connect!; the URL that caused it is on the line above, in the browser's message, without the [ReactPy] prefix.

The || defaults and the URL constructor make this host-dependent

Two of these fail earlier than the table suggests, and only sometimes. new URL(...) throws TypeError: Failed to construct 'URL': Invalid URL for a missing or slashless pathPrefix \u2014 but only when the host carries a port, because ws://example.comreactpy/main.counter/ parses happily as host example.comreactpy. Verified with Node's URL, the same implementation Chrome uses:

Host pathPrefix Outcome 127.0.0.1:8842 \"\" TypeError: Invalid URL 127.0.0.1:8842 \"reactpy/\" TypeError: Invalid URL example.com \"\" Connects to host example.commain.counter example.com \"reactpy/\" Connects to host example.comreactpy

So the mistake that fails loudly in development fails silently in production, sending the socket to a hostname that does not exist. Check the host, not just the path.

"},{"location":"reference/client-api/#the-executors-route-differently","title":"The executors route differently","text":"

The last table row is not a universal truth, and this is where the two executors part company. ReactPy \u2014 the standalone app \u2014 overrides the matcher:

def match_dispatch_path(self, scope):\n    \"\"\"Method override to remove `dotted_path` from the dispatcher URL.\"\"\"\n    return str(scope[\"path\"]) == self.dispatcher_path\n

So the two want opposite things:

componentPath Unmatched dotted path ReactPyMiddleware required, with trailing slash Falls through to your app (with Starlette's websocket handler, a 403 handshake rejection) ReactPy must be omitted \u2014 the bare {pathPrefix} is the route socket error; the server logs NotImplementedError (see below)

Verified against both executors on the same build: the middleware accepts /reactpy/main.counter/ and refuses /reactpy/; the standalone app does exactly the reverse. A mount snippet copied from one executor to the other therefore produces a socket that never opens.

The standalone rejection does not read like a normal exception. executors/asgi/standalone.py, ReactPyApp.__call__ builds msg as a tuple of a %-format template and its arguments and raises NotImplementedError(msg) without ever formatting it, so the server logs it verbatim \u2014 the placeholders survive and the path is appended:

NotImplementedError: (\"ReactPy app received unsupported request of type '%s' at path '%s'\", 'websocket', '/reactpy//')\n

Do not search the log for ...type 'websocket'; search for unsupported request of type. The same tuple is written by _logger.warning immediately before the raise.

"},{"location":"reference/client-api/#the-socket-does-not-open-immediately","title":"The socket does not open immediately","text":"

Constructing the client does not connect. The websocket waits on a ready promise, and that promise is resolved by the first onMessage registration \u2014 nothing else in the codebase resolves it. The layout registers its handler from an effect, so the client connects only after that effect runs, which is why [ReactPy] Starting client... always precedes Connected! and never follows it.

Because onMessage is the sole resolver, a run where the layout's effect never fires produces a fourth console state: no Starting client..., no socket attempt, and no error. Treat that as \"the bundle ran but the component never mounted\" rather than a network problem \u2014 the other four messages below are all post-connect.

Two consequences of the gating:

  • Reconnection refreshes path and qs from the live page on every attempt, but never re-reads componentPath \u2014 that was baked into the URL at mount time.
  • Messages sent before the socket reports OPEN are buffered rather than dropped, and flushed on the next open. They survive a reconnect \u2014 but once retries are exhausted the buffer is never drained.

Note that path and qs are query parameters, not path segments. The full form is ws://127.0.0.1:8000/reactpy/main.counter/?path=%2F&qs=%3Ftab%3D1, and the server reads them with strict_parsing=True. When building a test client by hand, copy a real frame from the browser rather than assembling one from the tables on this page.

"},{"location":"reference/client-api/#the-import-map","title":"The import map","text":"

For a server-rendered component, no import map is needed. The bundle imports its own two chunks by relative path and has preact inlined, so nothing it loads statically is a bare specifier. Verified: a page with no map at all mounted, connected, and the server never received a single request for preact.js.

The map becomes load-bearing only when the model contains importSource and the resolved module does not export its own bind. Rendering such an element goes through loadImportSource, which uses the module's bind when it is a function and only otherwise calls infer_bind_from_environment() \u2014 the function that runs await import(\"react\") and await import(\"react-dom/client\"). Those are dynamic imports, which are exactly what an import map resolves. If they succeed, real React renders the subtree; if they fail, the client falls back to its own preact bindings and says so:

ReactPy will render JavaScript components using internal bindings for 'react'.\n

Note the level and the prefix: that line is console.debug with no [ReactPy] prefix, so it is invisible in a console filtered to warnings, and it is not caught by filtering on [ReactPy].

The executors' bootstrap injects a map only if none is present \u2014 and the check is keyed on one specific id:

if (!document.querySelector(\"#reactpy-importmap\")) { /* inject ReactPy's map */ }\n

The id is the trigger, not the content. Verified across three pages, all served through the same executor bootstrap:

Page ReactPy injects What you see No map Yes, as <script type=\"importmap\" id=\"reactpy-importmap\"> ReactPy did not detect a suitable JavaScript import map. Falling back to ReactPy's internal framework (Preact). Your own map, any other id Yes \u2014 a second map appears, alongside yours The same did not detect line, plus the browser's own An import map rule for specifier 'react' was removed, as it conflicted with an existing rule. Your own map with id=\"reactpy-importmap\" No Nothing at all.

The two messages have different authors, which matters when you search a minified bundle. The did not detect line is written by the Python side of the bootstrap \u2014 executors/utils.py, server_side_component_html emits it inside the inline <script> it generates, so it is in the served HTML, not in the bundle. The internal bindings for 'react' line comes from the client \u2014 infer_bind_from_environment in bind.tsx, reached from loadImportSource in vdom.tsx. Both are unprefixed console.debug, so neither shows up under a [ReactPy] filter.

To control resolution yourself, use that exact id. Two maps is worse than untidy: the browser keeps the first rule it sees for a specifier and discards the later one, so with two maps the winner is decided by document order \u2014 which is why the warning above names a rule being removed.

The map ReactPy writes maps all four React specifiers onto its own preact files under {pathPrefix}static/. Save the page and the app under those exact names \u2014 the dotted path in componentPath is a Python module path, and main.counter only resolves when this file is called main.py:

page.htmlmain.py Run
<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Counter</title>\n    <script type=\"importmap\" id=\"reactpy-importmap\">\n      {\n        \"imports\": {\n          \"react\": \"/reactpy/static/preact.js\",\n          \"react-dom\": \"/reactpy/static/preact-dom.js\",\n          \"react-dom/client\": \"/reactpy/static/preact-dom.js\",\n          \"react/jsx-runtime\": \"/reactpy/static/preact-jsx-runtime.js\"\n        }\n      }\n    </script>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" crossorigin=\"anonymous\">\n      import { mountReactPy } from \"/reactpy/static/index.js\";\n\n      mountReactPy({\n        mountElement: document.getElementById(\"app\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"main.counter/\",\n      });\n    </script>\n  </body>\n</html>\n
from pathlib import Path\n\nfrom starlette.applications import Starlette\nfrom starlette.responses import FileResponse\nfrom starlette.routing import Route\n\nfrom reactpy import component, html, use_state\nfrom reactpy.executors.asgi import ReactPyMiddleware\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def increment(_event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"count is {count}\"),\n        html.button({\"onClick\": increment}, \"Add\"),\n    )\n\n\nasync def home(request):\n    return FileResponse(Path(__file__).parent / \"page.html\")\n\n\nmy_app = ReactPyMiddleware(\n    Starlette(routes=[Route(\"/\", home)]),\n    root_components=[\"main.counter\"],\n)\n
# TODO\n

Serve it with uvicorn main:my_app. Verified in a browser: the counter rendered and logged [ReactPy] Connected!, and no request for preact.js reached the server \u2014 confirming the client never consulted the map.

"},{"location":"reference/client-api/#the-client-object","title":"The client object","text":"

mountReactPy builds on two classes from the @reactpy/client source package. Neither is reachable from a page \u2014 the shipped bundle exports mountReactPy and nothing else \u2014 but you meet them reading the bundle, and they are what a custom binding's sendMessage and onMessage come from.

BaseReactPyClient is the transport-independent half:

Member Purpose onMessage(type, handler) Register a handler for a message type. Returns an unsubscribe function. handleIncoming(message) Dispatch to the handlers for message.type \u2014 except live event reads, which it answers itself. eventProxy The EventProxyRegistry created with the client, which holds the live objects behind event handles. ready Promise, resolved by the first onMessage call. Gates the connection.

One type never reaches a registered handler. handleIncoming routes \"event-proxy-request\" straight to the client's own eventProxy registry (client.ts), which is why live event reads work even in a binding that registered no handler for them \u2014 and why Received message without handler is never logged for one. Build a client that does not extend BaseReactPyClient and you must answer those requests yourself, or every await on an event ends in EventProxyTimeout \u2014 see Protocol Structure.

Messages with no type, or with one nobody registered for, are logged and dropped \u2014 the connection is never closed over a bad message:

Received message without type {\u2026}\nReceived message without handler {\u2026}\n

ReactPyClient adds the websocket, plus sendMessage(message) and loadModule(name). They are not peers. sendMessage is handed to a JavaScript binding as its way to talk back; loadModule is called for the binding, by ReactPy, to pull in a web module named by an importSource whose sourceType is \"NAME\" \u2014 it resolves against jsModulesPath, which is what the server's web modules directory is mounted under.

"},{"location":"reference/client-api/#the-console","title":"The console","text":"

Every message ReactPy writes is prefixed [ReactPy], so filtering the console by that string shows the whole story of a mount. The set is finite:

Message Meaning Starting client... The layout registered its handler; a connection attempt is about to start. Connected! The handshake completed. Resets the retry counter and the backoff interval. Failed to connect! The socket closed without ever having opened. Usually a routing miss \u2014 see The URL is built by concatenation. Disconnected! The socket closed after at least one successful open. Reconnecting in 0.7500 seconds... A retry is scheduled. The number is the current backoff interval. Connection max retries exhausted! Giving up. The page needs a reload; nothing recovers after this. did not detect a suitable JavaScript import map... <script type=\"importmap\" id=\"reactpy-importmap\"> was absent. Informational \u2014 see The import map.

Failed to connect! and Disconnected! are distinguished by whether the socket ever opened, which makes them useful as a diagnosis: the first means the server never routed the request, the second means it did and then the link dropped. Both are logged at console.info level, so a console set to warnings-only shows nothing at all \u2014 check the level before concluding the client is silent.

"},{"location":"reference/client-api/#a-hand-written-mount","title":"A hand-written mount","text":"

Nothing is required beyond an element and the call. Both files below were run to produce the [ReactPy] Connected! and the incremented counter quoted on this page.

main.py Run
from starlette.applications import Starlette\nfrom starlette.responses import HTMLResponse\nfrom starlette.routing import Route\n\nfrom reactpy import component, html, use_state\nfrom reactpy.executors.asgi import ReactPyMiddleware\n\nPATH_PREFIX = \"/reactpy/\"\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def increment(_event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"count is {count}\"),\n        html.button({\"onClick\": increment}, \"Add\"),\n    )\n\n\nasync def home(request):\n    # pathPrefix and the bundle's URL both have to agree with the server's,\n    # so both are interpolated from one value rather than typed twice.\n    return HTMLResponse(f\"\"\"<!doctype html>\n        <html>\n          <body>\n            <div id=\"app\"></div>\n            <script type=\"module\" crossorigin=\"anonymous\">\n              import {{ mountReactPy }} from \"{PATH_PREFIX}static/index.js\";\n\n              mountReactPy({{\n                mountElement: document.getElementById(\"app\"),\n                pathPrefix: \"{PATH_PREFIX}\",\n                componentPath: \"main.counter/\",\n              }});\n            </script>\n          </body>\n        </html>\"\"\")\n\n\nmy_app = ReactPyMiddleware(\n    Starlette(routes=[Route(\"/\", home)]),\n    root_components=[\"main.counter\"],\n)\n
# TODO\n

The component is registered with root_components, and the componentPath in the page \u2014 \"main.counter/\" \u2014 is that dotted path with a slash appended.

The 404 in the console is usually not yours

A page like this logs one Failed to load resource: 404 on every load. It is /favicon.ico, which no browser should be told about by a component framework. Verified: it appears with everything else working. Ignore it; a second 404 naming a /reactpy/... path means the bundle could not be fetched, which does mean the mount will not happen.

"},{"location":"reference/client-api/#failure-modes","title":"Failure modes","text":"Symptom Cause TypeError: Cannot read properties of null (reading '__k') mountElement is None. The id does not exist, or the script ran before the element was parsed. TypeError: Failed to construct 'URL': Invalid URL pathPrefix missing, or missing a leading or trailing slash. No request is ever made. mountReactPy() throws Cannot read properties of undefined No options object at all. Failed to connect!, nothing rendered The socket URL did not match the server's route. Read the URL in the browser's own error line. [ReactPy] Connected!, empty element, nothing in the browser console The component path is unknown to the server. The failure is server-side: RuntimeError: Attempting to use an unregistered root component <path>. Check the server log, not the browser. Renders once, never updates Handlers are not firing \u2014 usually a snake_case event name such as on_click. See Responding to Events. Component vanishes when you remove it from the DOM, socket still open Expected. There is no unmount that closes the socket. Two import maps in the document, React warnings about a removed rule Your map does not use id=\"reactpy-importmap\"."},{"location":"reference/client-api/#related","title":"Related","text":"
  • Protocol Structure \u2014 what travels over the socket this function opens.
  • ReactPy and ReactPyMiddleware \u2014 the executors that generate this call.
  • Jinja \u2014 emitting mounts from templates, including {% pyscript_component %} for running Python in the page.
  • Use Location \u2014 reading back the path and qs the client sends.
"},{"location":"reference/common-props/","title":"Common Props","text":""},{"location":"reference/common-props/#overview","title":"Overview","text":"

Every element a ReactPy component renders is a plain dict with a fixed set of keys, and the attributes you pass to a tag end up on the DOM node nearly untouched. This page is the reference for what you may put in there \u2014 and for the handful of places ReactPy behaves differently from React.

{\n    \"tagName\": \"div\",\n    \"children\": [...],\n    \"attributes\": {...},\n    \"eventHandlers\": {...},\n    \"inlineJavaScript\": {...},\n    \"importSource\": {...},\n}\n

Only \"tagName\" is required. You almost never write this by hand \u2014 reactpy.html builds it for you \u2014 but everything below is what a tag call can carry.

"},{"location":"reference/common-props/#attributes","title":"Attributes","text":"

An attribute is any key/value pair in the first argument to a tag:

html.input({\"type\": \"search\", \"placeholder\": \"Filter\", \"autoFocus\": True})\n

ReactPy forwards the name and the value to the browser verbatim. There is no translation layer, which is the main difference from React, where JSX rewrites props before they reach the DOM.

The value must be JSON-serializable, because the model is sent to the browser as JSON. Nothing stops you from putting something else in there: with debug mode off, the value is accepted at the tag call and only fails when the layout tries to send the model, deep inside the connection:

TypeError: Type is not JSON serializable: object\n

Run with REACTPY_DEBUG=1 and the same mistake fails at the tag call, where you can see which element caused it:

TypeError: Object of type object is not JSON serializable\n

Write the name the browser expects

Attribute names are not validated against any allow-list. If the browser understands an attribute, ReactPy delivers it \u2014 including data-*, aria-*, and attributes ReactPy has never heard of:

html.div({\"aria-live\": \"polite\", \"data-row-id\": 7, \"spellcheck\": False})\n
"},{"location":"reference/common-props/#class-and-classname","title":"class and className","text":"

Both work, and both produce class=\"...\" in the DOM:

html.div({\"class\": \"one two\"}, \"...\")\nhtml.div({\"className\": \"one two\"}, \"...\")\n

There is no JSX in Python, so nothing forces you toward className. Pick one and stay consistent \u2014 class reads better beside the HTML it mirrors, className is familiar to anyone arriving from React.

"},{"location":"reference/common-props/#for-and-htmlfor","title":"for and htmlFor","text":"

Both work on <label>, and both produce for=\"...\". Prefer for; htmlFor exists only for readers who expect it.

"},{"location":"reference/common-props/#style","title":"style","text":"

Pass a dict. Keys may be camelCase or kebab-case \u2014 both are accepted, and both reach the browser in a form it understands:

html.div(\n    {\n        \"style\": {\n            \"backgroundColor\": \"rgb(255, 0, 0)\",  # camelCase\n            \"font-size\": \"20px\",  # kebab-case\n        }\n    }\n)\n

Always write the unit as part of the string

Do not rely on a bare number being given a unit. React appends px to numeric style values; ReactPy's executors do not agree with each other on this. Measured in Chrome, the same component rendered through each executor:

style value ReactPy / ReactPyMiddleware ReactPyCsr {\"fontSize\": \"30px\"} font-size: 30px font-size: 30px {\"font-size\": \"30px\"} font-size: 30px font-size: 30px {\"fontSize\": 30} font-size: 30px nothing at all {\"fontSize\": \"30\"} nothing nothing {\"zIndex\": 5} z-index: 5 z-index: 5 {\"opacity\": 0.5} opacity: 0.5 opacity: 0.5

A number that needs a unit is dropped in silence \u2014 no warning, no log line, and the layout model shows the value untouched, so it is nowhere to be found by reading the server side. Unitless properties like z-index and opacity are the exception: a number is already a valid value there.

The portable rule: put units in the string, everywhere.

{\"style\": {\"font-size\": 20}}  # works by accident in some executors\n{\"style\": {\"font-size\": \"20px\"}}  # works everywhere\n
"},{"location":"reference/common-props/#all-together","title":"All together","text":"

A table of rows is where most of these rules meet at once \u2014 key in attributes, data-* and aria-* passed straight through, and units inside style strings:

app.py Run
ROWS = [\n    {\"id\": 1, \"name\": \"Dana\", \"role\": \"admin\", \"active\": True},\n    {\"id\": 2, \"name\": \"Sam\", \"role\": \"viewer\", \"active\": False},\n]\n\n\n@component\ndef row(person):\n    # `key` lives in attributes, `data-*` and `aria-*` are forwarded verbatim,\n    # and `class` works exactly like `className`. Style values carry their own\n    # units, because a bare number is not portable across executors.\n    return html.tr(\n        {\n            \"key\": person[\"id\"],\n            \"class\": \"is-active\" if person[\"active\"] else \"is-inactive\",\n            \"data-role\": person[\"role\"],\n            \"aria-label\": f\"{person['name']}, {person['role']}\",\n            \"style\": {\"opacity\": \"1\" if person[\"active\"] else \"0.55\"},\n        },\n        html.td(person[\"name\"]),\n        html.td(person[\"role\"]),\n        html.td(\n            {\"style\": {\"text-align\": \"right\", \"font-variant-numeric\": \"tabular-nums\"}},\n            \"yes\" if person[\"active\"] else \"no\",\n        ),\n    )\n\n\n@component\ndef people_table():\n    return html.table(\n        None,\n        html.thead(\n            html.tr(\n                None,\n                html.th(\"Name\"),\n                html.th(\"Role\"),\n                html.th({\"scope\": \"col\"}, \"Active\"),\n            )\n        ),\n        # A list of children: every row needs a key or state will follow the\n        # row's position rather than the person.\n        html.tbody(None, [row(person) for person in ROWS]),\n    )\n
# TODO\n

Rendering this through Layout gives rows whose attributes are exactly what was written:

{\n    \"tagName\": \"tr\",\n    \"attributes\": {\n        \"key\": 1,\n        \"class\": \"is-active\",\n        \"data-role\": \"admin\",\n        \"aria-label\": \"Dana, admin\",\n        \"style\": {\"opacity\": \"1\"},\n    },\n    \"children\": [...],\n}\n

Nothing was renamed, and nothing was dropped. In the DOM the row arrives as:

<tr data-role=\"admin\" aria-label=\"Dana, admin\" class=\"is-active\" style=\"opacity: 1;\">\n

The one attribute that does not reach the DOM is key itself: ReactPy and ReactPyMiddleware consume it for reconciliation and never set it on the element. ReactPyCsr does leak it, so a key=\"1\" attribute shows up in the page \u2014 harmless, but do not rely on the attribute being absent or present. Every other name you write arrives as written.

"},{"location":"reference/common-props/#booleans-and-numbers","title":"Booleans and numbers","text":"

Values are serialized as JSON and coerced by the browser:

You write The browser receives {\"hidden\": True} hidden=\"\" \u2014 present, therefore true {\"hidden\": False} no attribute at all {\"readonly\": True} readonly=\"true\" {\"data-x\": \"1\"} data-x=\"1\", readable as element.dataset.x {\"tabindex\": 2} tabindex=\"2\"

React's attribute quirks do not carry over. There is no htmlFor requirement, no auto-px, and no special-casing of value versus defaultValue \u2014 what you write is what arrives.

"},{"location":"reference/common-props/#dangerouslysetinnerhtml","title":"dangerouslySetInnerHTML","text":"

Injects a raw HTML string, exactly as in React:

html.div({\"dangerouslySetInnerHTML\": {\"__html\": \"<b>bold</b>\"}})\n

The key is the plain string \"dangerouslySetInnerHTML\" \u2014 not a class to import \u2014 and its only field is \"__html\".

This is the XSS hole

ReactPy does nothing to sanitize the string. Never put user input in it.

A dangerouslySetInnerHTML attribute also suppresses children. Nothing enforces the combination \u2014 no error, no warning, at the tag call or at render time \u2014 so the children you passed are simply never displayed. Verified in the browser: for html.div({\"dangerouslySetInnerHTML\": {\"__html\": \"<b>RAW</b>\"}}, \"CHILDTEXT\") the server sends both the attribute and \"children\": [\"CHILDTEXT\"], and the rendered element contains only <b>RAW</b>. Pass the children or the raw HTML, never both, because the silent one is your children.

"},{"location":"reference/common-props/#children","title":"Children","text":"

Children are the arguments that follow the attribute dict. ReactPy accepts a wide range of types and normalizes each one:

Child Becomes str a text node, verbatim int / float its str(), so 7 renders as \"7\" None dropped \u2014 renders nothing True / False the literal text True / False list / tuple flattened into the children another dict an element a @component instance that component's rendered output

Nested lists are flattened, so [items, \"more\"] is the same as one flat list.

Only None renders nothing

False renders the word False. React-style short-circuits therefore misfire:

html.div(None, items or False)  # renders the word \"False\" when items is empty\nhtml.div(None, banner if is_visible else None)  # renders nothing\n

Replace every x and <element> habit with a conditional expression ending in else None.

"},{"location":"reference/common-props/#void-elements","title":"Void elements","text":"

html.br, html.img, html.input, html.hr, html.meta and the rest of the void-element set reject children:

html.br(\"oops\")\n
TypeError: 'br' nodes cannot have children.\n

Nothing special is needed for self-closing tags \u2014 a tag with no children is simply:

html.img({\"src\": \"/logo.png\", \"alt\": \"Logo\"})\n
"},{"location":"reference/common-props/#fragments","title":"Fragments","text":"

html.fragment groups children without adding an element to the DOM. It renders as an element with an empty tagName, which the client renders as its children:

html.fragment(\n    html.h1(\"Title\"),\n    html.p(\"Body\"),\n)\n

A fragment accepts key and nothing else:

html.fragment({\"id\": \"x\"}, html.p(\"...\"))\n
TypeError: Fragments cannot have attributes besides 'key'\n

html(...) \u2014 calling html directly \u2014 is the same fragment constructor.

"},{"location":"reference/common-props/#keys","title":"Keys","text":"

ReactPy matches children between renders to decide what to update versus what to rebuild. It reads the key out of attributes[\"key\"]:

html.ul(\n    None,\n    [html.li({\"key\": person[\"id\"]}, person[\"name\"]) for person in people],\n)\n

key is an attribute, not a sibling of tagName

React treats key as a top-level prop. ReactPy reads it from attributes, so this has no effect \u2014 the key is silently ignored:

{\"tagName\": \"li\", \"key\": \"row-1\", \"children\": [\"...\"]}\n

Write {\"tagName\": \"li\", \"attributes\": {\"key\": \"row-1\"}, \"children\": [\"...\"]}, or simply html.li({\"key\": \"row-1\"}, \"...\"). Passing key= to VdomDict directly raises ValueError: Invalid keys: {'key'}.

Without a key, a child's position in the list becomes its key. That is why reordering a list makes state stick to the wrong row: nothing moved as far as ReactPy is concerned. Keys must be unique among siblings.

When debug mode is on, ReactPy warns for each unkeyed child that came out of a list:

UserWarning: Key not specified for child in list {'children': ..., 'tagName': 'li'}\n

Run your app with REACTPY_DEBUG=1 to see them \u2014 see Rendering Lists for the same warning in context. The check only fires for children inside a list; the direct children of a tag are never warned about, and a generator cannot be inspected at all:

UserWarning: Did not verify key-path integrity of children in generator <generator object ...> - pass a sequence (i.e. list of finite length) in order to verify\n

Pass a list \u2014 a comprehension is usually the smallest change.

"},{"location":"reference/common-props/#event-handlers","title":"Event handlers","text":"

Any callable value under any attribute name is treated as an event handler \u2014 and the coroutine check runs at that same moment, so a callable which is not one raises there rather than being quietly routed to the attributes:

async def save(event):\n    await store.save(await event.target.value)\n\n\nhtml.button({\"onClick\": save}, \"Save\")\n

The name is forwarded as the DOM event to listen for, so the names that work are the DOM event names in camelCase \u2014 onClick, onInput, onKeyDown. The handler receives one argument: a reactpy.types.Event, a live proxy onto the browser's event object rather than a serialized copy of it. await event.target.value asks the page for the property at that moment; the proxy has no __getitem__, so event[\"target\"][\"value\"] raises TypeError: 'Event' object is not subscriptable. There is no \"data\" key to unwrap \u2014 the browser sends a list of arguments, and ReactPy hands you its single element.

The coroutine requirement is not a style rule and async is not optional. The event lives in the browser, so every read is a round trip which must be awaited; a handler that cannot be awaited cannot make one. core/events.py, check_is_async_handler therefore rejects a def and a lambda outright, at element-build time, with a TypeError naming the function. What is accepted: a coroutine function, a callable object whose __call__ is a coroutine, and a functools.partial or functools.wraps wrapper around either. See Responding to Events.

A handler wrapped in EventHandler receives the raw list

A plain coroutine is called with the browser's argument list unpacked, so async def f(event) gets the event. An EventHandler does the opposite: it is called with the list itself \u2014 an EventArguments, a list subclass \u2014 so async def f(data) must index into it. The difference stays invisible until a handler is converted, and the two directions fail differently \u2014 AttributeError: 'EventArguments' object has no attribute 'target' one way, TypeError: 'Event' object is not subscriptable the other:

from reactpy.core.events import EventHandler\n\nasync def save(event):        # plain coroutine: receives the event\n    await store.save(await event.target.value)\n\n\nasync def save_all(data):     # EventHandler: receives the list of arguments\n    await store.save(await data[0].target.value)\n\n\nhtml.button({\"onClick\": save}, \"Save\")\nhtml.button({\"onClick\": EventHandler(save_all, prevent_default=True)}, \"Save\")\n
"},{"location":"reference/common-props/#reactpys-own-options","title":"ReactPy's own options","text":"

Five behaviours run in the browser before the event is sent to the server. Configure them by wrapping the function, not by an attribute:

from reactpy.core.events import EventHandler\n\nhtml.form(\n    {\"onSubmit\": EventHandler(submit, prevent_default=True)},\n    html.button({\"type\": \"submit\"}, \"Submit\"),\n)\n
Parameter Value Effect prevent_default bool calls preventDefault() in the browser before sending stop_propagation bool calls stopPropagation() in the browser before sending stop_immediate_propagation bool also calls stopImmediatePropagation(), blocking sibling listeners on the same element debounce int ms coalesces outgoing events on a trailing edge, so the server sees one after the user pauses \u2014 only on user-input elements throttle int ms rate-limits outgoing events on a leading edge, on any element

A non-negative integer is required for debounce and throttle; anything else is ignored with a browser-console warning rather than an error:

Ignoring invalid debounce value \"fast\" on event handler \"onInput\": expected a non-negative integer.\n

A dict is not an event handler

The React-style {\"function\": ..., \"preventDefault\": True} shape does not work here. A dict is not callable, so ReactPy treats it as an ordinary attribute and no listener is ever registered. With debug mode on you get an immediate error instead of silence:

TypeError: Object of type function is not JSON serializable\n
# wrong: no listener is attached\nhtml.button({\"onClick\": {\"function\": save, \"preventDefault\": True}}, \"...\")\n\n# right\nhtml.button({\"onClick\": EventHandler(save, prevent_default=True)}, \"...\")\n
"},{"location":"reference/common-props/#inline-javascript","title":"Inline JavaScript","text":"

A str under an event handler attribute is treated as JavaScript to run in the page instead of being sent to Python:

html.button({\"onClick\": \"console.log(event)\"}, \"Log the event\")\n

ReactPy records it under the element's inlineJavaScript rather than eventHandlers:

{\n    \"tagName\": \"button\",\n    \"children\": [\"Log the event\"],\n    \"inlineJavaScript\": {\"onClick\": \"console.log(event)\"},\n}\n

The string is eval'd in the page; if it evaluates to a function, that function is called with the event.

Trusted strings only

This runs with your application's privileges. Never build the string from user input \u2014 use a Python handler and pass data through state.

<script> elements reject handlers

html.script raises ValueError: 'script' elements do not support event handlers. It also infers a key from its content or src, so the same script is not re-run on every render.

"},{"location":"reference/common-props/#importsource","title":"importSource","text":"

importSource marks an element as coming from a JavaScript module rather than a built-in tag. ReactPy's web module support produces it for you; you rarely set it by hand. Its keys are source, fallback, sourceType and unmountBeforeUpdate, and sourceType must be \"URL\" or \"NAME\":

JsonSchemaValueException: data.children[0].importSource.sourceType must be one of ['URL', 'NAME']\n
"},{"location":"reference/common-props/#invalid-dictionaries","title":"Invalid dictionaries","text":"

ReactPy validates the six key names as it builds an element:

from reactpy.types import VdomDict\n\nVdomDict(tagName=\"div\", bogus=1)\n
ValueError: Invalid keys: {'bogus'}.\n
element = VdomDict(tagName=\"div\")\nelement[\"bogus\"] = 1\n
KeyError: 'Invalid key: bogus'\n

A dict used as a child with no \"tagName\" does not raise on the server. The layout catches it, logs the traceback, and sends an element whose error field carries the message \u2014 which the client prints in the page, in a <pre> that replaces the component, not in the console:

html.div(None, {\"children\": [\"x\"]})\n
ValueError: Expected a VDOM element dict, not {'children': ['x']}\n

That text exists only under REACTPY_DEBUG=1. Verified in a browser with a bad child between two headings: with debug on, the page read above / ValueError: Expected a VDOM element dict\u2026 / below; with debug off the bad component simply vanished \u2014 above / below, no <pre>, and nothing at all in the console. The server log has the traceback either way. So when an element renders as nothing, check the server log first and re-run with REACTPY_DEBUG=1 before trusting the page. See Protocol Structure.

"},{"location":"reference/common-props/#at-a-glance","title":"At a glance","text":""},{"location":"reference/common-props/#vdomdict-keys","title":"VdomDict keys","text":"Key Type Notes tagName str required; \"\" means a fragment children list any accepted child type, flattened attributes dict[str, Any] must be JSON-serializable eventHandlers dict[str, EventHandler] written for you from callables inlineJavaScript dict[str, str] written for you from str handlers importSource dict written for you by web modules

Any other key raises, as shown above. VdomDict is a dict subclass, so you can read and update it with normal dict access \u2014 but not add a seventh key.

"},{"location":"reference/common-props/#attribute-names-worth-knowing","title":"Attribute names worth knowing","text":"Attribute Value class / className str of space-separated class names for / htmlFor str, the id of the labelled control style dict of CSS property to value; include units in strings dangerouslySetInnerHTML {\"__html\": str} key str or int, unique among siblings anything else forwarded to the browser verbatim"},{"location":"reference/common-props/#related","title":"Related","text":"
  • Usage \u2014 the tag API itself: naming rules, custom tags, SVG
  • Responding to Events \u2014 the event proxy, and why handlers must be coroutines
  • Protocol Structure \u2014 what an event costs on the wire
  • Rendering Lists \u2014 keys and why rows keep the wrong state
  • Use Ref \u2014 ReactPy's Ref, which is never attached to an element
"},{"location":"reference/components-and-hooks-must-be-pure/","title":"Components and Hooks must be pure","text":""},{"location":"reference/components-and-hooks-must-be-pure/#overview","title":"Overview","text":"

A component is a formula: given the same arguments, it must return the same VDOM, and it must change nothing outside itself. This page states that contract precisely \u2014 for components and for hooks \u2014 and names the places ReactPy expects side effects instead.

The short version:

  • A component may read its arguments, call hooks, and return VDOM. It must not mutate anything that existed before it was called, and its output must not depend on anything it did not receive.
  • A hook follows the same rule, plus one of its own: it must be called in the same order on every render \u2014 see Rules of Hooks.

This is not a style preference. ReactPy calls your component at times and frequencies you do not control, and rendering is where the shared-state bug below lives \u2014 because in ReactPy components run on a server, inside one long-lived Python process.

"},{"location":"reference/components-and-hooks-must-be-pure/#the-contract","title":"The contract","text":"

A component body is allowed to:

Do Example Read its own arguments html.p(title) Call hooks count, set_count = use_state(0) Compute from those values html.p(f\"{count} item(s)\") Define functions it hands to the client {\"onClick\": save} Call other components html.div(None, Row(item))

A component body is not allowed to:

Don't Why it fails Assign to a module-level or global variable Shared by every connection in the process; see The server makes this worse Mutate an object it received as an argument The caller still holds it, so the change is visible outside the render Mutate a value read from use_state The comparison that decides whether to re-render can drop an in-place mutation silently Read the clock, random, or the environment Same inputs, different output \u2014 the formula is broken Do I/O: open, requests, a database, print Runs on every render, at a frequency you do not control Start a task, thread, or timer Leaks one per render

The two halves of the rule are independent. random.choice mutates nothing and is still impure of output; writing a module-level dict is an outside effect whether or not it changes what you return.

The pair below is the difference, side by side. Both render identically on the first pass. The first one counts renders in a module-level variable, so the number it prints is a property of the process, not of the component \u2014 press the button twice in two browser tabs and the two pages disagree about what \"the same render\" produced:

impure.pypure.py Run
# Module-level state is shared by every connection and survives across renders,\n# so `total` is not part of the component's inputs at all. Two browsers, or two\n# renders of the same component, see the same counter.\ntotal = 0\n\n\n@component\ndef impure_counter():\n    global total\n    count, set_count = use_state(0)\n\n    # Render mutates module state: an outside effect, and a value that changes\n    # what the next render returns even though nothing the component received\n    # changed. Nothing warns about this -- the symptom is a number that does not\n    # match the button presses.\n    total += 1\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.p(f\"renders (module-wide): {total}\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
@component\ndef counter():\n    count, set_count = use_state(0)\n\n    # Nothing outside this component is touched. `count` comes from use_state,\n    # which ReactPy keeps per component instance -- so two connections to this\n    # same component each have their own number, and re-running the render\n    # changes nothing but the value returned.\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n
"},{"location":"reference/components-and-hooks-must-be-pure/#the-server-makes-this-worse","title":"The server makes this worse","text":"

In a browser, module-level state belongs to one user. In ReactPy it belongs to every connection your process serves, for the whole life of the process. Rendering into a shared container is therefore not a code smell but a data leak \u2014 and it produces no error.

Three separate layouts, three separate visitors, one module-level dict:

SESSION = {}\n\n\n@component\ndef account(name):\n    SESSION.setdefault(\"cart\", [])\n    SESSION[\"cart\"].append(f\"{name}-item\")\n    return html.div(f\"{name}: cart has {len(SESSION['cart'])} item(s)\")\n

Measured output from three independent layouts:

visitor A: cart has 1 item(s): ['A-item']\nvisitor B: cart has 2 item(s): ['A-item', 'B-item']\nvisitor C: cart has 3 item(s): ['A-item', 'B-item', 'C-item']\n

Nothing crashes, each page looks plausible, and every visitor after the first sees other people's data. The same failure appears with total += 1 at module scope, a module-level cache, or a mutable default argument on a component.

Per-user state belongs in use_state or use_reducer, which ReactPy keeps per component instance. State genuinely shared across connections belongs in an explicit store you pass in \u2014 through context or an argument \u2014 never in a bare module global that rendering reaches for.

"},{"location":"reference/components-and-hooks-must-be-pure/#where-side-effects-belong","title":"Where side effects belong","text":"Need Put it in React to a click, keystroke, submit An event handler (Responding to Events) Run once on mount, or when values change use_effect (Use Effect) Await something outside the render use_async_effect (Use Async Effect) Read a request's headers or URL use_connection, use_scope, use_location Build a value for the render, only when inputs change use_memo (Use Memo) Log for debugging use_debug_value, or a logging logger outside the render Load data at process start Executor startup (ReactPy)

An event handler is the default answer. It is defined inside the component but runs later, in response to something the user did, so it may do anything a pure function may not.

use_effect is the escape hatch, not the habit

Effects run after the render, which makes them correct for touching the outside world and awkward for expressing logic. Most of the data you would reach for an effect for can be computed during the render instead. If you find yourself writing an effect to derive a value, stop and compute it in the body.

"},{"location":"reference/components-and-hooks-must-be-pure/#hooks-must-be-pure-too","title":"Hooks must be pure too","text":"

A hook body is a function your component calls during its render, so it inherits every rule above, plus these:

  • No global, no module mutation. Same leak, harder to see because the hook is shared by several components.
  • No time, no randomness. use_memo(lambda: random.random(), []) fixes each instance to whatever the first render produced. Pass the value in as an argument instead.
  • No I/O. A custom hook that queries a database runs that query on every render of every component that uses it.
  • No conditional hook calls, and no hooks in loops. That is the Rules of Hooks, and it is the one a reviewer can catch by reading.

A custom hook that needs the outside world should take the data as an argument, or expose an event handler that does the work when called.

"},{"location":"reference/components-and-hooks-must-be-pure/#what-reactpy-cannot-do-with-purity","title":"What ReactPy cannot do with purity","text":"

React skips re-rendering a component whose props are unchanged \u2014 safe because pure functions are safe to cache. ReactPy has no such bailout: a child component re-runs whenever its parent renders, no matter how pure it is.

There is use_memo for values and use_callback for function identity, which reduce the work a render does, but neither stops the render. So purity here buys correctness, testability, and safety under restart \u2014 not performance. Do not add memoization on the theory that ReactPy will skip the render; it will not.

Why the rule is enforced by review, not by tests

ReactPy cannot detect impurity \u2014 reading a global is a legal Python statement. The rule is about where the language lets you put things, which is why it belongs in code review.

"},{"location":"reference/components-and-hooks-must-be-pure/#testing-purity","title":"Testing purity","text":"

A pure component is an ordinary function, so test it as one \u2014 no browser:

import asyncio\n\nfrom reactpy.core.layout import Layout\n\n\ndef render_model(component):\n    async def go():\n        async with Layout(component) as layout:\n            return (await layout.render())[\"model\"]\n\n    return asyncio.run(go())\n\n\ndef test_same_arguments_same_output():\n    assert render_model(Greeting(\"a\")) == render_model(Greeting(\"a\"))\n

Run each layout in its own event loop and build a fresh component instance per call. If a component renders differently the second time, or one layout's render changes another's output, it is impure and the test says so \u2014 which is exactly what happens for a component that counts its own renders.

"},{"location":"reference/components-and-hooks-must-be-pure/#symptoms","title":"Symptoms","text":"You see Likely cause Two users see each other's data Module-level state written during render A counter is higher than the number of clicks The component rendered more often than you assumed, and it counts renders Numbers change on reload with no user action Clock or random during render A list mutation does nothing State mutated in place; see Updating Arrays in State One component's render changes another's output A shared object reached through a global, a default argument, or a class attribute"},{"location":"reference/components-and-hooks-must-be-pure/#related","title":"Related","text":"
  • Rules of React \u2014 the section overview
  • Rules of Hooks \u2014 the hook-order rule, and what breaking it does in ReactPy
  • React calls Components and Hooks \u2014 why \"it works locally\" does not prove purity
  • Keeping Components Pure \u2014 the tutorial version, with exercises
  • Use State \u2014 per-instance state, and the comparison that decides re-renders
  • Use Effect \u2014 the sanctioned place for side effects
"},{"location":"reference/jinja/","title":"Jinja","text":""},{"location":"reference/jinja/#overview","title":"Overview","text":"

ReactPyJinja adds three tags to a Jinja2 environment so an existing Python web app can mount ReactPy components inside templates it already owns \u2014 one tag for server-rendered components, two for components that run in the browser via PyScript.

{% component \"my_app.pages.counter\" %}\n

Install the extra the extension needs:

pip install \"reactpy[asgi,jinja]\"\n

It lives at reactpy.templatetags.ReactPyJinja. Despite the templatetags name it is not a Django app \u2014 it is a jinja2-simple-tags StandaloneTag, so it works with any Jinja2 environment.

"},{"location":"reference/jinja/#reference","title":"Reference","text":""},{"location":"reference/jinja/#registering","title":"Registering the extension","text":"

Two equivalent forms:

from jinja2 import Environment, FileSystemLoader\n\nfrom reactpy.templatetags import ReactPyJinja\n\n# at construction\nenv = Environment(loader=FileSystemLoader(\"templates\"), extensions=[ReactPyJinja])\n\n# or afterwards\nenv = Environment(loader=FileSystemLoader(\"templates\"))\nenv.add_extension(ReactPyJinja)\n

Both register all three tags at once; there is no way to register one alone, and no configuration to pass. Registering ReactPyJinja(...) as an instance and calling .register() does not work \u2014 that object has no register method.

Register before a template is compiled

Tags are resolved when a template is parsed. If the environment has already compiled a template that uses them, you get jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'component'. Frameworks that cache templates at startup make this a startup-time error, which is easy to attribute to something else.

"},{"location":"reference/jinja/#component-tag","title":"{% component \"dotted.path\" %}","text":"

Emits the markup that mounts a server-side component: an empty mount element, an import-map fallback, and a module script that calls mountReactPy.

{% component \"my_app.pages.counter\" %}\n{% component \"my_app.pages.badge\", class=\"badge\" %}\n
  • The dotted path (required): the import path of the root component function \u2014 the same string you give ReactPyMiddleware's root_components. It is embedded verbatim into the emitted JavaScript; ReactPy imports it on the server when the browser asks to connect.
  • class (optional): a CSS class for the generated mount element, producing <div id=\"...\" class=\"badge\"></div>.

safe_output is set on the tag, so Jinja does not escape the returned HTML even under autoescape=True.

Arguments are parsed as Python expressions

The tag uses Jinja's argument parser, so keyword arguments must follow a comma \u2014 {% component \"m.f\" class=\"x\" %} fails at template-compile time with TemplateSyntaxError: expected token ',', got 'class'. Write {% component \"m.f\", class=\"x\" %}.

The path may be a quoted string (as above) or a Jinja expression that resolves to one; in the latter case it is read from the template context, so an unquoted my_app.pages.counter requires my_app to be a context variable.

"},{"location":"reference/jinja/#errors-this-tag-raises","title":"Errors this tag raises","text":"Mistake Error Omitting the path TypeError: component() missing 1 required positional argument: 'dotted_path' Passing a second positional TypeError: component() takes 1 positional argument but 2 were given Any other keyword ValueError: Unexpected keyword arguments: id, style A typo in the tag name TemplateSyntaxError: Encountered unknown tag 'bogus_tag'.

A wrong dotted path is not one of them. Nothing checks that the module or attribute exists: the tag happily emits markup pointing at a path that will never connect, and the only symptom is a permanently empty element plus a failed request in the browser console. Verify the path against what you passed to root_components.

"},{"location":"reference/jinja/#pyscript-setup","title":"{% pyscript_setup %}","text":"

Emits the PyScript bootstrap \u2014 stylesheet, module script, and the config that installs ReactPy into the browser.

<head>\n    {% pyscript_setup %}\n</head>\n
  • extra_py (optional, positional, repeatable): extra Python packages for the PyScript runtime \u2014 {% pyscript_setup \"numpy\", \"rich\" %}. They are appended to the package list alongside ReactPy's own wheel.
  • extra_js (optional, keyword): a JSON object mapping module URLs to names, e.g. extra_js='{\"./my.js\": \"mylib\"}'. It is merged into the config's js_modules.main. A plain string that is not valid JSON raises json.decoder.JSONDecodeError.
  • config (optional, keyword): a JSON object (or JSON string) merged over the generated config, letting you set anything PyScript accepts.

Put this tag in the document head, once. A page with several pyscript_component tags needs one bootstrap, not one per component.

Packages install in the browser

Anything in extra_py must be installable under Pyodide \u2014 pure Python, or a wheel built for it. A package with C extensions that installs cleanly on the server will fail in the browser, and the failure appears only in the browser console.

"},{"location":"reference/jinja/#pyscript-component-tag","title":"{% pyscript_component \"path/to/file.py\" %}","text":"

Emits a <div> plus a <script type='py'> that runs the named file's component in the browser.

{% pyscript_component \"components/counter.py\" initial=\"Loading...\" %}\n
  • File paths (required, positional, repeatable): paths to Python files, resolved relative to the process's current working directory at template-render time. Several files may be passed; they are concatenated into one script.
  • initial (optional): markup rendered inside the mount element while PyScript boots.
  • root (optional): the name of the root component function, default \"root\".

Because the files are read and inlined, the browser never fetches them and your app serves no route for them.

The file's source ships to the client

Nearly the whole file is embedded in the HTML: executors/pyscript/utils.py, minify_python deletes # comments and inner docstrings before inlining, but the code and every string literal survive. One docstring does escape the filter \u2014 the module docstring, because the regex that removes docstrings needs a newline before the opening quotes and that docstring sits at the very start of the file (measured: a module docstring reached the page, a function docstring and both comment styles did not). Treat all of it as published, including any secret read into a string at render time. This is unrelated to debug mode: turning reactpy.config.REACTPY_DEBUG off hides nothing here.

Mistake Error File does not exist (wrong cwd or path) FileNotFoundError: [Errno 2] No such file or directory: 'components/missing.py' No root function in the file ValueError: Could not find the root component function 'root' in your PyScript file(s).

Both are raised at render time, so they surface as a 500 on the page \u2014 not as a broken widget.

Prefer ReactPyCsr unless you are embedding

For a page built from scratch, ReactPyCsr is an ASGI app with its own URL and handles the setup tag for you. These two tags exist for putting a component inside a template you do not control. See ReactPyCsr.

"},{"location":"reference/jinja/#usage","title":"Usage","text":""},{"location":"reference/jinja/#a-server-rendered-component-in-an-existing-page","title":"A server-rendered component in an existing page","text":"main.pyhome.htmlpages.py Run
templates = Jinja2Templates(\n    env=Environment(\n        loader=FileSystemLoader(\"templates\"),\n        extensions=[ReactPyJinja],\n        autoescape=True,\n    )\n)\n\n\nasync def home(request):\n    return templates.TemplateResponse(request, \"home.html\")\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", home)])\n\n# The dotted path in the template is resolved on the server, not in the\n# template: the browser asks ReactPy to render `pages.counter`, and ReactPy\n# imports it to satisfy that request. Register exactly the paths the template\n# passes to `{% component %}` -- here the `counter` and `badge` functions in\n# the sibling `pages.py`.\nmy_app = ReactPyMiddleware(\n    starlette_app, root_components=[\"pages.counter\", \"pages.badge\"]\n)\n
<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <title>Accounts</title>\n</head>\n\n<body>\n  <h1>Welcome</h1>\n\n  {# One tag per component; ReactPy gives each mount element its own id. #}\n  {% component \"pages.counter\" %}\n\n  {# `class` must follow a comma -- the tag parses its arguments as Python. #}\n  {% component \"pages.badge\", class=\"badge\" %}\n</body>\n\n</html>\n
from reactpy import component, html, use_state\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n\n\n@component\ndef badge():\n    # The tag's `class` argument styles the mount element, not this markup, so\n    # the component is free to render whatever it likes inside it.\n    return html.div(\n        html.span({\"class\": \"badge-label\"}, \"Status:\"),\n        html.span({\"class\": \"badge-value\"}, \"active\"),\n    )\n
# TODO\n

The components cannot live in main.py even though that is where the wiring is. The browser reaches them by dotted path, so they must sit in a module the server can import \u2014 a component defined next to the view is reachable as main.counter, but then the template has to say exactly that. Whatever you choose, the path in {% component %} and the entry in root_components must agree character for character. Nothing checks it for you: a mismatch emits markup that mounts an element which stays empty forever, and the failed import surfaces only as a server-side traceback on the websocket request.

"},{"location":"reference/jinja/#a-client-side-component","title":"A client-side component","text":"

Setup in the head, the component where it belongs:

home.htmlcounter.py Run
<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Counter</title>\n    {% pyscript_setup %}\n  </head>\n  <body>\n    {% pyscript_component \"components/counter.py\" initial=\"Loading...\" %}\n  </body>\n</html>\n
@component\ndef root():\n    # `root` is the name the tag looks for by default; pass root=\"other\" to\n    # point it somewhere else. This file is inlined into the page at render\n    # time and runs in the browser, so treat all of it as public.\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n

There is no websocket, so use_connection, use_scope, and use_location have no connection to read and raise \u2014 see Use Connection.

"},{"location":"reference/jinja/#which-one-to-use","title":"Which one to use","text":"{% component %} {% pyscript_setup %} + {% pyscript_component %} Component runs Server Browser Needs a websocket Yes No Component source visible to users No Yes First paint Server HTML, then it connects initial until PyScript boots Can read the request Yes (use_connection, use_scope, use_location) No Needs a route on your app Yes No Packages must be Pyodide-compatible No Yes"},{"location":"reference/jinja/#related","title":"Related","text":"
  • Add React to an Existing Project \u2014 the full Starlette wiring these tags are for
  • ReactPyMiddleware \u2014 the middleware that serves server-rendered components, and the markup this tag generates
  • ReactPyCsr \u2014 the ASGI-app form of the PyScript tags
  • ReactPy \u2014 standalone server-rendered app
  • Common Props \u2014 what a component may return
"},{"location":"reference/overview/","title":"Overview","text":"

This section is the detailed reference for ReactPy. For an introduction, start with the Learn section.

The reference is split into functional subsections:

"},{"location":"reference/overview/#hooks","title":"Hooks","text":"

Call these at the top level of a component. ReactPy provides twelve:

  • use_state \u2014 declare a state variable.
  • use_effect \u2014 run a synchronous side effect.
  • use_async_effect \u2014 run an async def effect, or one that keeps running across renders.
  • use_reducer \u2014 hold state behind a reducer.
  • use_context \u2014 read a value provided higher in the tree.
  • use_ref \u2014 hold a mutable box that survives renders.
  • use_memo \u2014 cache a computed value.
  • use_callback \u2014 cache a function.
  • use_debug_value \u2014 attach a debug label to a component.
  • use_connection \u2014 inspect the current websocket connection.
  • use_scope \u2014 read the ASGI connection scope.
  • use_location \u2014 read the browser's current URL.

There is no use_id, use_transition, use_deferred_value, use_layout_effect, use_insertion_effect, use_imperative_handle, or use_sync_external_store. Texts ported from React that rely on them have no ReactPy equivalent.

"},{"location":"reference/overview/#html","title":"HTML","text":"

How to describe markup. ReactPy has no JSX; you call functions.

  • Common Props \u2014 attributes shared by every tag.
  • Usage \u2014 the html.* functions and the shape of a VDOM dictionary.
"},{"location":"reference/overview/#executors","title":"Executors","text":"

The ASGI applications that serve your components:

  • ReactPy \u2014 standalone single-page app; components render on the server.
  • ReactPyMiddleware \u2014 add ReactPy routes to an ASGI app you already have.
  • ReactPyCsr \u2014 run component code in the browser via PyScript.
"},{"location":"reference/overview/#template-integration","title":"Template integration","text":"
  • Jinja \u2014 render components from Jinja templates.
"},{"location":"reference/overview/#rules-of-react","title":"Rules of React","text":"

Idioms for expressing React patterns so they stay understandable and correct:

  • Components and Hooks must be pure
  • React calls Components and Hooks
  • Rules of Hooks
"},{"location":"reference/overview/#internals","title":"Internals","text":"
  • Protocol Structure \u2014 the messages exchanged between server and client.
  • Client API \u2014 the browser-side entry point.
"},{"location":"reference/overview/#where-react-differs","title":"Where React differs","text":"

ReactPy models React's function-component API, but it is not React. Effects infer their dependencies instead of requiring an array, state updates are replaced rather than merged, an update that mutates in place is dropped without any error, and several React hooks do not exist at all. Each page calls out the differences that matter to it, so when porting code or prose from a React tutorial, read the ReactPy page rather than assume parity.

"},{"location":"reference/protocol-structure/","title":"Protocol Structure","text":""},{"location":"reference/protocol-structure/#overview","title":"Overview","text":"

ReactPy renders on the server and paints in the browser. A single websocket carries four message types: the server sends \"layout-update\" patches, the client sends \"layout-event\" interactions, and a handler which wants to read the browser goes back and forth over \"event-proxy-request\" / \"event-proxy-response\".

server --{\"type\": \"layout-update\", \"path\": ..., \"model\": ...}--------------->  client\nserver <--{\"type\": \"layout-event\", \"target\": ..., \"data\": [...]}------------  client\nserver --{\"type\": \"event-proxy-request\", \"eventId\": ..., \"path\": ...}------->  client\nserver <--{\"type\": \"event-proxy-response\", \"requestId\": ..., \"value\": ...}-  client\n

The last two are new, and they change what an event is. Nothing about the DOM event is serialized at all: the client keeps the live object in a registry of its own and sends the server a handle. What the server can do with that handle \u2014 and what happens when the browser never answers \u2014 is the subject of The event proxy round trip.

"},{"location":"reference/protocol-structure/#the-connection","title":"The connection","text":"

Every mounted component opens exactly one websocket. The client builds it from the pathPrefix and componentPath it was handed at mount time (static/index.js, mountReactPy):

ws://<host><pathPrefix><componentPath>?path=<page path>&qs=<page query string>\n

With the default REACTPY_PATH_PREFIX of \"/reactpy/\" and a component registered as \"main.counter/\", mounting on /page?tab=1 opens:

ws://127.0.0.1:8000/reactpy/main.counter/?path=%2Fpage&qs=%3Ftab%3D1\n

Two things about that URL are load-bearing:

  • The trailing slash after the dotted path is required. The server matches the websocket path against

    ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$\n

    which ends in /$. Without the slash the route never matches, the request falls through to your own ASGI app, and the handshake is refused. Verified from a page: ws://\u2026/reactpy/main.counter/?path=/ opens, ws://\u2026/reactpy/main.counter?path=/ fails with Unexpected response code, and the console logs [ReactPy] Failed to connect!.

  • path and qs are how the component learns its URL. ReactPy runs on the server, where the browser's location does not exist. The client copies window.location.pathname into path and window.location.search into qs; the server parses them back out (executors/asgi/middleware.py, _location_from_websocket_query_string) into the Location that use_location() returns. They are percent-encoded and carried in the websocket's own query string, so a server that normalises the handshake URL cannot silently rewrite them.

Reconnection does not re-read the mount

When the socket drops, the client waits and reconnects. Each attempt refreshes path and qs from the current page, but it never re-reads componentPath or the connect options \u2014 those were captured when the element mounted. A client-side navigation that changes which component belongs on the page needs a fresh mount, not a reconnect.

The client retries with exponential backoff: REACTPY_RECONNECT_INTERVAL (750 ms) times REACTPY_RECONNECT_BACKOFF_MULTIPLIER (1.25) per attempt, capped at REACTPY_RECONNECT_MAX_INTERVAL (60 s), for REACTPY_RECONNECT_MAX_RETRIES (150) tries. Those are arguments to mountReactPy(...); see ReactPyMiddleware for the bootstrap that emits them.

"},{"location":"reference/protocol-structure/#the-server-to-client-message","title":"The server-to-client message","text":"

One message type goes down the wire. Its keys are fixed (reactpy.types.LayoutUpdateMessage):

Key Value Meaning \"type\" \"layout-update\" Always this literal. \"path\" str A JSON Pointer into the client's copy of the model. \"\" means replace everything. \"model\" dict The value to place at that pointer.

The model is JSON, not a VdomDict. Six key names are legal (reactpy/types.py, ALLOWED_VDOM_KEYS), and only tagName is required:

Key Value Notes \"tagName\" str The DOM tag, or \"\" for a wrapper element. \"children\" list Nested models or plain strings. \"attributes\" dict Sent to the client as written, and written to the DOM essentially verbatim. Two ReactPy-specific exceptions: key is read for reconciliation and consumed, never becoming an attribute, and _reactpy_ack_seq is deleted by the client before render. \"eventHandlers\" dict Handler name to target spec, described below. \"inlineJavaScript\" dict Handler name to JavaScript source string. \"importSource\" dict Marks an element as coming from a JavaScript module.

Any other key name is rejected by VdomDict while the element is being built (reactpy/types.py):

ValueError: Invalid keys: {'bogus'}.\n

The wire schema in core/vdom.py, VDOM_JSON_SCHEMA is a looser thing: it lists these six plus \"error\" (which the layout injects when a component raises), and it is only consulted when REACTPY_CHECK_VDOM_SPEC or REACTPY_DEBUG is on.

Here is a real mount message for a counter, captured off the wire:

{\n  \"type\": \"layout-update\",\n  \"path\": \"\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"children\": [\n      {\n        \"tagName\": \"\",\n        \"children\": [\n          {\n            \"tagName\": \"\",\n            \"children\": [\n              {\n                \"tagName\": \"div\",\n                \"children\": [\n                  {\"tagName\": \"h1\", \"children\": [\"count is 0\"]},\n                  {\n                    \"tagName\": \"button\",\n                    \"eventHandlers\": {\n                      \"onClick\": {\n                        \"target\": \"/0/0/0/1:onClick\",\n                        \"preventDefault\": false,\n                        \"stopPropagation\": false\n                      }\n                    },\n                    \"children\": [\"Add\"]\n                  }\n                ]\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}\n
"},{"location":"reference/protocol-structure/#wrapper-elements","title":"Wrapper elements","text":"

Read from the top, that model is three elements with \"tagName\": \"\" before the div appears. ReactPy needs somewhere to put a component's output that is not itself a DOM node, and it does not reuse your tag for the purpose.

Do not depend on how many there are. The count is an implementation detail that shifts with structure: the same component produced one wrapper rendered bare through Layout, and three behind the ASGI executor's context provider. Never index the tree by position, and never assume the root has a tag name. The client renders a wrapper as a fragment, so it costs you nothing on screen.

"},{"location":"reference/protocol-structure/#patches-not-re-renders","title":"Patches, not re-renders","text":"

The server does not resend the tree. When only an inner component's state changes, it sends the subtree rooted where that component renders:

{\n  \"type\": \"layout-update\",\n  \"path\": \"/children/0/children/0\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"children\": [\n      {\n        \"tagName\": \"div\",\n        \"children\": [\n          {\"tagName\": \"h1\", \"children\": [\"count is 1\"]},\n          {\n            \"tagName\": \"button\",\n            \"eventHandlers\": {\n              \"onClick\": {\n                \"target\": \"/0/0/0/1:onClick\",\n                \"preventDefault\": false,\n                \"stopPropagation\": false\n              }\n            },\n            \"children\": [\"Add\"]\n          }\n        ]\n      }\n    ]\n  }\n}\n

That is the whole reply to a click: one-third the depth of the mount, and the only text that changed is inside it. The pointer indexes the model, and path: \"\" replaces the root \u2014 which is what a state change in the topmost component produces.

A patch replaces rather than merges, so whatever subtree it carries is complete: keys you would expect to be sticky, eventHandlers among them, are resent inside it. Nothing is deleted by a patch; a smaller subtree simply arrives with fewer children.

"},{"location":"reference/protocol-structure/#errors-arrive-as-a-model","title":"Errors arrive as a model","text":"

A component that raises during render does not kill the socket. The layout sends an element whose tagName is empty and whose error is the message, which the client prints in a <pre> in place of the component:

{\n  \"type\": \"layout-update\",\n  \"path\": \"\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"error\": \"ValueError: Expected a VDOM element dict, not {'noTag': True}\"\n  }\n}\n

The schema ties the two together: when error is present, tagName must be empty. Without REACTPY_DEBUG=1 the same element carries \"error\": \"\" \u2014 an empty string, which the client renders as nothing, so the component silently disappears instead of showing a blank box.

"},{"location":"reference/protocol-structure/#the-client-to-server-message","title":"The client-to-server message","text":"

One message type goes up (reactpy.types.LayoutEventMessage):

Key Value Meaning \"type\" \"layout-event\" Always this literal. \"target\" str The handler's target, copied from the model. \"data\" list The arguments for the handler. Usually one event handle. \"seq\" int Optional. A client-assigned counter, per element.

A real click on the button above:

{\n  \"type\": \"layout-event\",\n  \"target\": \"/0/0/0/1:onClick\",\n  \"data\": [{\"__reactpy_event__\": {\"id\": \"lzk4q1-0\"}, \"type\": \"click\"}],\n  \"seq\": 1\n}\n
"},{"location":"reference/protocol-structure/#how-data-is-built","title":"How data is built","text":"

The client does not serialize the DOM event. It stores the live object in a registry and sends a handle \u2014 {\"__reactpy_event__\": {\"id\": ...}} \u2014 plus \"type\", the one plain value the client attaches because the server needs it to name the event and it is too cheap to be worth a round trip (vdom.tsx). Anything else the handler wants is fetched, per property, over the proxy messages described below.

That replaced the old event-to-object package, which walked the event and its target: enumerable properties, plus value, checked, files, type and name read off the element even though they are accessors, plus dataset, plus every named control of a form on submit. A single <input> used to put the whole element on the wire \u2014 around ninety keys, of which a handler reads one. What goes over the wire for an event now is a handle and a type: on the order of a hundred and fifty bytes.

The consequence for you: the second element of data is whatever the event was dispatched with, and the first is the handle \u2014 a dict of the marker and nothing but the id, never a copy of the event. Handlers are still free to take more than one argument \u2014 vdom.tsx builds the list from the event plus any extra values the dispatch supplied \u2014 so index data positionally rather than counting on a specific key surviving.

"},{"location":"reference/protocol-structure/#unpacking-and-why-the-two-handler-forms-differ","title":"Unpacking, and why the two handler forms differ","text":"

Layout._process_event_queue turns data into arguments and calls the handler \u2014 but not the same way for both kinds of handler (core/events.py, to_event_handler_function):

function(*data)   # a plain coroutine: the list is unpacked\nfunction(data)    # an EventHandler: the list itself\n

So async def f(event) receives the event when passed directly, while async def f(data) must index into the list once wrapped in EventHandler. Converting a working handler just to add prevent_default breaks it in a way the traceback does not explain. Both forms are coroutines: EventHandler.__init__ and to_event_handler_function each run the value through check_is_async_handler, so a sync function or a lambda raises at element-build time. Common Props shows both shapes side by side.

"},{"location":"reference/protocol-structure/#the-event-proxy-round-trip","title":"The event proxy round trip","text":"

A handler which reads await event.target.value makes the server ask the browser. Layout.set_event_proxy_sender wires the backend to the connection, and the request that goes down is one per property (_event_proxy.py):

{\n  \"type\": \"event-proxy-request\",\n  \"requestId\": \"f5a9b645f0a145e984b7c2d8e19f753c\",\n  \"eventId\": \"lzk4q1-0\",\n  \"path\": [\"target\", \"value\"],\n  \"call\": false,\n  \"args\": [],\n  \"reply\": true,\n  \"maxDepth\": 10\n}\n

The two ids come from different sides and mean different things. \"requestId\" is a fresh uuid4().hex per request, minted by the server so it can match the answer to whoever asked. \"eventId\" is the client's own registry key for the object behind the handle \u2014 an opaque string such as \"lzk4q1-0\" from EventProxyRegistry.register, never a handler target \u2014 so build it from the handle you were given and do not try to guess one.

The client answers with the value, or with a failure it can name:

{\n  \"type\": \"event-proxy-response\",\n  \"requestId\": \"f5a9b645f0a145e984b7c2d8e19f753c\",\n  \"ok\": true,\n  \"value\": \"typed text\",\n  \"error\": null,\n  \"code\": null\n}\n

Two requests raised in the same synchronous stretch \u2014 a batch via await event.read(...), or an asyncio.gather of reads \u2014 are flushed together as {\"type\": \"event-proxy-request\", \"requests\": [ ... ]} and answered the same way with a \"responses\" array. Requests which genuinely depend on each other (await one, then the next) cost one round trip each.

A client that never answers does not hang the handler. After REACTPY_EVENT_PROXY_TIMEOUT seconds the read raises EventProxyTimeout. Strikes are counted by the connection's EventProxyBackend \u2014 one backend per layout, so one per connection \u2014 and one unanswered message costs one strike however many properties it was asking for; after REACTPY_EVENT_PROXY_MAX_TIMEOUTS consecutive unanswered round trips, live access is switched off for that connection and every later read raises EventProxyUnavailable at once rather than stalling. Binding a new sender \u2014 a reconnect \u2014 clears the strikes and gives the client another chance. REACTPY_EVENT_PROXY_MAX_DEPTH bounds how deep a returned object is expanded. See Configuring ReactPy.

The client is the one that decides what may be read. It refuses the members that would let a server reach outside the event \u2014 window, document, parentNode, eval, setTimeout, and friends (BLOCKED_MEMBERS in event-proxy.ts) \u2014 and it also refuses __reactpy_event__, __reactpy_ref__, and anything matching /^on[a-z]/i, so a handler cannot read the handle back off its own event or fish for another one. A refusal is an error on the response, not a value.

The client also decides how long an event stays readable, and it enforces that two different ways. An event which the server never once asked about is forgotten after LIVE_OBJECT_TTL_MS (60 s); one the server has read is exempt, because the handler may simply be slow. What bounds the page overall is MAX_LIVE_OBJECTS (512): past that, the event the server has gone longest without reading is dropped. Either way the id keeps working as an address and keeps explaining why it failed \u2014 \"not-found\" for one that aged out unread, \"evicted\" for one dropped to make room while this side was still reading it \u2014 which is the difference between EventProxyNotFound and EventProxyEvicted. An \"evicted\" points at the page rather than at your handler.

Build a non-Python client from this page and you must implement both proxy message types as well, or every await on an event raises. The stock client does, which is why a handler written against it works.

"},{"location":"reference/protocol-structure/#the-target-string","title":"The target string","text":"

\"/0/0/0/1:onClick\" is two parts joined by a colon:

  • Before the colon, the element's key path: one segment per level, where each segment is that child's key if it has one and its index otherwise (core/layout.py, _get_children_info and _make_element_model_state). Patch path\\ s, by contrast, are always positional (.../children/<index>). The two line up only while nobody sets a key \u2014 give a child {\"key\": \"row-3\"} and its target segment becomes row-3 while its patch path stays children/2.
  • After the colon, the attribute name the handler was declared under. It is whatever key you used, which is why {\"onMyThing\": f} produces a target ending in :onMyThing.

Treat the whole thing as an opaque handle. It is generated per render and is not meant to be constructed by hand; two structurally identical elements get distinct targets because their index paths differ.

Events for unmounted elements are dropped, not queued

If the server has no handler for a target \u2014 because the element was removed since the client last rendered, or the target was mistyped \u2014 the event is discarded with an informational log and the socket stays open:

Ignored event - handler '/99/99:nope' does not exist or its component unmounted\n

ReactPy retries the lookup three times at 10 ms intervals first, to cover a handler that is briefly absent mid-render. That window is a race guard, not a queue: nothing is replayed later. A click that happens while the socket is down is held in a client-side send buffer and flushed if the connection reopens \u2014 but once REACTPY_RECONNECT_MAX_RETRIES is exhausted the client stops trying, and everything buffered stays unsent.

"},{"location":"reference/protocol-structure/#seq-and-typed-input","title":"seq and typed input","text":"

Typing needs a protocol answer that a plain request/response does not give. Each keystroke sends an event carrying a counter the client incremented for that element. The server records the highest number it has finished handling, per event name for that target, and on the next render of an <input>, <select>, or <textarea> stamps that whole mapping into the element's attributes under _reactpy_ack_seq \u2014 not one number for the element, because a single counter would let one event's progress stand in for another's (core/layout.py, _inject_event_ack_seq):

{\n  \"type\": \"layout-update\",\n  \"path\": \"/children/0/children/0\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"children\": [\n      {\n        \"tagName\": \"input\",\n        \"attributes\": {\"value\": \"hello world\", \"_reactpy_ack_seq\": {\"onInput\": 1}},\n        \"eventHandlers\": {\n          \"onInput\": {\n            \"target\": \"/0/0/0:onInput\",\n            \"preventDefault\": false,\n            \"stopPropagation\": false\n          }\n        }\n      }\n    ]\n  }\n}\n

The client compares that number against its own counter to decide whether the server has caught up with the latest keystrokes. If the server is still behind, the client keeps what the user typed instead of overwriting the box with a stale value. Without it, a slow handler erases text mid-word \u2014 the classic complaint about server-rendered inputs.

The value lives in a dedicated attribute rather than among your own so it cannot collide with, or leak into, a real DOM attribute. It is added only to those three tags, and only once at least one event has arrived for the element.

Debounce and throttle do travel on the wire

They change when events leave the browser, not what is in them \u2014 but the layout still has to tell the client about them. core/layout.py, _serialize_event_handler copies both into the handler's model entry whenever they are not None, and vdom.tsx reads them back off that entry. @event(handler, debounce=250, throttle=500) arrives as:

{\n  \"onClick\": {\n    \"target\": \"/0:onClick\",\n    \"preventDefault\": false,\n    \"stopPropagation\": false,\n    \"debounce\": 250,\n    \"throttle\": 500\n  }\n}\n

Both are omitted entirely when None, which is what keeps the common case minimal. On arrival the client validates each with isValidDebounce \u2014 finite, non-negative integer \u2014 and an invalid one is dropped with a console.warn naming the handler rather than an error, leaving the handler live but un-debounced. Build a non-Python client from this page and you must carry these two fields; skipping them silently loses the throttling. See Common Props.

One further key may appear in that entry, and it is new with this release: \"stopImmediatePropagation\". It is a boolean like its two neighbours, but unlike preventDefault and stopPropagation \u2014 which are always sent \u2014 it is omitted rather than sent as false: _serialize_event_handler in core/layout.py adds the key only when the handler asked for it, so read its absence as false. @event(stop_immediate_propagation=True), or a detected event.stopImmediatePropagation() in the handler's body, is what puts it there. The client then calls stopImmediatePropagation() on the event before running the handler, so the other listeners on that same element are skipped too (vdom.tsx).

"},{"location":"reference/protocol-structure/#watching-it-yourself","title":"Watching it yourself","text":"

The protocol is plain JSON text frames, so an ordinary websocket client is a sufficient observer. Start this app:

main.py Run
from starlette.applications import Starlette\nfrom starlette.responses import HTMLResponse\nfrom starlette.routing import Route\n\nfrom reactpy import component, html, use_state\nfrom reactpy.executors.asgi import ReactPyMiddleware\nfrom reactpy.executors.utils import server_side_component_html\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def increment(_event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"count is {count}\"),\n        html.button({\"onClick\": increment}, \"Add\"),\n    )\n\n\nasync def home(request):\n    return HTMLResponse(\n        \"<!doctype html>\"\n        \"<html><body>\"\n        + server_side_component_html(\"root\", \"\", \"main.counter/\")\n        + \"</body></html>\"\n    )\n\n\nmy_app = ReactPyMiddleware(\n    Starlette(routes=[Route(\"/\", home)]),\n    root_components=[\"main.counter\"],\n)\n
# TODO\n

Then read the mount message, send a click, and read the patch:

client.py Run
import asyncio\nimport json\n\nimport websockets\n\nURL = \"ws://127.0.0.1:8000/reactpy/main.counter/?path=%2F\"\n\n\nasync def main():\n    patches = asyncio.Queue()\n\n    async with websockets.connect(URL) as socket:\n        mount = json.loads(await socket.recv())\n        print(\"1. the mount update:\", mount[\"path\"] or \"(root)\")\n        print(json.dumps(mount[\"model\"], indent=2))\n\n        target = find_handler_target(mount[\"model\"])\n        print(\"\\n2. sending a click to\", target)\n        await socket.send(\n            json.dumps(\n                {\n                    \"type\": \"layout-event\",\n                    \"target\": target,\n                    # The client keeps the DOM event and sends a handle, not a copy\n                    # of it. \"type\" is the one plain value sent along up front.\n                    \"data\": [\n                        {\"__reactpy_event__\": {\"id\": \"lzk4q1-0\"}, \"type\": \"click\"}\n                    ],\n                    \"seq\": 1,\n                }\n            )\n        )\n\n        # One reader for the whole connection: answer proxy requests as they arrive\n        # and hand everything else to the caller. Two consumers on one websocket\n        # would race for each frame. With a handler which only calls a state setter\n        # \u2014 the counter this observes \u2014 no proxy request is ever sent, and this task\n        # simply forwards patches.\n        reading = asyncio.create_task(read(socket, patches))\n        try:\n            patch = await asyncio.wait_for(patches.get(), timeout=10)\n            print(\"\\n3. the reply is a patch at path:\", repr(patch[\"path\"]))\n            print(json.dumps(patch[\"model\"], indent=2))\n        finally:\n            reading.cancel()\n\n\nasync def read(socket, patches):\n    async for raw in socket:\n        message = json.loads(raw)\n        if message.get(\"type\") == \"event-proxy-request\":\n            await answer_requests(socket, message)\n        else:\n            await patches.put(message)\n\n\nasync def answer_requests(socket, message):\n    \"\"\"Reply to every property path the server is asking about\"\"\"\n    requests = message.get(\"requests\", [message])\n    for request in requests:\n        print(\"   the server asks for\", request[\"path\"])\n    if \"requests\" in message:\n        await socket.send(\n            json.dumps(\n                {\n                    \"type\": \"event-proxy-response\",\n                    \"responses\": [answer(request) for request in requests],\n                }\n            )\n        )\n    else:\n        await socket.send(json.dumps(answer(requests[0])))\n\n\ndef answer(request):\n    \"\"\"The value behind one property path, in the shape the client must use\"\"\"\n    return {\n        \"type\": \"event-proxy-response\",\n        \"requestId\": request[\"requestId\"],\n        \"ok\": True,\n        \"value\": \"typed text\" if request[\"path\"][-1] == \"value\" else None,\n        \"error\": None,\n        \"code\": None,\n    }\n\n\ndef find_handler_target(model):\n    \"\"\"Return the target of the first event handler found in a model.\"\"\"\n    if isinstance(model, dict):\n        for spec in (model.get(\"eventHandlers\") or {}).values():\n            return spec[\"target\"]\n        for child in model.get(\"children\", []):\n            found = find_handler_target(child)\n            if found:\n                return found\n    return None\n\n\nasyncio.run(main())\n
# TODO\n

Both were run against each other, which is where the mount message, the target, and the patch above come from. The counter's handler only calls a setter, so it never reads a property and the proxy messages never appear \u2014 the observer answers them, and gets nothing to answer, which is the \"a read you never make costs nothing\" rule from Responding to Events showing up on the wire. The two proxy frames further up the page were captured from a handler which does read, against the same middleware. Nothing about the protocol is private to ReactPy's client \u2014 given a mount element and a correct target, you can drive a server-rendered component from any language.

The server trusts the client completely

Nothing checks that target names a real handler, that data describes a real event, or that the sender is the page that mounted the component. Whatever your handler reads out of data, it read because the browser asserted it. Treat it as user input: await event.target.value is a string an attacker chose, at any length. The one thing which does not travel in the message at all is the ASGI scope: the server built that itself when the socket was accepted, which is why Use Connection and User Scope are the trusted side of the picture \u2014 even there, remember that the headers and peer address it reports are what the client chose to send.

The proxy did not weaken this; it moved the trust question rather than answering it. EventProxyBackend._route takes \"value\" from a response verbatim \u2014 no schema, no type check \u2014 so a crafted client can make any read return anything, including a list where a str was expected. And a call: true request runs a method in the page, which is why the client, not the server, holds the veto: BLOCKED_MEMBERS and the /^on[a-z]/i rule are the page's defence against a hostile server. Read that as what it is \u2014 the two sides do not trust each other, and only the page defends the page. Anything the client volunteers without being asked (\"type\") is the same kind of assertion as the rest of data.

"},{"location":"reference/protocol-structure/#failure-modes","title":"Failure modes","text":"Symptom Cause Handshake refused, [ReactPy] Failed to connect! Missing trailing slash in componentPath, or a path_prefix that differs between the bootstrap and the server. Socket opens then closes; server logs RuntimeError: Attempting to use an unregistered root component <path>. The dotted path is not in root_components. The socket is accepted before the component is resolved, so the failure is visible only in the server log. Server log: Unknown message type: <type> Anything other than \"layout-event\" or \"event-proxy-response\". The server accepts those two and drops the rest, leaving the connection open. Server log: Ignored event - handler ... does not exist or its component unmounted Stale or wrong target. Console: Received message without type or Received message without handler A frame the client has no handler for. Ignored; the connection stays up. A <pre> with an exception message appears in the page The component raised. See Errors arrive as a model. Component renders once and never updates The handler never fires \u2014 usually a snake_case event name such as on_click, so no event is ever sent. [ReactPy] Disconnected! then Reconnecting in \u2026 The socket dropped and backoff is in effect. If Connection max retries exhausted! follows, the page needs a reload."},{"location":"reference/protocol-structure/#related","title":"Related","text":"
  • Common Props \u2014 the attribute names that become eventHandlers and inlineJavaScript.
  • Usage \u2014 writing the model by hand as a plain dict.
  • ReactPyMiddleware \u2014 the paths the server claims, and the bootstrap that opens the socket.
  • Use Connection and Use Location \u2014 the server-side view of the two query parameters.
"},{"location":"reference/react-calls-components-and-hooks/","title":"React calls Components and Hooks","text":""},{"location":"reference/react-calls-components-and-hooks/#overview","title":"Overview","text":"

You do not call a component or a hook \u2014 ReactPy does. Understanding who calls what explains why components must be pure, why hook order matters, and why calling a component yourself almost never does what you meant.

@component\ndef counter():\n    count, set_count = use_state(0)  # ReactPy runs this, not you\n    return html.h1(count)\n

Writing counter() does not run the function. And use_state does not work from anywhere you like \u2014 it works only while ReactPy is in the middle of rendering the component that called it.

"},{"location":"reference/react-calls-components-and-hooks/#reference","title":"Reference","text":""},{"location":"reference/react-calls-components-and-hooks/#reactpy-calls-components","title":"ReactPy calls components","text":"

@component turns a function into a constructor. Calling it returns a Component object \u2014 a description of \"render this function with these arguments\", to be rendered later, by the layout:

>>> type(counter())\n<class 'reactpy.types.Component'>\n

Component is defined in reactpy/types.py; core/component.py only imports it to build these objects, so reactpy.core.component.Component is an alias, not the class's own module.

The layout is what invokes your function body, at times it decides:

ReactPy calls your component When On mount The first time it appears in the tree On state change A use_state or use_reducer in that component is updated On parent render Whenever the component that renders it re-renders On an event Indirectly \u2014 your handler runs, and any state it sets schedules a render

Two consequences follow immediately.

You do not control how often it runs. A parent re-render re-renders its children, and ReactPy has no memo bailout for components. Code that counts, logs, or accumulates in a component body accumulates faster than the number of user actions. Measured: one press of a parent's button produced exactly one render of the parent and one of an unrelated child \u2014 but nothing guarantees those counts match your mental model once several state updates are in play.

Calling it yourself does not render it. Two mistakes, both silent:

html.div(None, my_component)  # passing the function, not calling it\nhtml.div(None, my_component())  # correct: an instance to be rendered\n

The first is accepted, and the function's repr becomes a text child \u2014 the page shows something like <function my_component at 0x7f0d1b2c4e00>. There is no error, because a callable that is not a known child type is stringified like a number would be. If your page ever displays a hex address, this is why.

Calling .render() on an instance does not work either:

await my_component().render()\n
RuntimeError: No life cycle hook is active. Are you rendering in a layout?\n

Rendering needs a life cycle hook, which only a layout provides. To see what a component produces, put it in one:

import asyncio\n\nfrom reactpy.core.layout import Layout\n\n\ndef render_model(component):\n    async def go():\n        async with Layout(component) as layout:\n            return (await layout.render())[\"model\"]\n\n    return asyncio.run(go())\n
"},{"location":"reference/react-calls-components-and-hooks/#components-are-values","title":"Components are values","text":"

An instance is an ordinary Python object, so you can build one in a loop or pass it as an argument \u2014 the way ReactPy expects you to compose:

html.ul(None, [row(item) for item in items])\n

Each call creates a fresh instance, and each gets its own state. Reusing one instance in two positions in the tree is the thing to avoid: build a new one per position, and key them when they come from a loop.

"},{"location":"reference/react-calls-components-and-hooks/#key-is-reserved","title":"key is a reserved parameter","text":"

A component constructor accepts an optional key keyword, which the layout uses for reconciliation:

[row(item, key=item[\"id\"]) for item in items]\n

Because of that, key is reserved as a component parameter name, and a component that declares one is rejected at import time \u2014 before anything renders:

@component\ndef row(key):  # do not do this\n    return html.li(key)\n
TypeError: Component render function <function row at 0x...> uses reserved parameter 'key'\n

Name the argument something else \u2014 row_key, label, item_id \u2014 and pass the reconciliation key as key= at the call site.

Note this differs from plain elements, where the key lives inside the attributes dict. See Common Props.

"},{"location":"reference/react-calls-components-and-hooks/#hooks-are-called-by-reactpy","title":"Hooks are called by ReactPy, during a render","text":"

A hook is a plain function that reads the current life cycle hook off a task-local stack. That is why it raises outside a render \u2014 and in several places that look like renders:

You call a hook from Result A component body Works \u2014 the intended place A custom hook called from a component body Works, and is how you build one Module level, directly RuntimeError: No life cycle hook is active. Are you rendering in a layout? await component().render() The same RuntimeError An event handler The same RuntimeError, logged on the server as Failed to execute event handler ... An use_effect callback body The same RuntimeError, logged on the server as Error in effect \u2014 a different log line, from a different place

The last two are the trap, and they look alike from the browser and nothing like each other in the log. Calling use_state from inside an event handler does not crash the app; it kills that handler, and the traceback goes to the server log:

Failed to execute event handler EventHandler(...)\n...\nRuntimeError: No life cycle hook is active. Are you rendering in a layout?\n

That line comes only from core/layout.py, _process_event_queue \u2014 delivered browser events. An effect body is a task, not a delivered event, so it fails through core/_life_cycle_hook.py, affect_component_will_unmount, and you see Error in effect instead. Search the log for both strings, not one.

Nothing changes in the browser, so the symptom is \"the button does nothing\". Move the hook call to the component body and close over the value instead.

"},{"location":"reference/react-calls-components-and-hooks/#usage","title":"Usage","text":""},{"location":"reference/react-calls-components-and-hooks/#the-mental-model","title":"The mental model","text":"You ReactPy Define what to render Decide when to render Return VDOM from a component Call the component Declare state with a hook Store it, per component instance Write an event handler Run it, when the event arrives Declare dependencies Compare them, and decide whether to re-run an effect

Everything in the Rules of React follows from the left column being yours and the right column being ReactPy's. You cannot ask ReactPy to render less often, so you write components that are safe to render often.

"},{"location":"reference/react-calls-components-and-hooks/#a-custom-hook-is-a-components-helper-not-a-standalone-function","title":"A custom hook is a component's helper, not a standalone function","text":"

Because a hook is called during a render, a custom hook is only usable from a render:

def use_cart():\n    items, set_items = use_state([])  # fine: runs during the caller's render\n    return items, add_item\n

Calling use_cart() from an event handler, a utility function, or a background task raises the same RuntimeError. If logic must be callable from there, write it as a normal function that takes what it needs as arguments.

"},{"location":"reference/react-calls-components-and-hooks/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/react-calls-components-and-hooks/#a-hex-address-appeared-on-the-page","title":"A hex address appeared on the page","text":"

You passed a component instead of calling it: html.div(None, my_component). Add the parentheses.

"},{"location":"reference/react-calls-components-and-hooks/#runtimeerror-no-life-cycle-hook-is-active-are-you-rendering-in-a-layout","title":"RuntimeError: No life cycle hook is active. Are you rendering in a layout?","text":"

A hook ran with no render in progress. Usual causes: called from an event handler, from an effect body, from a helper that a handler calls, or by awaiting .render() yourself. Read the server log for the traceback \u2014 the browser will not show it.

"},{"location":"reference/react-calls-components-and-hooks/#typeerror-uses-reserved-parameter-key","title":"TypeError: ... uses reserved parameter 'key'","text":"

A component parameter is named key. Rename it, and pass the reconciliation key as key= at the call site.

"},{"location":"reference/react-calls-components-and-hooks/#a-button-does-nothing-and-the-browser-shows-no-error","title":"A button does nothing, and the browser shows no error","text":"

The handler raised on the server. Handlers that call hooks fail this way. Check the server log.

"},{"location":"reference/react-calls-components-and-hooks/#related","title":"Related","text":"
  • Rules of React \u2014 the section overview
  • Components and Hooks must be pure \u2014 what these calling rules require of your code
  • Rules of Hooks \u2014 the ordering rule, and what breaking it does in ReactPy
  • Your First Component \u2014 writing your first component
  • Responding to Events \u2014 where handler code may and may not reach
"},{"location":"reference/reactpy-csr/","title":"ReactPyCsr","text":""},{"location":"reference/reactpy-csr/#overview","title":"Overview","text":"

ReactPyCsr serves a page that runs your component code in the browser via PyScript. There is no websocket: the component renders entirely client-side.

from reactpy.executors.asgi import ReactPyCsr\n\nmy_app = ReactPyCsr(\"root.py\", initial=\"Loading...\")\n
"},{"location":"reference/reactpy-csr/#reference","title":"Reference","text":""},{"location":"reference/reactpy-csr/#reactpycsrfile_paths-settings","title":"ReactPyCsr(*file_paths, **settings)","text":"
from reactpy.executors.asgi import ReactPyCsr\n
"},{"location":"reference/reactpy-csr/#parameters","title":"Parameters","text":"
  • *file_paths: One or more paths to Python files containing components. Their source is inlined into the served HTML. Accepts str or pathlib.Path.

    Read once, then cached for the life of the process: executors/pyscript/utils.py, fetch_cached_python_file is decorated with functools.cache, keyed on the path. Editing the file does not change the page on reload \u2014 restart the server. The same call minifies the source before inlining \u2014 # comments and inner docstrings are stripped, the module docstring is not \u2014 so what reaches the browser is not quite what you wrote (see Jinja).

  • root_name (optional): The name of the root component inside your Python file. Defaults to \"root\". The file may define any number of components; this one is mounted.

    ReactPyCsr(\"components.py\", root_name=\"my_app\")\n

    The name must match exactly, and the object must be a component.

  • initial (optional): Content shown while PyScript boots. A string or a VdomDict. Defaults to \"\".

  • extra_py (optional): Additional Python packages to install into the PyScript runtime, as version specifier strings.

  • extra_js (optional): Additional JavaScript modules, as a dict[str, str] where each key is the URL of the JavaScript file and the value is the name to export it as \u2014 {\"/static/foo.js\": \"foo\"}. The name is how the module is reached from the component, via pyscript.js_modules.foo. Same direction as the js_modules.main block PyScript itself consumes.

  • pyscript_config (optional): Raw PyScript configuration dictionary.

  • http_headers, html_head, prepend_body, html_lang: Same meaning as in ReactPy.

  • **settings: Any key from ReactPyConfig.

"},{"location":"reference/reactpy-csr/#returns","title":"Returns","text":"

An ASGI application.

"},{"location":"reference/reactpy-csr/#usage","title":"Usage","text":""},{"location":"reference/reactpy-csr/#files","title":"Files","text":"

Two files: the ASGI app, and the root component.

main.pyroot.py Run
my_app = ReactPyCsr(\n    Path(__file__).parent / \"root.py\",\n    initial=\"Loading...\",\n    root_name=\"root\",\n)\n
from reactpy import component, html, use_state\n\n\n@component\ndef root():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n

Serve with uvicorn main:my_app. The served page carries the Loading... placeholder, the PyScript bootstrap, and your component source.

Your component source is public

ReactPyCsr embeds the text of every file you pass into the HTML response, so the browser can execute it. Verified: requesting the index route returned the full body of the component file, including components that are not mounted.

Treat these files as client-side code. Do not read secrets, query a database, or call privileged APIs from a CSR component \u2014 anything reachable from that file is reachable by anyone who loads the page. CSR is for genuinely client-side work: offline apps, demos, UI that needs no server authority. For anything that touches data you do not publish, use ReactPy or ReactPyMiddleware, which keep your Python on the server.

"},{"location":"reference/reactpy-csr/#how-it-loads","title":"How it loads","text":"

The page ReactPy generates does three things:

  1. Links PyScript's CSS and module script from {path_prefix}static/.
  2. Declares what PyScript should fetch: a packages list for Python \u2014 ReactPy's own wheel from {path_prefix}static/wheels/ and jsonpointer==3.* \u2014 and a separate js_modules entry for morphdom, served from {path_prefix}static/morphdom/morphdom-esm.js. They are different lists with different installers; morphdom is not a packages entry.
  3. Inlines your Python, then runs it under PyScript.

Verified against a running server: the initial response carries the PyScript core.js, \"packages\": [\"/reactpy/static/wheels/reactpy-2.0.0b13-py3-none-any.whl\", \"jsonpointer==3.*\"], \"js_modules\": {\"main\": {\"/reactpy/static/morphdom/morphdom-esm.js\": \"morphdom\"}}, and the initial placeholder inside a <div class=\"pyscript\">. pyscript_config lets you replace either list.

The first load is slow

The browser downloads and installs a CPython runtime plus your packages before anything renders. That is what initial is for. pyscript_config and extra_py let you pin packages, and packages_cache controls re-download, but the cold start is inherent to running Python in the browser.

"},{"location":"reference/reactpy-csr/#choosing-csr-over-the-other-executors","title":"Choosing CSR over the other executors","text":"Where your Python runs Needs a websocket Source visible to the client ReactPy server yes no ReactPyMiddleware server yes no ReactPyCsr browser no yes

Pick CSR when the client must work without a server connection. Pick one of the others whenever the component touches anything you would not publish.

PyScript without CSR

ReactPy accepts pyscript_setup=True to load PyScript alongside server-rendered components, for mixed setups. ReactPyCsr is the fully client-side case.

"},{"location":"reference/reactpy-csr/#related","title":"Related","text":"
  • Creating a Standalone React App
  • Adding ReactPy to an Existing Project \u2014 the Jinja template tags include a CSR variant.
"},{"location":"reference/reactpy-middleware/","title":"ReactPyMiddleware","text":""},{"location":"reference/reactpy-middleware/#overview","title":"Overview","text":"

ReactPyMiddleware adds ReactPy routes to an ASGI application you already have. Requests ReactPy does not recognise fall through to your app.

from reactpy.executors.asgi import ReactPyMiddleware\n\nmy_app = ReactPyMiddleware(existing_asgi_app, root_components=[\"main.component\"])\n
"},{"location":"reference/reactpy-middleware/#reference","title":"Reference","text":""},{"location":"reference/reactpy-middleware/#reactpymiddlewareapp-root_components-settings","title":"ReactPyMiddleware(app, root_components, **settings)","text":"
from reactpy.executors.asgi import ReactPyMiddleware\n
"},{"location":"reference/reactpy-middleware/#parameters","title":"Parameters","text":"
  • app: The ASGI application to serve when a request does not match a ReactPy route. Any ASGI app works \u2014 Starlette, FastAPI, or hand-written.

  • root_components: A list, set, or tuple of dotted paths to root components, e.g. [\"main.counter\"]. The path must be importable by Python's import system: module path, dot, attribute name. Resolution is module.rsplit(\".\", 1) followed by getattr, so it must name a module that is importable from the server's working directory.

  • **settings: Any key from ReactPyConfig. See Configuration. path_prefix is validated at construction and raises ValueError if malformed.

"},{"location":"reference/reactpy-middleware/#returns","title":"Returns","text":"

An ASGI application.

"},{"location":"reference/reactpy-middleware/#usage","title":"Usage","text":""},{"location":"reference/reactpy-middleware/#wrapping-an-existing-app","title":"Wrapping an existing app","text":"main.py Run
my_app = ReactPyMiddleware(starlette_app, root_components=[\"main.counter\"])\n
# TODO\n

The wrapped app is untouched. Verified with Starlette: a plain /healthz route still returned its text response through the middleware.

"},{"location":"reference/reactpy-middleware/#what-the-middleware-actually-claims","title":"What the middleware actually claims","text":"

ReactPyMiddleware.__call__ handles exactly three kinds of request and forwards everything else:

Path Handled by websocket matching {path_prefix}{dotted_path}/ ReactPy render stream {path_prefix}static/... ReactPy client bundle {path_prefix}modules/... built web modules anything else your app

There is no fourth row. ReactPyMiddleware.match_extra_paths is a hook that returns None \u2014 its own comment says custom routes are \"unused by default to encourage users to handle routing within your ASGI framework of choice\". The @my_app.route(...) registry people expect belongs to the standalone ReactPy executor, which overrides that method to match extra_http_routes / extra_ws_routes. Under the middleware, every route is your app's.

The middleware never serves your HTML

This is the part that surprises people. Registering a dotted path does not create a page. Requesting \"/\" falls through to your app, and there is no {path_prefix}{dotted_path}/ HTTP route either \u2014 that pattern is matched only when the ASGI scope type is \"websocket\". Verified: requesting /reactpy/main.counter/ over HTTP returns 404, while the websocket on the same path streams the model.

You produce the document yourself, by emitting an empty mount element and the client bootstrap that points at one registered component.

"},{"location":"reference/reactpy-middleware/#serving-a-component-from-your-own-route","title":"Serving a component from your own route","text":"main.py Run
async def home(request):\n    # The mount point plus the client bootstrap, for one registered component.\n    mount = server_side_component_html(\n        element_id=\"app\",\n        class_=\"\",\n        # Trailing slash required: the websocket route matches `<prefix><dotted_path>/`.\n        component_path=\"main.counter/\",\n    )\n    return HTMLResponse(f'<html><body><div id=\"wrapper\">{mount}</div></body></html>')\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", home)])\n\nmy_app = ReactPyMiddleware(starlette_app, root_components=[\"main.counter\"])\n
# TODO\n

server_side_component_html emits the <div id=\"app\">, the import map fallback, and the mountReactPy(...) call. Verified in a real browser: the counter rendered into <div id=\"app\"> with [ReactPy] Connected! in the console.

The trailing slash in component_path is required

Pass \"main.counter/\", not \"main.counter\". The client opens ws://host/reactpy/<component_path>?path=/, and the server matches the websocket path against

^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$\n

which requires the trailing slash. Verified both ways: without it the handshake fails with Unexpected response code and the console logs [ReactPy] Failed to connect!, leaving an empty div; with it, the component renders.

"},{"location":"reference/reactpy-middleware/#multiple-components","title":"Multiple components","text":"

Register several and choose per page. Each page mounts exactly one:

my_app = ReactPyMiddleware(\n    starlette_app,\n    root_components=[\"main.counter\", \"main.settings\", \"main.profile\"],\n)\n

Then emit a different component_path from each of your routes.

Unregistered paths are rejected

\"Attempting to use an unregistered root component <path>.\" is raised when a websocket asks for a dotted path you never listed. Register every component you intend to mount.

"},{"location":"reference/reactpy-middleware/#import-errors","title":"Import errors","text":"

A dotted path that cannot be imported fails at construction, with a message naming the part that failed:

ImportError: ReactPy failed to import \"main\"\nAttributeError: ReactPy failed to import \"counter\" from \"main\"\n

The first means the module was not found; the second means the module loaded but has no such attribute. ValueError is raised earlier still if the path has no dot at all.

"},{"location":"reference/reactpy-middleware/#choosing-an-executor","title":"Choosing an executor","text":"Goal Use Whole app is ReactPy, no existing server code ReactPy Add components to an existing ASGI app ReactPyMiddleware Run component code in the browser via PyScript ReactPyCsr

ReactPy is itself a subclass of ReactPyMiddleware that registers a single root and serves its own index document, which is why the standalone executor can do the HTML the middleware leaves to you.

"},{"location":"reference/reactpy-middleware/#related","title":"Related","text":"
  • Adding ReactPy to an Existing Project
  • ReactPy \u2014 the standalone executor.
"},{"location":"reference/reactpy/","title":"ReactPy","text":""},{"location":"reference/reactpy/#overview","title":"Overview","text":"

ReactPy is ReactPy's standalone ASGI application. It serves the HTML document, the client bundle, and the websocket through which components render.

from reactpy.executors.asgi import ReactPy\n\nmy_app = ReactPy(root_component)\n
"},{"location":"reference/reactpy/#reference","title":"Reference","text":""},{"location":"reference/reactpy/#reactpyroot_component-settings","title":"ReactPy(root_component, **settings)","text":"

Instantiate ReactPy once, at module level, and hand the instance to any ASGI server:

uvicorn main:my_app\n

ReactPy serves the page at every route that is not claimed by its own static or websocket paths.

from reactpy.executors.asgi import ReactPy\n
"},{"location":"reference/reactpy/#parameters","title":"Parameters","text":"
  • root_component: The component to mount. A component constructor \u2014 the @component function itself, not a call to it. This app is a single-page application: there is exactly one root.

  • html_head (optional): Extra content for the document's <head>.

    Must be built with html.head

    Pass html.head(...) \u2014 not a bare html.title(...) or a html.div(...). Anything else raises at request time:

    ValueError: Head element must be constructed with `html.head`.\n

    Verified: this surfaces as a 500 on the index route, not at import time, so a typo here only shows up once the server is running.

  • prepend_body (optional): Content rendered at the start of the <body>, before the mount point. Accepts a VdomDict built with html.*, or None to omit. Defaults to html.noscript(\"Enable JavaScript to view this site.\").

    This is the one part of the tree that is genuinely server-rendered. See what ReactPy does and does not render.

  • html_lang (optional): Value for the document's lang attribute. Defaults to \"en\".

  • http_headers (optional): Extra response headers for the index document \u2014 a plain dict[str, str]. Use it for CSP, X-Frame-Options, caching, and similar.

  • pyscript_setup (optional): Load PyScript into the document head, for running component code in the browser. Defaults to False. ReactPyCsr sets this for you.

  • pyscript_options (optional): A PyScriptOptions configuring that behaviour.

  • **settings: Any key from ReactPyConfig. Despite appearances these are not scoped to this app instance \u2014 each one is written to the matching global in reactpy.config, so the last executor constructed wins process-wide. See Configuration.

"},{"location":"reference/reactpy/#returns","title":"Returns","text":"

An ASGI application.

"},{"location":"reference/reactpy/#usage","title":"Usage","text":""},{"location":"reference/reactpy/#a-standalone-app","title":"A standalone app","text":"app.py Run
my_app = ReactPy(counter)\n
# TODO\n

Serve it with uvicorn main:my_app. The name you assign matters, because it is what uvicorn module:name refers to.

"},{"location":"reference/reactpy/#page-metadata-and-headers","title":"Page metadata and headers","text":"app.py Run
my_app = ReactPy(\n    page,\n    # Must be built with html.head(); any other tag raises ValueError.\n    html_head=html.head(\n        html.title(\"My ReactPy App\"),\n        html.meta({\"name\": \"description\", \"content\": \"A ReactPy app\"}),\n    ),\n    html_lang=\"fr\",\n    http_headers={\"X-Frame-Options\": \"DENY\"},\n    # Rendered into the <body> before the mount point, on the server.\n    prepend_body=html.header({\"id\": \"nav\"}, \"Server-rendered navigation\"),\n)\n
# TODO\n

Verified against a running server: lang=\"fr\", the custom <title>, the custom response header, and the prepend_body markup all appear in the initial HTML.

"},{"location":"reference/reactpy/#configuration","title":"Configuration","text":"

Settings are ordinary keyword arguments, and they reach the browser-side client:

app.py Run
my_app = ReactPy(\n    page,\n    path_prefix=\"/apps/reactpy/\",\n    reconnect_interval=500,\n    reconnect_max_interval=30000,\n    reconnect_max_retries=50,\n    async_rendering=True,\n    debug=False,\n)\n
# TODO\n
Setting Default Meaning path_prefix /reactpy/ Base path ReactPy serves its own static assets and websocket from. web_modules_dir temp dir Where built web modules are cached. reconnect_interval 750 Milliseconds before the first reconnect attempt. reconnect_max_interval 60000 Ceiling on the reconnect backoff. reconnect_max_retries 150 Attempts before giving up. reconnect_backoff_multiplier 1.25 Growth factor between attempts. async_rendering True Allow renders to overlap. debug False Server-side checks and richer error text \u2014 see the note below. max_queue_size 1000 Bound on the pending events of each handler target \u2014 the layout keeps a queue per target (core/layout.py, _event_queues), not one per connection. event_proxy_timeout 5.0 Seconds to wait for the browser to answer one live event read before EventProxyTimeout. event_proxy_max_depth 10 How deep one resolution may expand nested objects before they come back as references. event_proxy_max_timeouts 3 Consecutive unanswered round trips tolerated before live access is switched off for the connection. tests_default_timeout 15.0 Default timeout for reactpy.testing. Not settable this way \u2014 see the note below.

Verified: setting path_prefix=\"/apps/reactpy/\" moves asset serving to that prefix, and the reconnect values appear in the generated mount call. The three event_proxy_* settings were verified by constructing an executor with them and reading the matching reactpy.config options back.

They belong to a handler which reads the browser rather than to the client bootstrap: they bound the round trips behind await event.target.value. Reach for them only when a handler is timing out or a deeply nested read comes back as references \u2014 see Protocol Structure.

These keywords are process-wide, not per-app

executors/utils.py, process_settings walks the keywords and calls set_current() on the matching reactpy.config Option object. Those objects are module-level globals, so the last constructor to run wins for the whole process \u2014 including for any app built before it. Verified: two ReactPy instances created with reconnect_interval=111 and reconnect_interval=999 leave REACTPY_RECONNECT_INTERVAL.current at 999 for both. There is no per-app configuration layer; do not host two apps that need different values.

tests_default_timeout is worse than ineffective. Its Option is declared mutable=False in reactpy/config.py, and _option.py, set_current raises TypeError on an immutable option \u2014 so passing it raises Option(REACTPY_TESTS_DEFAULT_TIMEOUT=15.0) cannot be modified after initial load out of the constructor. Set it via the REACTPY_TESTS_DEFAULT_TIMEOUT environment variable before import, or not at all.

debug is not a client switch. REACTPY_DEBUG turns on server-side VDOM and JSON-attribute validation, populates the model's error text (without it, a failed component ships \"error\": \"\" and renders as nothing), and changes a few asset URLs to their ?dev forms. See Common Props.

Each setting also has an environment-variable twin in reactpy.config \u2014 REACTPY_PATH_PREFIX, REACTPY_RECONNECT_INTERVAL, and so on. Since the keyword already writes to that same global option, the two differ only in timing: the environment variable is read when the module is first imported, the keyword whenever an executor is constructed.

"},{"location":"reference/reactpy/#what-reactpy-does-and-does-not-render","title":"What ReactPy does and does not render","text":"

This is the distinction that trips people up, because the executor has historically been described as \"SSR\".

The initial HTML document contains an empty mount point:

<body>\n  <noscript>Enable JavaScript to view this site.</noscript>\n  <div id=\"app\" class=\"\"></div>\n  <script type=\"module\" crossorigin=\"anonymous\">\n    import { mountReactPy } from \"/reactpy/static/index.js\";\n    mountReactPy({ mountElement: document.getElementById(\"app\"), ... });\n  </script>\n</body>\n

Verified by fetching the index route: your root component's markup is not in the response. The browser connects over the websocket and ReactPy renders and streams from there.

What is server-rendered into the first response:

  • prepend_body, converted with reactpy_to_string.
  • html_head.
  • The document shell: lang, static asset paths, the mount script.

Practical consequences:

  • Search engines and curl see an empty app. For content that must exist without JavaScript, render it yourself \u2014 reactpy_to_string turns a component into an HTML string, which you can place in prepend_body or emit from your own route. It renders the component's first render directly, without a layout, so the component cannot use any hook: use_state, use_ref, use_effect, use_location and the rest all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout?. Pass plain html.* elements, or a component that calls none of them.
  • There is no first-paint flash to optimise away, because there is no first paint to replace.
  • A component that fails on the server fails after the page loads, leaving a blank <div id=\"app\">. When the page loads but shows nothing, check the server log before the browser console.

Not the same as ReactPyCsr

ReactPyCsr is the one that runs component code in the browser via PyScript. ReactPy runs components on the server and sends VDOM over a websocket. Both need a server; they differ in where your Python executes. See ReactPyMiddleware for mounting into an existing ASGI app.

"},{"location":"reference/reactpy/#related","title":"Related","text":"
  • Creating a Standalone React App \u2014 the tutorial version of this page.
  • ReactPyMiddleware \u2014 mount components inside an existing ASGI application.
  • ReactPyCsr \u2014 run component code in the browser.
"},{"location":"reference/rules-of-hooks/","title":"Rules of Hooks","text":""},{"location":"reference/rules-of-hooks/#overview","title":"Overview","text":"

Call hooks at the top level of a component, in the same order, on every render. No loops, no conditions, no early returns, no nesting inside a function that runs later. ReactPy will not always tell you when you break this rule \u2014 in one case it corrupts state in silence.

@component\ndef form():\n    name, set_name = use_state(\"\")  # top level, every render, same order\n    email, set_email = use_state(\"\")\n    return html.form(name, email)\n

ReactPy identifies a hook's state by where it was called, not by what it is called into. The nth use_state in a component body gets the nth stored slot. Change which hooks run, and you change which slot each one reads.

"},{"location":"reference/rules-of-hooks/#the-rules","title":"The rules","text":"
  • Only call hooks from a component body or another hook \u2014 never from an event handler, an effect body, a plain function, or module level. See React calls Components and Hooks.
  • Call them at the top level \u2014 not inside if, for, try, a while, a comprehension, or a nested function that runs later.
  • Always call the same hooks in the same order \u2014 which hooks run may not depend on props, state, or anything else that can change between renders.
  • No early return before a hook \u2014 an early exit skips the hooks after it.

The one exception is use_context: it takes no slot at all (core/hooks.py reads the provider off the current hook and never touches the state list), so calling it conditionally shifts nothing \u2014 verified, a component whose use_context call runs on only some renders keeps _state at the same length throughout. It is still a hook and still needs an active render to call.

"},{"location":"reference/rules-of-hooks/#why-hooks-are-slots-not-names","title":"Why: hooks are slots, not names","text":"

ReactPy keeps a component's hook values in a plain list and hands out the next entry on each call \u2014 a running index, not a name. From reactpy/core/_life_cycle_hook.py, LifeCycleHook.use_state:

if not self._rendered_atleast_once:\n    # since we're not initialized yet we're just appending state\n    result = function()\n    self._state.append(result)\nelse:\n    # once finalized we iterate over each succesively used piece of state\n    result = self._state[self._current_state_index]\nself._current_state_index += 1\n

The first render appends one entry per hook call, in order. Every later render reads back by position. Nothing records the variable name, the hook's type, or what it stores \u2014 so position is identity.

That is why the rules are phrased as \"same order\". The component cannot tell ReactPy which value is which; only the sequence of calls says so.

"},{"location":"reference/rules-of-hooks/#what-breaking-the-rules-actually-does","title":"What breaking the rules actually does","text":"

This is where ReactPy and React part ways, and why the rules must be enforced by review rather than trusted to a loud failure.

"},{"location":"reference/rules-of-hooks/#more-hooks-than-the-first-render-a-crash","title":"More hooks than the first render: a crash","text":"

A component that mounts with one hook and later calls two tries to read a slot that was never appended:

@component\ndef grow():\n    n, set_n = use_state(0)\n\n    async def bump(event):\n        set_n(n + 1)\n\n    if n >= 1:  # a second hook appears only after the first click\n        extra, set_extra = use_state(\"EXTRA\")\n        return html.div(f\"n={n} extra={extra}\")\n    return html.div(f\"n={n}\", html.button({\"onClick\": bump}, \"bump\"))\n

With debug mode on, the component is replaced by an error node and the server logs the cause:

IndexError: list index out of range\n

With debug mode off, the error field is an empty string, so the region simply renders as nothing. This case fails loudly enough to find.

"},{"location":"reference/rules-of-hooks/#fewer-hooks-than-the-first-render-silent-state-corruption","title":"Fewer hooks than the first render: silent state corruption","text":"

This is the dangerous one. A hook call that disappears from above another one shifts every later index by one \u2014 and the shift is invisible:

@component\ndef shifting():\n    show, set_show = use_state(True)  # slot 0\n    if show:\n        hidden, set_hidden = use_state(\"HIDDEN\")  # slot 1, only while show\n    n, set_n = use_state(\"N\")  # slot 1 while show, slot 2 once it is off\n    return html.div(f\"show={show} n={n}\")\n

Measured, flipping show from True to False once (through its setter \u2014 this snippet has no button; the runnable broken_toggle below wires the same shape to a real Toggle button):

mount:          show=True  n=N\nafter toggle:   show=False  n=HIDDEN     <-- `n` is now reading `hidden`'s slot\n

No error, no warning, no traceback. n starts showing another hook's value and keeps doing so until the branches line up again. React, by contrast, throws Rendered fewer hooks than expected. A ported component can therefore be wrong in ReactPy where it was a crash in React.

The corruption usually surfaces later, somewhere that looks unrelated. With a counter that should hold an int, the shifted read hands a string to arithmetic, and the next click dies in the handler:

TypeError: can only concatenate str (not \"int\") to str\n

That traceback names the event handler, not the hook \u2014 so the hunt starts in the wrong file. The button stops working, and the error is on the server log only.

app.py Run
@component\ndef broken_toggle():\n    \"\"\"Do not copy this: a hook behind a condition that can change.\n\n    `hidden` occupies slot 1 only while `show` is true. Clicking Toggle makes\n    that call disappear, so every hook after it shifts up one slot and starts\n    reading somebody else's value -- `count` begins showing the string that\n    belongs to `hidden`. No error is raised, on the server or in the browser.\n    \"\"\"\n    show, set_show = use_state(True)  # slot 0\n\n    if show:\n        hidden, set_hidden = use_state(\"NOT-A-NUMBER\")  # slot 1, conditionally\n\n    count, set_count = use_state(0)  # slot 1 when `show` is False\n\n    async def handle_show_click(event):\n        set_show(not show)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.p(f\"show={show}\"),\n        html.button({\"onClick\": handle_show_click}, \"Toggle\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n\n\n@component\ndef fixed_toggle():\n    \"\"\"The same component with the hook order held constant.\n\n    Every hook runs on every render, so the slots stay aligned with the\n    variables they belong to. Branching happens in the *return value*, which is\n    what rendering is for.\n    \"\"\"\n    show, set_show = use_state(True)\n    hidden, set_hidden = use_state(\"NOT-A-NUMBER\")\n    count, set_count = use_state(0)\n\n    async def handle_show_click(event):\n        set_show(not show)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.p(f\"show={show} hidden={hidden}\"),\n        html.button({\"onClick\": handle_show_click}, \"Toggle\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n

In broken_toggle, Add 1 works, then Toggle makes the heading read NOT-A-NUMBER, and the next Add 1 does nothing at all \u2014 the handler raised TypeError on the server. fixed_toggle is the same component with the hooks moved out of the condition: both of its buttons keep working through every toggle.

"},{"location":"reference/rules-of-hooks/#a-hook-in-a-loop-frozen-values-then-a-crash","title":"A hook in a loop: frozen values, then a crash","text":"
@component\ndef rows(n):\n    out = []\n    for i in range(n):\n        value, set_value = use_state(f\"v{i}\")\n        out.append(value)\n    return html.div(out)\n

Measured with n going 3 \u2192 2 \u2192 1 \u2192 0:

vals=['v0', 'v1', 'v2']\nvals=['v0', 'v1']\nvals=['v0']\nvals=[]\n

It looks correct. It is not: use_state(f\"v{i}\") applies its initial value only on the first render, so the i in it is decoration, and the list's length is driven by an argument while its contents come from frozen slots. Growing n past its first-render value raises the IndexError from the first case. A loop of hooks is wrong either way \u2014 build one component per item, which gives each its own state legitimately:

html.div(None, [row(item, key=item[\"id\"]) for item in items])\n
"},{"location":"reference/rules-of-hooks/#an-early-return-before-a-hook","title":"An early return before a hook","text":"
@component\ndef loading():\n    n, set_n = use_state(0)\n    if n == 2:\n        return html.div(\"early\")  # skips the hook below\n    label, set_label = use_state(\"LABEL\")\n    return html.div(f\"{n} {label}\")\n

ReactPy does not crash, and label survives while the branch is away. But this is the same slot-shifting hazard as above, arriving whenever the condition flips. Put the early return after every hook, or move the conditional content into a child component:

@component\ndef loading():\n    n, set_n = use_state(0)\n    label, set_label = use_state(\"LABEL\")  # all hooks first\n    if n == 2:\n        return html.div(\"early\")\n    return html.div(f\"{n} {label}\")\n

Tolerance is not safety

ReactPy lets several of these through; React does not. The case above corrupts state without a word, and the same code raises the moment it is ported, tested against React semantics, or read by someone who assumes React's guarantees. Treat the rules as absolute.

"},{"location":"reference/rules-of-hooks/#custom-hooks-follow-the-same-rules","title":"Custom hooks follow the same rules","text":"

A custom hook is only a function, so nothing checks it:

def use_auth(token):\n    if not token:\n        return None, None  # hooks skipped on some calls\n    user, set_user = use_state(None)\n    return user, set_user\n

Call it with a falsy token and the caller silently loses a slot \u2014 measured, the happy path consumes one hook slot and the falsy path consumes none. Return early only after every hook has run, or split into two hooks and choose between them at the call site, where the choice is visible in the component body:

@component\ndef page(token):\n    return authed(token) if token else anonymous()\n

Because a hook's name is all a reviewer has, name custom hooks use_* and call them only from component bodies or other hooks.

"},{"location":"reference/rules-of-hooks/#catching-violations","title":"Catching violations","text":"

There is a linter: reactpy-flake8, the plugin this repository pins (reactpy-flake8 >=0.7 in pyproject.toml, select = [\"RPY\"]). Its RulesOfHooksVisitor is the working equivalent of eslint-plugin-react-hooks for the call-site rule, and it reports:

Code Catches REACTPY100 a hook defined as a closure inside another function REACTPY101 a hook used outside any component or hook REACTPY102 a hook used inside an if / if expression / try / match / for / while REACTPY103 a hook used after an early return

Run it as part of flake8 (the repo config selects only its RPY codes), or drive it directly:

import ast\nfrom reactpy_flake8.run import run_checks\n\nfor line, col, msg in run_checks(ast.parse(source), exhaustive_hook_deps=False):\n    print(line, col, msg)\n

Verified against a component with a hook after an early return and a hook inside an if: it reports exactly REACTPY103 hook 'use_state' used after an early return on line 7 and REACTPY102 hook 'use_state' used inside if statement. Pass exhaustive_hook_deps=True to add the dependency-shape checks (REACTPY200\u2013REACTPY202), which cover far less than people expect \u2014 see Lifecycle of Reactive Effects.

What the linter cannot do is check the order rule, because order is a runtime property. There is still no runtime check, so the two habits remain:

  • Run with REACTPY_DEBUG=1 during development, so a hook-count increase becomes a visible error instead of an empty region.
  • Treat any use_ call that appears below the first non-hook statement of a component body, or inside an if / for / try / nested def, as a review defect \u2014 and wire reactpy-flake8 into CI so you are not the only one who spots it.

The linter also matches on names: it flags anything whose name fits ^_*use_\\w+ and treats a function as a component only if decorated with something matching ^(component|[\\w.]+\\.component)$. Rename the decorator or the hook and the checks silently stop applying; the patterns are configurable via --component-decorator-pattern and --hook-function-pattern.

"},{"location":"reference/rules-of-hooks/#reference","title":"Reference","text":"Pattern ReactPy behaviour Verdict Hook at component top level Works Do this Hook in a custom hook called from a body Works Do this Hook in an if that never changes Works by accident Broken Hook in an if that can change Fewer hooks: silent state corruption. More hooks: IndexError Broken Hook in a for Frozen initial values; IndexError if the loop grows Broken Hook after an early return Slot shift when the branch flips Broken Hook in an event handler RuntimeError, server log only Broken Hook in an effect body RuntimeError, server log only Broken Hook at module level RuntimeError: No life cycle hook is active. Broken"},{"location":"reference/rules-of-hooks/#related","title":"Related","text":"
  • Rules of React \u2014 the section overview
  • Components and Hooks must be pure \u2014 the other half of the contract
  • React calls Components and Hooks \u2014 who runs a hook, and when
  • Use State, Use Effect, Use Memo \u2014 the hooks these rules govern
  • Use Callback, Use Debug Value \u2014 hooks worth writing your own wrappers around
"},{"location":"reference/rules-of-react/","title":"Overview","text":""},{"location":"reference/rules-of-react/#overview","title":"Overview","text":"

Two rules make ReactPy predictable: components and hooks must be pure, and ReactPy must be the one that calls them. They look like style advice. They are load-bearing \u2014 and in ReactPy, one of them is not enforced by anything except you.

Rule In one line Read Purity A component or hook returns the same output for the same inputs, and changes nothing outside itself. Components and Hooks must be pure Who calls what You write components and hooks; ReactPy calls them \u2014 always from the top level of a component, in the same order. React calls Components and Hooks, Rules of Hooks

The two are one idea seen from either side. ReactPy is free to call your component whenever it likes, as often as it likes, because purity makes that safe. In return, your component gets to assume nothing about when it ran \u2014 which is why state lives in hooks rather than in variables.

"},{"location":"reference/rules-of-react/#why-the-rules-exist","title":"Why the rules exist","text":"

ReactPy renders on a server, inside one Python process that serves many connections, and re-renders a component whenever its state changes or its parent renders. Both facts are what the rules protect against.

  • Impure rendering leaks between users. A module-level dict written during a render is shared by every connection in the process. Three visitors, one cart. No error, and each page looks fine. Measured in Components and Hooks must be pure.
  • Render frequency is not yours to predict. Code that counts, accumulates, or logs in a component body runs more often than the user acts. A child re-renders every time its parent does \u2014 ReactPy has no memo bailout for components.
  • Hook state is positional. ReactPy hands out hook values from a list by index, so changing which hooks run changes which value each hook reads. The usual outcome is not an error but a component quietly reading its neighbour's state. See Rules of Hooks.

The last point is the one that surprises people coming from React. React's hook dispatcher validates call order and throws; ReactPy's does not. Several violations are tolerated silently, and one of them corrupts state with nothing logged anywhere. The rules are still exactly React's \u2014 the safety net underneath them is not.

"},{"location":"reference/rules-of-react/#the-rules-as-a-checklist","title":"The rules, as a checklist","text":"

For a component body:

  • reads only its arguments, and values from its own hooks
  • assigns no global or module-level name
  • mutates no object it did not create during this render
  • reads no clock, random, environment, or I/O
  • starts no task, thread, or timer
  • may freely define event handlers \u2014 they run later, and may do all of the above

For a hook call site:

  • called from a component body or another hook \u2014 never an event handler, effect body, or plain function
  • at the top level of that body: not under if, for, try, and not inside a nested def that runs outside the render (an event handler, an effect callback). A nested def whose name starts with use_ and which the body calls during the render is a custom hook, and calling hooks in it is the point.
  • the same hooks, in the same order, on every render
  • no early return before a hook

use_context is the one hook that takes no slot, so it alone may be called on only some renders without shifting the others. See Rules of Hooks.

For a custom hook (use_*): everything above, plus it must not skip its own hooks on some calls.

"},{"location":"reference/rules-of-react/#what-reactpy-cannot-check-for-you","title":"What ReactPy cannot check for you","text":"

reactpy-flake8 covers the call-site rule at build time (see Rules of Hooks), but there is no runtime assertion for hook order, and that is the one that bites silently. Two settings get you most of the way:

  • REACTPY_DEBUG=1 turns a render failure into a visible error in the model instead of a blank region, and enables the VDOM checks described in Common Props.
  • Tests are cheap precisely because of the rules: a pure component is an ordinary function \u2014 call it inside reactpy.core.layout.Layout, render it twice, assert the models are equal. No browser involved.

Everything else is review. The greppable version: any use_ call that appears below the first non-hook statement of a component body, or inside a conditional, loop, or nested function, is a defect until proven otherwise.

"},{"location":"reference/rules-of-react/#at-a-glance","title":"At a glance","text":"You might be tempted to Instead Cache a computation in a module-level dict use_memo Keep a per-user counter in a global use_state or use_reducer Fetch data in the component body An event handler, or use_async_effect Fetch request headers with os.environ use_scope / use_location Call a hook from an event handler Call it in the body and close over the value Skip a hook when a prop is falsy Return early only after all hooks, or split into two components Loop over use_state One component per item, keyed Reach for use_effect to derive a value Compute it in the body"},{"location":"reference/rules-of-react/#related","title":"Related","text":"
  • Components and Hooks must be pure \u2014 the contract, the server-shared-state leak, where effects belong
  • React calls Components and Hooks \u2014 constructors versus calls, reserved key, where hooks may run
  • Rules of Hooks \u2014 slots, and the measured cost of breaking order
  • Keeping Components Pure \u2014 the tutorial version, with exercises
  • Your First Component \u2014 start here if you have not written one yet
"},{"location":"reference/usage/","title":"Usage","text":""},{"location":"reference/usage/#overview","title":"Overview","text":"

reactpy.html is how you write markup in ReactPy. It is an object whose attributes are tag constructors: html.div(...) builds a <div>, and there is no template language, no JSX, and no parser involved.

from reactpy import html\n\nhtml.div({\"class\": \"card\"}, html.h2(\"Title\"), html.p(\"Body\"))\n

That call produces a plain dict:

{\n    \"children\": [\n        {\"children\": [\"Title\"], \"tagName\": \"h2\"},\n        {\"children\": [\"Body\"], \"tagName\": \"p\"},\n    ],\n    \"attributes\": {\"class\": \"card\"},\n    \"tagName\": \"div\",\n}\n

reactpy.h is the same object under a shorter name \u2014 h is html is True \u2014 so use whichever reads better in a given file. Importing both and mixing them in one module buys nothing.

"},{"location":"reference/usage/#reference","title":"Reference","text":""},{"location":"reference/usage/#html-tag","title":"html.<tag_name>(attributes, *children)","text":"

Every attribute of html is a tag constructor. The common tags are declared for autocomplete, but any name works \u2014 see Any name is a tag.

  • First argument: the attributes \u2014 but only when it is a plain dict. A VdomDict, or any dict subclass, is treated as a child instead. This single rule is the most common source of confusion, and it is worth internalizing:

    html.div({\"class\": \"card\"}, html.p(\"Body\"))  # dict -> attributes\nhtml.div(html.p(\"Body\"))  # no dict -> no attributes\nhtml.div(None, html.p(\"Body\"))  # None -> an explicit \"no attributes\"\n

    Leading None is the conventional way to say \"no attributes\" and keep the arguments in a predictable shape. It renders nothing.

    A VdomDict as the first argument is a child

    VdomDict is a dict subclass, and the check is for the exact plain dict type. So a VdomDict is never mistaken for attributes \u2014 even when you meant it that way:

    from reactpy.types import VdomDict\n\nhtml.div(VdomDict(tagName=\"span\"))  # -> a <span> *child* of the div\n
  • Remaining arguments: children, of any type Common Props accepts \u2014 strings, numbers, None, lists, dicts, components. Lists are flattened.

  • Returns: a VdomDict. Tag constructors are cached, so html.div is html.div \u2014 the same object every time. Nothing is rendered until a component returns it.

"},{"location":"reference/usage/#caveats","title":"Caveats","text":"
  • A plain dict that is meant as an element can be read as attributes. html.div({\"children\": [\"x\"]}) produces a div with an attribute literally named children, not a child \u2014 because the first argument was a plain dict. When in doubt, pass the element as the second argument after None.
  • Void tags reject children. html.br(\"x\") raises TypeError: 'br' nodes cannot have children. See Common Props.
  • Tags do not validate their own attributes. html.img({\"scr\": \"typo.png\"}) is accepted and renders an image with no source. There is no prop-type check; a misspelled attribute name is a silent no-op.
"},{"location":"reference/usage/#fragments","title":"html(*children) \u2014 fragments","text":"

Calling html itself is the fragment constructor: children with no wrapper element. See Common Props \u2014 a fragment accepts key and nothing else.

"},{"location":"reference/usage/#usage","title":"Usage","text":""},{"location":"reference/usage/#any-name-is-a-tag","title":"Any name is a tag","text":"

ReactPy does not keep a list of valid HTML tags. It converts the attribute name and sends it as the tag name, so you can reach tags the module has never declared:

You write You get html.div <div> html.data_table <data-table> html.my_widget <my-widget> html.del_ <del> html.class_ <class>

Two transformations: a trailing underscore is stripped (so you can name tags that are Python keywords, like del and class), and remaining underscores become dashes (so you can name custom elements and hyphenated tags that are not valid Python identifiers).

That makes custom elements first-class \u2014 no registration step, no wrapper component:

app.py Run
# Any name is a tag: underscores become dashes, so this is <user-badge>.\nuser_badge = html.user_badge\n\n\n@component\ndef profile(name, role):\n    return html.section(\n        {\"class\": \"profile\", \"data-role\": role},\n        html.h2(name),\n        # A plain string is a text child; an attribute dict is always first.\n        html.p({\"class\": \"muted\"}, \"Signed in as \", html.strong(name)),\n        user_badge({\"role\": role, \"label\": role.title()}),\n        html.footer(\n            html.small(\n                {\"style\": {\"font-size\": \"12px\", \"color\": \"#666\"}},\n                \"Last seen today\",\n            )\n        ),\n    )\n\n\n@component\ndef app():\n    return html.main(\n        None,\n        html.h1(\"Accounts\"),\n        profile(\"Ada\", \"admin\"),\n        profile(\"Linus\", \"viewer\"),\n    )\n
# TODO\n

The <user-badge> element reaches the browser as written. Whether it does anything is up to whatever defines it \u2014 a web component, an external script, or nothing at all. A tag the browser does not know renders as an unknown element: its children show up, its attributes are set, and it is laid out inline by default.

Prefer a component over a mystery tag

A custom tag is only worth it when something on the client implements it. For grouping your own markup, write a @component \u2014 it takes part in rendering, can hold state, and is checkable by a type checker.

"},{"location":"reference/usage/#tags-versus-components","title":"Tags versus components","text":"

Both are called the same way and both produce VDOM, and the difference is what happens next:

html.div(...) my_component(...) Returns A VdomDict, immediately A Component, rendered later by the layout Owns state No Yes \u2014 hooks belong to it Re-rendered Only when its parent returns a new value When its state changes or its parent re-renders it Defined with Provided by html @component over a function

The practical rule: a tag is for HTML elements, a component is for anything with behaviour. A <div> that has to remember something is not a div any more \u2014 it is a component that returns a div. See Your First Component.

"},{"location":"reference/usage/#nested-and-adjacent-tags","title":"Nested and adjacent tags","text":"

Tags nest by call nesting, and read left-to-right, innermost first:

html.p(html.em(\"emphasized\"), \" and \", html.strong(\"bold\"))\n

Because Python evaluates arguments eagerly, the inner call completes before the outer one runs. There is no indentation-scope or closing-tag syntax to get wrong, and no unbalanced-tag error \u2014 the structure either parses or it does not.

The one habit to build is the leading None. Without it, a first child that is a plain dict is consumed as attributes:

html.div({\"tagName\": \"span\"})  # attributes named \"tagName\" \u2014 not a child\nhtml.div(None, {\"tagName\": \"span\"})  # a <span> child\n

A complete page reads like the HTML it produces:

app.py Run
@component\ndef article_card():\n    # Children are positional. The first argument is attributes only if it is a\n    # plain dict, which is why a leading `None` is needed when there are no\n    # attributes but the first child could look like one.\n    return html.article(\n        {\"class\": \"card\"},\n        html.h2(\"Naming rules\"),\n        html.p(\n            html.em(\"html.del_\"),\n            \" is \",\n            html.code(\"<del>\"),\n            \", \",\n            html.code(\"html.data_table\"),\n            \" is \",\n            html.code(\"<data-table>\"),\n            \".\",\n        ),\n        html.ul(\n            None,\n            [html.li({\"key\": word}, word) for word in (\"class\", \"style\", \"key\")],\n        ),\n        html.hr(),\n        html.p({\"class\": \"muted\"}, \"Void tags take no children.\"),\n    )\n\n\n@component\ndef app():\n    return html.main(None, article_card(), html.br(), html.p(\"End of page.\"))\n
# TODO\n
"},{"location":"reference/usage/#lists-of-tags","title":"Lists of tags","text":"

A list comprehension produces children directly; give each one a key:

html.ul(None, [html.li({\"key\": word}, word) for word in words])\n

See Rendering Lists for why the key matters, and Common Props for where it lives in the dictionary.

"},{"location":"reference/usage/#classes-and-inline-styles","title":"Classes and inline styles","text":"

class and className both work, and style takes a dict. Include units inside the strings \u2014 a bare number is applied by some executors and silently dropped by others:

html.p({\"class\": \"muted\"}, \"...\")\nhtml.p({\"style\": {\"font-size\": \"12px\", \"color\": \"#666\"}}, \"...\")\n

Details, including the executor difference, are in Common Props.

"},{"location":"reference/usage/#svg","title":"SVG","text":"

SVG elements live under html.svg, because their tag names collide with HTML ones (<title>, <a>, <style>):

html.svg(\n    {\"viewBox\": \"0 0 24 24\", \"width\": \"24\"},\n    html.svg.circle({\"cx\": \"12\", \"cy\": \"12\", \"r\": \"10\"}),\n)\n

html.svg is itself a tag constructor, and nested names follow the same rules \u2014 trailing underscore stripped, underscores to dashes. Camel-cased SVG names are used as written, so html.svg.clipPath produces <clipPath>.

Names are not case-normalized

viewBox works because the name is forwarded exactly. Write SVG attribute names the way the spec spells them, capitals included.

"},{"location":"reference/usage/#raw-html","title":"Raw HTML","text":"

Two different jobs get confused here. To put a raw HTML string into the page as-is, without ReactPy parsing it, use dangerouslySetInnerHTML:

html.div({\"dangerouslySetInnerHTML\": {\"__html\": \"<b>bold</b>\"}})\n

To hand ReactPy a block of HTML and have it parsed into VDOM \u2014 real elements you can then inspect and transform \u2014 use string_to_reactpy() instead, which is exported at the top of the package:

from reactpy import string_to_reactpy\n\nstring_to_reactpy(\"<b>bold</b>\")\n# {'children': ['bold'], 'tagName': 'b'}\n

See Convert Between VDOM and HTML for the parse direction in full, including the *transforms argument.

Never pass untrusted input to either one: dangerouslySetInnerHTML is inserted without sanitizing, and string_to_reactpy() parses whatever you give it. Note also that a dangerouslySetInnerHTML attribute suppresses children rather than raising \u2014 nothing checks the combination, the raw HTML simply wins and the children are dropped. See Common Props.

"},{"location":"reference/usage/#inspecting-what-a-tag-produced","title":"Inspecting what a tag produced","text":"

A tag call is a plain dict, so print it:

>>> from reactpy import html\n>>> html.li({\"key\": \"a\"}, \"Item\")\n{'children': ['Item'], 'attributes': {'key': 'a'}, 'tagName': 'li'}\n

Key order can differ, and empty keys are omitted \u2014 a tag with no attributes has no \"attributes\" entry at all. To see what a component renders, drive it through reactpy.core.layout.Layout rather than printing the component instance, which shows only its arguments.

"},{"location":"reference/usage/#script-tags","title":"Script tags","text":"

html.script is special-cased. It rejects event handlers:

html.script({\"onLoad\": handler})\n
ValueError: 'script' elements do not support event handlers\n

and it infers its own key from its content, falling back to src, so the script is not re-run on every render:

>>> html.script({\"src\": \"/analytics.js\"})\n{'tagName': 'script', 'attributes': {'src': '/analytics.js', 'key': '/analytics.js'}}\n

Because the key drives re-execution, giving a script an explicit key is how you make it run again on purpose. As with dangerouslySetInnerHTML, keep untrusted data out of the body of a script.

"},{"location":"reference/usage/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/usage/#the-first-argument-became-an-attribute","title":"The first argument became an attribute","text":"

If your element has an attribute named children or tagName and renders nothing, a plain dict child was read as the attributes dict. Pass None first.

"},{"location":"reference/usage/#typeerror-tag-nodes-cannot-have-children","title":"TypeError: '<tag>' nodes cannot have children.","text":"

You passed content to a void tag. Remove the children.

"},{"location":"reference/usage/#typeerror-fragments-cannot-have-attributes-besides-key","title":"TypeError: Fragments cannot have attributes besides 'key'","text":"

Give the fragment a real element, or move the attributes onto a child.

"},{"location":"reference/usage/#an-attribute-has-no-effect","title":"An attribute has no effect","text":"

The name is forwarded verbatim and never validated, so a typo is invisible. Print the dict and compare the key against what the browser expects. Only class/className and for/htmlFor have aliases; nothing else does.

"},{"location":"reference/usage/#a-numeric-style-does-nothing","title":"A numeric style does nothing","text":"

Add the unit to the string. See Common Props.

"},{"location":"reference/usage/#at-a-glance","title":"At a glance","text":""},{"location":"reference/usage/#tag-names","title":"Tag names","text":"You write You get Rule html.div <div> as-is html.data_table <data-table> _ \u2192 - html.del_ <del> trailing _ stripped html.class_ <class> trailing _ stripped html.svg.clipPath <clipPath> as-is, under svg"},{"location":"reference/usage/#arguments","title":"Arguments","text":"Position Type Meaning first plain dict attributes, or pass None for none first (otherwise) any accepted child a child rest any accepted child children, flattened"},{"location":"reference/usage/#accepted-children","title":"Accepted children","text":"Child Renders as str text, verbatim int / float str(value) None nothing True / False the words True / False list / tuple flattened children dict an element (needs \"tagName\") @component instance that component's output"},{"location":"reference/usage/#related","title":"Related","text":"
  • Common Props \u2014 the VDOM dictionary: attributes, style, key, event handlers, dangerouslySetInnerHTML
  • Your First Component \u2014 components, and when to write one instead of a tag
  • Rendering Lists \u2014 keys
  • Responding to Events \u2014 onClick and friends
  • ReactPy \u2014 serving a component tree to a browser
"},{"location":"reference/use-async-effect/","title":"Use Async Effect","text":""},{"location":"reference/use-async-effect/#overview","title":"Overview","text":"

use_async_effect is a ReactPy hook that lets you run an async def effect, and keep it running across renders.

use_async_effect(effect)\nuse_async_effect(effect, dependencies=[value])\n
"},{"location":"reference/use-async-effect/#reference","title":"Reference","text":""},{"location":"reference/use-async-effect/#use_async_effecteffect-dependencies-shieldfalse","title":"use_async_effect(effect, dependencies=..., shield=False)","text":"

Call use_async_effect at the top level of your component to run an asynchronous effect. ReactPy wraps the coroutine in an asyncio.Task, so it runs on the connection's event loop rather than blocking the render.

from reactpy import use_async_effect\n
"},{"location":"reference/use-async-effect/#parameters","title":"Parameters","text":"
  • effect: An async def function taking no arguments. To run work in the background and stop it later, loop and handle cancellation \u2014 see running a long-lived effect.

  • dependencies (optional): Same four-way meaning as in use_effect, and the default is again not React's:

    Value Behaviour (verified) omitted (...) Inferred from the free variables of your closure. dependencies=[] Starts once, on mount. dependencies=[a, b] Restarts when a or b changes. dependencies=None Restarts after every render.

    Comparison is strictly_equal (core/hooks.py), not is \u2014 see Use Effect for the rule.

  • shield (optional): When True, an in-flight effect is not cancelled when the component unmounts or the effect is superseded. Default False.

"},{"location":"reference/use-async-effect/#returns","title":"Returns","text":"

None when given the effect, or a decorator when called without one.

"},{"location":"reference/use-async-effect/#caveats","title":"Caveats","text":"
  • The effect must take zero arguments. ReactPy calls asyncio.create_task(func()) (core/hooks.py). The 1.x signature async def effect(stop): is therefore broken \u2014 and it fails silently. Verified: the component renders normally, the model carries no error, the browser shows nothing wrong; the only trace is Error in effect / TypeError: ... missing 1 required positional argument: 'stop' in the server log. If your async effect \"does nothing\", check the server log first.
  • There is no stop event to wait on. Cancellation is ordinary asyncio.CancelledError; a long-running effect should loop and break out of it.
  • Cancelling is one of two clean-up paths: when dependencies change, the running task is cancelled before the new one starts. The other is the function your coroutine returns \u2014 see Cleanup.
  • The default dependency behaviour is inference, not \"every render\". See use_effect for the full table.
  • Effects run on the server. document and window do not exist.
  • An exception raised inside the coroutine is logged on the server only \u2014 no error reaches the model or the browser.
  • The new effect cancels and awaits the previous task before starting its own (core/hooks.py), but that hand-off is best-effort: the bookkeeping that locates the previous task can miss it, so two effect coroutines can be alive at once. Measured over five runs each, with three dependency changes per run: changes spaced 0.3 s apart produced zero overlaps, anything faster produced two. Do not write an effect that assumes it is the only one running; make teardown idempotent.
"},{"location":"reference/use-async-effect/#usage","title":"Usage","text":""},{"location":"reference/use-async-effect/#importing-use_async_effect","title":"Importing use_async_effect","text":"
from reactpy import use_async_effect\n
"},{"location":"reference/use-async-effect/#running-a-one-shot-async-effect","title":"Running a one-shot async effect","text":"

For a single await \u2014 a request, a slow lookup \u2014 write the coroutine straight:

@component\ndef stats(repo):\n    data, set_data = use_state(None)\n\n    async def effect():\n        result = await fetch_stats(repo)\n        set_data(result)\n\n    use_async_effect(effect, dependencies=[repo])\n\n    return html.p(data if data is not None else \"loading...\")\n

Setting state from the effect schedules the re-render; nothing else is needed.

"},{"location":"reference/use-async-effect/#running-a-long-lived-effect","title":"Running a long-lived effect","text":"

To keep work running across renders, loop and honour cancellation. This is what ReactPy's version of React's setInterval example looks like:

app.py Run
import asyncio\nfrom datetime import datetime, timezone\n\nfrom reactpy import component, html, use_async_effect, use_state\n\n\n@component\ndef clock():\n    now, set_now = use_state(datetime.now(timezone.utc))\n\n    async def effect():\n        # Zero arguments. A long-running effect loops and honours cancellation.\n        while True:\n            set_now(datetime.now(timezone.utc))\n            try:\n                await asyncio.sleep(1)\n            except asyncio.CancelledError:\n                break\n\n    use_async_effect(effect)\n\n    return html.h1(now.strftime(\"%H:%M:%S\"))\n
# TODO\n

The clock advances on its own \u2014 verified, one update per second. When dependencies change or the component unmounts, ReactPy cancels the task and the loop exits through CancelledError.

Do not wait for a stop event

ReactPy 1.x passed a stop event into async effects. That parameter no longer exists, and passing it is not reported anywhere the user can see. Write this:

async def effect():\n    while True:\n        do_work()\n        try:\n            await asyncio.sleep(1)\n        except asyncio.CancelledError:\n            break\n

Not this:

async def effect(stop):  # \ud83d\udea9 silent failure\n    while not stop.is_set():\n        ...\n
"},{"location":"reference/use-async-effect/#shielding-an-effect-from-cancellation","title":"Shielding an effect from cancellation","text":"

shield=True lets an in-flight effect run to completion even as the component unmounts \u2014 useful for finishing a request whose result must not be discarded:

use_async_effect(effect, dependencies=[], shield=True)\n

Verified: with shield=True a sleeping effect reached its completion line after the layout exited; with the default it was cancelled at that same moment. Leave this off unless you specifically want work to outlive the component, since it also means the effect keeps holding references and consuming the loop.

"},{"location":"reference/use-async-effect/#cleanup","title":"Cleanup","text":"

Async effects have two clean-up paths, and which one applies depends on whether your coroutine finishes.

A coroutine that completes may return a clean-up function. ReactPy stores the coroutine's result (core/hooks.py, cleanup_func.current = task.result()) and runs it when the effect is next stopped \u2014 before a replacement effect starts, and again on unmount. Verified across three dependency changes and an unmount: every (start, n) was followed by its own (cleanup, n).

async def effect():\n    handle = await open_connection()\n    return handle.close  # runs at the next dependency change, and on unmount\n

A coroutine that never completes \u2014 the while True: loop \u2014 never returns, so its return value cannot be the clean-up. Cancellation is:

async def effect():\n    handle = await open_connection()\n    try:\n        while True:\n            await handle.poll()\n    except asyncio.CancelledError:\n        handle.close()\n        raise\n

Re-raising matters: swallowing CancelledError leaves the task in a state asyncio did not expect.

"},{"location":"reference/use-async-effect/#choosing-between-use_effect-and-use_async_effect","title":"Choosing between use_effect and use_async_effect","text":"Need Use A synchronous side effect use_effect Anything await-based use_async_effect A repeating timer, polling, a subscription stream use_async_effect with a loop Work that must block the render neither \u2014 do it during render, or raise

Passing an async def to use_effect fails loudly, which is the one mismatch ReactPy does report: TypeError: use_effect does not support async functions. Use use_async_effect instead.

"},{"location":"reference/use-callback/","title":"Use Callback","text":""},{"location":"reference/use-callback/#overview","title":"Overview","text":"

use_callback is a ReactPy hook that caches a function, replacing it only when one of its listed dependencies changes.

cached_handler = use_callback(handler, dependencies=[dep_1, dep_2])\n

It is the function-shaped counterpart of use_memo: same rules, same dependency behaviour, but the thing you cache is a callable rather than a value.

You probably do not need it

In React, useCallback earns its place by keeping a child from re-rendering behind memo and by keeping effect dependencies from thrashing. Neither reason applies to ReactPy. There is no render bailout and no memo, and a function you define in the component body already counts as an unchanged dependency, because ReactPy compares local functions by their code rather than by identity. So the effect-churn problem useCallback exists to solve does not occur here.

What it does give you is a genuinely stable function object, which matters only when the function leaves ReactPy and something else keys on its identity \u2014 a subscription registry, an event bus, a cached table. If none of that describes your code, write a plain def and skip this page.

"},{"location":"reference/use-callback/#reference","title":"Reference","text":""},{"location":"reference/use-callback/#use-callback","title":"use_callback(function, dependencies)","text":"

Call use_callback at the top level of your component to cache an event handler or other callback.

from reactpy import use_callback\n
"},{"location":"reference/use-callback/#parameters","title":"Parameters","text":"
  • function: The function you want to keep. It must be pure in the sense that matters here: same closure, same behaviour.
  • dependencies: The values the function reads. ReactPy replaces the cached function when any of them changes. Three shapes, identical to use_memo:

    You pass Meaning dependencies=[a, b] Replace the function when a or b changes. omitted (the default) Infer the dependencies from the function's closure. dependencies=None Return a new function on every render \u2014 no caching at all.
"},{"location":"reference/use-callback/#returns","title":"Returns","text":"

The cached function \u2014 either the one from a previous render, or, if a dependency changed, the one you just passed in.

"},{"location":"reference/use-callback/#caveats","title":"Caveats","text":"
  • The function you pass is not discarded, it is stored. ReactPy does not call it; it keeps the object. That is why caching a function that closes over state can hand you an old closure \u2014 see A cached handler sees stale values.
  • dependencies=[] pins the function forever. The same object comes back on every render, including the one holding the values it closed over on the first render. This is the single most common use_callback bug, and ReactPy will not warn you.
  • Listing a dependency you read is not optional. Forgetting one produces a handler that quietly acts on outdated values. Nothing checks this for you.
  • Stable identity does not mean stable behaviour. A function can be the same object across renders and still be wrong, because identity was the point and the closure went stale. Cache identity when something external needs identity; do not use it as a performance gesture.
  • Comparison follows strictly_equal, not identity \u2014 see the equivalent caveat in use_memo: same type first, code comparison for local functions, == otherwise.
  • The decorated name is the function, not a wrapper. @use_callback(dependencies=[x]) receives your function and returns a function, so the name still calls fine \u2014 unlike use_memo, where the name becomes the value.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return.
"},{"location":"reference/use-callback/#usage","title":"Usage","text":""},{"location":"reference/use-callback/#when-a-stable-identity-matters","title":"When a stable identity matters","text":"

Here is the case use_callback is actually for. The component subscribes to a bus that lives outside ReactPy. That bus stores handlers in a list and skips duplicates by identity \u2014 so a new function object on each render registers another handler, and every handler receives each message.

Press Re-render three times. handlers registered stays at 1, because the handler really is the same object each time. Press Publish and it is delivered once. Replace the use_callback line with a plain def and the same interaction registers four handlers.

app.pydata.py Run
@component\ndef listener():\n    renders, set_renders = use_state(0)\n    heard, set_heard = use_state(0)\n\n    # The registry lives outside ReactPy, so nothing deduplicates it for you.\n    # A plain `def` here would be a brand new function object on every render,\n    # and the registry would therefore collect another handler each time --\n    # all of which receive every message. Pinning the identity keeps exactly\n    # one handler, however often this component renders.\n    handler = use_callback(\n        lambda message: set_heard(lambda previous: previous + 1), dependencies=[]\n    )\n\n    def subscribe():\n        bus.subscribe(handler)\n\n    use_effect(subscribe, dependencies=None)\n\n    async def handle_renders_click(event):\n        set_renders(renders + 1)\n\n    async def handle_click(event):\n        bus.publish(\"ping\")\n\n    return html.div(\n        html.p(f\"renders: {renders}\"),\n        html.p(f\"handlers registered: {len(bus.handlers)}\"),\n        html.p(f\"this component heard: {heard}\"),\n        html.button({\"onClick\": handle_renders_click}, \"Re-render\"),\n        html.button({\"onClick\": handle_click}, \"Publish\"),\n    )\n
\"\"\"A stand-in for any registry that lives outside ReactPy.\n\nThis is the shape of code where a callback's identity actually matters: the\nregistry is ordinary Python, so nothing deduplicates it for you -- it stores\nhandlers by identity, and a *new* function object on each render means a new\nentry each render.\n\"\"\"\n\n\nclass Bus:\n    def __init__(self):\n        self.handlers = []\n        self.handled = []\n\n    def subscribe(self, handler):\n        # `in` on a list of plain functions compares by identity, which is\n        # exactly what a new `def` per render changes.\n        if handler not in self.handlers:\n            self.handlers.append(handler)\n\n    def publish(self, message):\n        for handler in list(self.handlers):\n            handler(message)\n\n\nbus = Bus()\n
# TODO\n

Note the heard updater: set_heard(lambda previous: previous + 1). It reads no state, which is what lets this handler keep an empty dependency list honestly \u2014 an updater function gets the current value at the moment the setter runs, so the handler does not need to close over anything.

"},{"location":"reference/use-callback/#a-cached-handler-sees-stale-values","title":"A cached handler sees stale values","text":"

The notes app below caches a handler with dependencies=[] while the handler reads draft. ReactPy keeps the first function, which closed over an empty draft, so every click adds a blank note no matter what you type.

app.py Run
@component\ndef notes():\n    notes_list, set_notes_list = use_state([])\n    draft, set_draft = use_state(\"\")\n\n    # `draft` is read here, but `dependencies=[]` promises the function never\n    # changes -- so ReactPy keeps handing back the *first* function, which\n    # closed over an empty `draft`. Each click adds a blank note no matter what\n    # you typed. Listing what the handler reads is what fixes it.\n    async def add_note_handler(event):\n        set_notes_list([*notes_list, draft])\n\n    add_note = use_callback(add_note_handler, dependencies=[])\n\n    async def handle_draft_change(event):\n        set_draft(await event.target.value)\n\n    return html.div(\n        html.h1(\"Notes\"),\n        html.ul(\n            [\n                html.li({\"key\": str(i)}, note or \"(blank)\")\n                for i, note in enumerate(notes_list)\n            ]\n        ),\n        html.input(\n            {\n                \"value\": draft,\n                \"onChange\": handle_draft_change,\n            }\n        ),\n        html.button({\"onClick\": add_note}, \"Add note\"),\n    )\n
# TODO\n

Type something and press Add note: the list gains (blank). The handler is working perfectly \u2014 on the render it was created on.

"},{"location":"reference/use-callback/#listing-what-the-handler-reads","title":"Listing what the handler reads","text":"

The fix is to name the values the function reads. Now the cached function is replaced whenever notes_list or draft changes, so a click always sees what is currently in the box \u2014 while the identity still stays put between changes.

app.py Run
@component\ndef notes():\n    notes_list, set_notes_list = use_state([])\n    draft, set_draft = use_state(\"\")\n\n    # List what the handler reads and the cached function is replaced exactly\n    # when those values change: the click always sees the current draft, while\n    # the identity still stays put between changes.\n    async def add_note_handler(event):\n        set_notes_list([*notes_list, draft])\n\n    add_note = use_callback(add_note_handler, dependencies=[notes_list, draft])\n\n    async def handle_draft_change(event):\n        set_draft(await event.target.value)\n\n    return html.div(\n        html.h1(\"Notes\"),\n        html.ul(\n            [\n                html.li({\"key\": str(i)}, note or \"(blank)\")\n                for i, note in enumerate(notes_list)\n            ]\n        ),\n        html.input(\n            {\n                \"value\": draft,\n                \"onChange\": handle_draft_change,\n            }\n        ),\n        html.button({\"onClick\": add_note}, \"Add note\"),\n    )\n
# TODO\n

Type something and press Add note: the text lands. These two files differ by one line; the behaviour difference is the whole point.

"},{"location":"reference/use-callback/#omitting-the-dependency-list","title":"Omitting the dependency list","text":"

Leave out dependencies and ReactPy infers them from the function's closure, exactly as it does for use_memo and use_effect. For a handler that reads component state this is usually what you want, and it removes the \"forgot a dependency\" class of bug. Omitting is not the same as dependencies=[] \u2014 the inferred list is built from what the function actually reads, which is rarely empty.

"},{"location":"reference/use-callback/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-callback/#my-handler-acts-on-old-values","title":"My handler acts on old values","text":"

You cached it with an empty or incomplete dependency list. The function is the same object, and so is everything it closed over. Either list the values it reads, or stop the value from being read at all by using an updater \u2014 set_items(lambda previous: [*previous, new]) instead of set_items([*items, new]).

"},{"location":"reference/use-callback/#my-handler-never-fires","title":"My handler never fires","text":"

Check the attribute name. {\"on_click\": handler} renders without complaint, lands in eventHandlers, and is never called \u2014 the client registers a DOM listener for the key with on stripped, so the underscore spelling subscribes to an event named _click that no browser ever fires. The key must be the DOM event name in camelCase (\"onClick\"), and the value must be your function, not a call to it. A wrong name is invisible: the only difference is that nothing happens.

"},{"location":"reference/use-callback/#i-still-get-a-new-function-every-render","title":"I still get a new function every render","text":"
  • You passed dependencies=None, which means exactly that.
  • A dependency is rebuilt each render and does not compare equal \u2014 a plain class instance without __eq__, for instance.
  • The function is defined outside the component and you expect it to be cached; use_callback caches what you hand it, per component instance.
"},{"location":"reference/use-callback/#do-i-need-this-to-stop-a-child-re-rendering","title":"Do I need this to stop a child re-rendering?","text":"

No \u2014 that mechanism does not exist in ReactPy. See the note at the top of the page: children re-render whenever their parent does, regardless of prop or handler identity.

"},{"location":"reference/use-connection/","title":"Use Connection","text":""},{"location":"reference/use-connection/#overview","title":"Overview","text":"

use_connection is a ReactPy hook that returns the Connection object describing the websocket your component is rendering into.

connection = use_connection()\n

Each connected browser tab has its own. The object carries three things: the ASGI scope of the websocket, the location the browser reports, and the server's object mediating the connection.

"},{"location":"reference/use-connection/#reference","title":"Reference","text":""},{"location":"reference/use-connection/#use-connection","title":"use_connection()","text":"

Call it at the top level of a component. It takes no arguments.

from reactpy import use_connection\n
"},{"location":"reference/use-connection/#returns","title":"Returns","text":"

A reactpy.types.Connection dataclass with three fields:

  • scope: The ASGI scope of the websocket \u2014 the same dict use_scope returns.
  • location: A Location describing the page the browser is on \u2014 the same object use_location() returns.
  • carrier: The object mediating the connection. What it is depends on the executor; under the ASGI executors it is a ReactPyWebsocket. Capture type(connection.carrier).__name__ rather than assuming.

Import the type with from reactpy.types import Connection; it is not exported from the reactpy namespace.

"},{"location":"reference/use-connection/#caveats","title":"Caveats","text":"
  • This is a read-only view. Connection has no deliver, no send, no close, no id \u2014 verified against the class. You cannot push a message to this client from here; communicate through state, effects, and event handlers. The carrier object under the ASGI executors is an internal ReactPyWebsocket, not a supported API to call into.
  • scope and location are live, mutable objects, not copies. Writing into connection.scope changes what every other read sees. Treat both as read-only.
  • There is one per connection, not per render. Each connected tab gets its own \u2014 verified: two concurrent clients saw distinct scopes and distinct locations \u2014 but within one tab the same object comes back on every render.
  • It needs a live connection. Two distinct failures:

    • Called outside any component render \u2192 RuntimeError: No life cycle hook is active. Are you rendering in a layout?
    • Called inside a plain Layout render with no provider \u2192 RuntimeError: No backend established a connection.

    Both messages are verbatim. In practice the second is the one you meet in tests; see injecting a connection in tests. - It does not exist under client-side rendering. ReactPyCsr runs components without a Connection at all, so the hook raises there even though Python is in the browser. Verified in Chrome. - Like every hook, call it only at the top level.

"},{"location":"reference/use-connection/#usage","title":"Usage","text":""},{"location":"reference/use-connection/#who-is-connected","title":"Who is connected","text":"

Each field answers a different question, and mixing them up is the usual source of confusion.

app.py Run
@component\ndef connection_report():\n    connection = use_connection()\n\n    # Three things, three sources: `scope` is what the ASGI server received,\n    # `location` is the URL the browser reported, `carrier` is the object\n    # mediating the connection (a websocket for the ASGI executors).\n    return html.div(\n        html.h1(\"Who is connected\"),\n        html.p(f\"peer: {connection.scope['client']}\"),\n        html.p(f\"page: {connection.location.path}\"),\n        html.p(f\"protocol: {connection.scope['type']}\"),\n        html.p(f\"carrier: {type(connection.carrier).__name__}\"),\n    )\n
# TODO\n

scope['client'] is what the server saw when the websocket opened. location.path is what the browser reported. They are independent sources, which is why the page path is not in the scope \u2014 see use_location for why that matters.

"},{"location":"reference/use-connection/#injecting-a-connection-in-tests","title":"Injecting a connection in tests","text":"

Components that use any of the connection hooks fail in a bare Layout render. For a unit test, build the Connection yourself and wrap the component in the provider \u2014 ConnectionContext is meant to be set by the backend at the root of an app, and a test is a backend.

import asyncio\n\nfrom reactpy.core.hooks import ConnectionContext\nfrom reactpy.core.layout import Layout\nfrom reactpy.types import Connection, Location\n\nfrom .app import page  # the component under test\n\nconnection = Connection(\n    scope={\"type\": \"websocket\", \"client\": (\"127.0.0.1\", 5000)},\n    location=Location(path=\"/reports\", query_string=\"?tab=2\"),\n    carrier=None,\n)\n\n\ndef test_page():\n    async def render():\n        async with Layout(ConnectionContext(page(), value=connection)) as layout:\n            update = await layout.render()\n        return str(update[\"model\"])\n\n    rendered = asyncio.run(render())\n    assert \"/reports\" in rendered  # came from the injected Location\n

carrier can be anything \u2014 None is fine, since nothing in a unit test dispatches over it. Note that ConnectionContext lives in reactpy.core.hooks rather than the public reactpy namespace: it is deliberately the backend's knob, and in a test your code is the backend.

The hooks consume no hook slot

All three connection hooks ultimately call use_context, which reads the provider without reserving a hook slot. Unlike use_state or use_effect, they are safe to call conditionally or on only some renders; they will not shift other hooks' state. Verified: a component calling use_connection() only on even renders re-rendered cleanly across five renders.

"},{"location":"reference/use-connection/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-connection/#runtimeerror-no-backend-established-a-connection","title":"RuntimeError: No backend established a connection.","text":"

No ConnectionContext was set. Under ReactPy or ReactPyMiddleware it is set for you once the websocket opens, so in production this means the component rendered somewhere the executor does not cover \u2014 most commonly a plain Layout render, a reactpy_to_string call, or a ReactPyCsr page. Either inject one as above, or make the component tolerate its absence:

try:\n    connection = use_connection()\nexcept RuntimeError:\n    return html.p(\"(not connected yet)\")\n
"},{"location":"reference/use-connection/#runtimeerror-no-life-cycle-hook-is-active-are-you-rendering-in-a-layout","title":"RuntimeError: No life cycle hook is active. Are you rendering in a layout?","text":"

You called the hook outside a render \u2014 at module level, in a plain function the component calls before rendering, or directly in your test body. Hooks only work while a component is rendering inside a Layout.

"},{"location":"reference/use-connection/#connectioncarrier-is-none-in-my-test","title":"connection.carrier is None in my test","text":"

Correct \u2014 you passed None, and nothing reads it. Only the executor's own plumbing touches the carrier.

"},{"location":"reference/use-connection/#i-need-to-send-something-to-this-specific-client","title":"I need to send something to this specific client","text":"

There is no supported way to reach a single connection from a component. State changes re-render that client's own components; anything beyond that is outside the scope of these hooks.

"},{"location":"reference/use-context/","title":"Use Context","text":""},{"location":"reference/use-context/#overview","title":"Overview","text":"

use_context is a ReactPy hook that reads a value provided by a component higher in the tree, without every component in between having to pass it along.

value = use_context(SomeContext)\n

Context is for values a whole subtree needs and the middle of the tree does not: a theme, the current user, the active locale, a store. If a component would only forward a prop to its children, context removes both the prop and the component's reason to exist.

"},{"location":"reference/use-context/#reference","title":"Reference","text":""},{"location":"reference/use-context/#use-context","title":"use_context(context)","text":"

Call it at the top level of a component, passing the context object you created with create_context.

from reactpy import use_context\n
"},{"location":"reference/use-context/#parameters","title":"Parameters","text":"
  • context: The object returned by create_context. It is matched by identity, so the same object must be used on both the provider and the reader \u2014 import it from one place.
"},{"location":"reference/use-context/#returns","title":"Returns","text":"

The value of the nearest context provider above the component. With no provider anywhere above, it returns the default given to create_context.

"},{"location":"reference/use-context/#caveats","title":"Caveats","text":"
  • Reading context is not a subscription that spares renders. When a provider re-renders with a new value, its children re-render \u2014 which they would have anyway, since ReactPy re-renders children unconditionally. Context hands you the new value; it does not narrow the work. See What context does not do.
  • It returns the default, not None, when nothing provides it. create_context(\"light\") yields \"light\" to an unprovided reader, so a component renders on its own without a wrapper. Pick a default that makes sense standalone, or None plus an explicit fallback branch.
  • Call it during the render, not from an effect body. An effect runs later, in its own task, where no hook is active; calling use_context there raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? Read it in the component and let the closure carry the value in \u2014 see Reading context inside an effect.
  • The context object must be hashable and must be the same object. Passing something else raises: TypeError: <x> is not a Context for a class or string, TypeError: unhashable type: 'dict' if you hand it a dict or list. Two create_context calls over the same default are two unrelated contexts.
  • A None default narrows the type. create_context(None) infers a value type of None, so a type checker rejects value=[...]. Annotate the name to widen it: Cart: Context[list[str] | None] = create_context(None).
  • Like every hook, call it only at the top level. Unusually, context is safe to call conditionally \u2014 it reserves no hook slot, so unlike use_state it cannot shift other hooks' state.
"},{"location":"reference/use-context/#creating-and-providing-a-context","title":"Creating and providing a context","text":"

A context is created once, at module level, with create_context:

from reactpy import create_context\n\nTheme = create_context(\"light\")\n

What comes back is an ordinary function, and calling it is the provider: pass the children positionally and the value as a keyword.

Theme(child_a, child_b, value=\"dark\")\n

There is no Theme.Provider \u2014 calling the context is the only form. The provider renders a nameless fragment around its children, so it adds nothing to the output.

The value= keyword is optional; omit it and the default from create_context is used. A key= keyword is also accepted, with the same meaning as on any other element.

"},{"location":"reference/use-context/#usage","title":"Usage","text":""},{"location":"reference/use-context/#passing-data-deeply","title":"Passing data deeply","text":"

swatch needs the theme. toolbar does not \u2014 it renders swatch and never mentions it. Without context, toolbar would take a theme argument purely to forward it, and every component added in between would inherit that obligation.

Press Switch to dark: the provider gets a new value, and the component far below reads it.

app.py Run
# One context per concept, created once at module level. The argument is the\n# value descendants get when nothing provides one -- a sensible default means\n# a component can render before any provider exists.\nTheme = create_context(\"light\")\n\n\n@component\ndef swatch():\n    # Reads whatever the nearest provider above it supplied. It takes no props:\n    # the value arrives without anything in between having to pass it along.\n    theme = use_context(Theme)\n    return html.div(\n        html.p(f\"theme: {theme}\"),\n        html.div({\"style\": {\"background\": theme, \"height\": \"2rem\"}}, \"\"),\n    )\n\n\n@component\ndef toolbar():\n    # A component in the middle. It neither receives nor cares about `theme`;\n    # it only has to render its children for them to reach the provider above.\n    return html.div(html.h2(\"Toolbar\"), swatch())\n\n\n@component\ndef app():\n    theme, set_theme = use_state(\"light\")\n\n    async def handle_theme_click(event):\n        set_theme(\"dark\" if theme == \"light\" else \"light\")\n\n    return html.div(\n        Theme(toolbar(), value=theme),\n        html.button(\n            {\"onClick\": handle_theme_click},\n            f\"Switch to {'dark' if theme == 'light' else 'light'}\",\n        ),\n    )\n
# TODO\n
"},{"location":"reference/use-context/#choosing-a-default","title":"Choosing a default","text":"

The default is what a component sees when no provider is above it, which is exactly the situation in a unit test or a standalone render. Make it something the component can handle.

cart_badge below copes with either \u2014 rendered bare it says the cart is unavailable, wrapped in the provider it counts the items.

app.py Run
from reactpy import component, create_context, html, use_context\nfrom reactpy.types import Context\n\n# The annotation is what lets the value be a list *or* the None default. Without\n# it, create_context(None) fixes the value type to None and a type checker then\n# rejects `value=[...]`.\nCart: Context[list[str] | None] = create_context(None)\n\n\n@component\ndef cart_badge():\n    # With no provider above, use_context returns the default given to\n    # create_context. Choosing that default well is what lets a component\n    # render -- or be tested -- on its own, with no provider wrapper.\n    items = use_context(Cart)\n    if items is None:\n        return html.p(\"cart: not available\")\n    return html.p(f\"cart: {len(items)} item(s)\")\n\n\n@component\ndef shop():\n    return html.div(cart_badge())\n\n\n@component\ndef shop_with_cart():\n    return Cart(shop(), value=[\"widget\", \"gadget\", \"gizmo\"])\n
# TODO\n

Note the type annotation on Cart. Because the default is None, without the annotation a type checker infers the value type as None and rejects Cart(shop(), value=[\"widget\", ...]).

"},{"location":"reference/use-context/#nesting-providers","title":"Nesting providers","text":"

The provider nearest above the reader wins, and sibling subtrees are independent. \"Above\" means in the tree that renders it, so the reader has to live inside a child component \u2014 reading in the enclosing body runs before the provider is rendered:

Theme = create_context(\"light\")\n\n\n@component\ndef swatch():\n    # A child component: it renders after the provider above it has registered.\n    return html.p(use_context(Theme))\n\n\n@component\ndef page():\n    return Theme(\n        swatch(),                                     # \"outer\"\n        Theme(swatch(), value=\"inner\"),               # \"inner\"\n        value=\"outer\",\n    )\n

Verified: the first reads \"outer\", the second \"inner\"; two sibling providers with different values each see their own.

Do not read the context in the same body that renders the provider

@component\ndef page():\n    return Theme(\n        html.p(use_context(Theme)),   # \"light\", not \"outer\"\n        value=\"outer\",\n    )\n

Arguments are evaluated while page() runs, and the provider registers its value only when the layout later renders it. Both reads here return the create_context(\"light\") default. Move the read into a child component. The same rule explains why reading inside an effect fails (below).

Removing a provider returns its descendants to the default. What actually produces that is the remount: a provider is a component node in the tree, so dropping it moves its children to a different position and ReactPy rebuilds them \u2014 verified, a counter below a removed provider went back to 0 along with its value. Do not lean on that. core/_life_cycle_hook.py, set_current only ever merges the parent's providers into a hook (_context_providers.update(...)) and nothing prunes the dict, so a component that survives a render keeps whatever it inherited. If you need a guaranteed reset, change the child's \"key\".

"},{"location":"reference/use-context/#reading-context-inside-an-effect","title":"Reading context inside an effect","text":"

Effects run in their own task, outside any component render, so no hook is active there and use_context will not work. Read it in the component and use the local:

app.py Run
Config = create_context({\"endpoint\": \"https://api.example\"})\n\n\n@component\ndef status():\n    # Read the context HERE, during the render. An effect body runs later, in\n    # its own task, where no hook is active -- calling use_context there fails\n    # with `RuntimeError: No life cycle hook is active.` Capture the value in a\n    # local and let the closure carry it into the effect.\n    config = use_context(Config)\n\n    def on_mount():\n        print(f\"polling {config['endpoint']}\")\n\n    use_effect(on_mount, dependencies=[config])\n    return html.p(f\"endpoint: {config['endpoint']}\")\n
# TODO\n

Putting use_context(Config) inside on_mount instead raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 and since an effect error is logged only on the server, it is easy to miss.

"},{"location":"reference/use-context/#what-context-does-not-do","title":"What context does not do","text":"

Context solves plumbing: it moves a value past components that do not care about it. It is not a performance feature.

  • It does not stop children from re-rendering. ReactPy has no render bailout, so when the provider re-renders, everything below it re-renders too. A component reading a context whose value did not change still re-renders \u2014 verified: toggling a theme re-ran the bodies of both a context reader and an unrelated sibling.
  • It does not make a value reactive by itself. The value changes because the component that renders the provider re-rendered with a new one. If nothing re-renders the provider, readers keep seeing the old value.
  • It does not replace state. Context carries values down. To let a component change a shared value, put the state in a common ancestor and pass the setter down alongside it \u2014 or move to a reducer, see use_reducer.

For a value that only one component's subtree uses, a prop is simpler and easier to trace. Reach for context when the value genuinely crosses several levels that have no use for it.

"},{"location":"reference/use-context/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-context/#readers-always-get-the-default","title":"Readers always get the default","text":"

Usually one of:

  • The provider is not above the reader in the tree. Theme(child, value=...) must be an ancestor \u2014 calling it and discarding the result provides nothing.
  • The provider and the reader hold different context objects. Two create_context calls are different contexts even with the same default, so create it once and import it.
  • The provider's value= never changes, so readers keep seeing the value from the render that set it.
"},{"location":"reference/use-context/#runtimeerror-no-life-cycle-hook-is-active-are-you-rendering-in-a-layout","title":"RuntimeError: No life cycle hook is active. Are you rendering in a layout?","text":"

You called use_context outside a render \u2014 from an effect body, from a plain function called before the component runs, or directly in a test. Move the call into the component body.

"},{"location":"reference/use-context/#typeerror-is-not-a-context","title":"TypeError: ... is not a Context","text":"

You passed something other than the result of create_context \u2014 often the provider instead of the context (use_context(Theme(child, value=\"x\"))), or the class of a value rather than a context.

"},{"location":"reference/use-context/#my-type-checker-rejects-the-providers-value","title":"My type checker rejects the provider's value","text":"

The default narrowed the type. Annotate the module-level name with Context[<your type> | None], as in the default-value example above.

"},{"location":"reference/use-debug-value/","title":"Use Debug Value","text":""},{"location":"reference/use-debug-value/#overview","title":"Overview","text":"

use_debug_value is a ReactPy hook that writes a message to the server log whenever it changes, for the component that calls it.

use_debug_value(message, dependencies)\n

ReactPy components run on the server, so there is no DevTools panel to label. This is the equivalent: a value that shows up in your server's log output, tagged with the component that produced it, printed only when it differs from the last thing printed.

It needs debug mode, and logs go to the server

Nothing happens unless REACTPY_DEBUG is on. Set it before importing ReactPy \u2014 REACTPY_DEBUG=1 python app.py, or os.environ[\"REACTPY_DEBUG\"] = \"1\" at the top of your entry point. Output goes through the standard logging module under the reactpy.core.hooks logger, so it lands wherever your logging configuration sends it, not in the browser console. A reader who expects DevTools will conclude the hook is broken; it is not.

"},{"location":"reference/use-debug-value/#reference","title":"Reference","text":""},{"location":"reference/use-debug-value/#use-debug-value","title":"use_debug_value(message, dependencies)","text":"

Call it at the top level of a component, or at the top level of one of your own hooks.

from reactpy import use_debug_value\n
"},{"location":"reference/use-debug-value/#parameters","title":"Parameters","text":"
  • message: The value to log, or a function of no arguments that produces it. Pass a function when building the message costs something \u2014 ReactPy memoizes it, so it re-runs only when its dependencies change.
  • dependencies: Only meaningful with a function message. Same three shapes as every other hook that takes them:

    You pass Meaning dependencies=[a, b] Rebuild the message when a or b changes. omitted (the default) Infer them from the function's closure. dependencies=None Rebuild the message on every render.
"},{"location":"reference/use-debug-value/#returns","title":"Returns","text":"

None.

"},{"location":"reference/use-debug-value/#caveats","title":"Caveats","text":"
  • Silent without debug mode. With REACTPY_DEBUG off, the hook logs nothing. Whether it still builds the message is decided by the dependency comparison, not by debug mode: the message is produced through use_memo, so a builder whose dependencies never change runs once and its result is discarded on every later render. A builder that closes over changing state runs every render, debug or no debug. If the build is expensive, control it with the dependency argument \u2014 do not assume the hook skipped it, and do not assume it ran.
  • \"Changed\" means !=, not identity. Unlike every other hook, which compares dependencies with strictly_equal, this one compares the new message against the previous one with plain !=. So a freshly constructed {\"kind\": \"rows\"} logs once and then stays quiet, because it compares equal to what was logged last time. Verified.
  • The comparison runs on every render, on the value you supply. For a large or slow-to-compare object that is a real cost, paid whether or not anything gets logged. This is the one hook where the docstring's performance note is worth taking literally.
  • An uncomparable message breaks the component. If message != previous raises, the exception is not caught: it surfaces as a render error in the model. A class with a __eq__ that raises will fail the component, so keep messages to plain data \u2014 strings, numbers, dicts, lists.
  • The value is memoized like any other hook value. With a stable message, the builder function runs once; with a message that changes each render, it runs each render. Measured across four renders: stable \u2192 1 call, changing \u2192 4 calls.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return.
"},{"location":"reference/use-debug-value/#usage","title":"Usage","text":""},{"location":"reference/use-debug-value/#labelling-a-custom-hook","title":"Labelling a custom hook","text":"

The useful case. When a component calls three of your own hooks, a log line that says only shop(...) tells you nothing; a message naming what the hook holds tells you which hook is behaving oddly, and when.

Run this with debug on and press Add item. Each add logs one line through the reactpy.core.hooks logger, prefixed with the component and its id:

shop(7f7ef6f0d540) 0 item(s): -\nshop(7f7ef6f0d540) 1 item(s): item 1\nshop(7f7ef6f0d540) 2 item(s): item 1, item 2\nshop(7f7ef6f0d540) 3 item(s): item 1, item 2, item 3\n

Pressing a button that leaves the cart alone logs nothing, because the message did not change.

app.py Run
def use_cart():\n    \"\"\"A custom hook: it holds the cart and reports what it holds.\"\"\"\n    items, set_items = use_state([])\n\n    # Logged to the server as `use_cart's component(...) <message>` whenever the\n    # message differs from the last one logged. A plain string here would be\n    # built on every render; reading state makes it change only when it should.\n    use_debug_value(f\"{len(items)} item(s): {', '.join(items) or '-'}\")\n\n    async def add(event=None):\n        set_items([*items, f\"item {len(items) + 1}\"])\n\n    return items, add\n\n\n@component\ndef shop():\n    items, add = use_cart()\n    return html.div(\n        html.h1(f\"Cart ({len(items)})\"),\n        html.ul([html.li({\"key\": i}, item) for i, item in enumerate(items)]),\n        html.button({\"onClick\": add}, \"Add item\"),\n    )\n
# TODO\n
"},{"location":"reference/use-debug-value/#passing-a-function-as-the-message","title":"Passing a function as the message","text":"

If producing the message is more than string formatting, pass a function and let ReactPy memoize it. The report below finds the highest score to build its label; that walk happens once per distinct rows, not once per render.

Run it with debug on and press Add row:

report(7f7ef6f0da80) 1 rows, best=ada:91\nreport(7f7ef6f0da80) 2 rows, best=ada:91\nreport(7f7ef6f0da80) 3 rows, best=ada:91\n
app.py Run
@component\ndef report():\n    rows, set_rows = use_state([{\"name\": \"ada\", \"score\": 91}])\n\n    # Pass a function when turning the message into text is not free. ReactPy\n    # memoizes it like any other hook, so it re-runs only when what it reads\n    # changes -- and not at all on renders that would not have logged anything.\n    def describe():\n        best = max(rows, key=lambda row: row[\"score\"])\n        return f\"{len(rows)} rows, best={best['name']}:{best['score']}\"\n\n    use_debug_value(describe)\n\n    async def add_row(event=None):\n        set_rows([*rows, {\"name\": f\"row {len(rows)}\", \"score\": len(rows) * 7}])\n\n    return html.div(\n        html.h1(f\"{len(rows)} rows\"),\n        html.button({\"onClick\": add_row}, \"Add row\"),\n    )\n
# TODO\n

Keep the message honest about what it reads. A function that reads no state is computed once and then logs nothing ever again, which usually reads as \"the hook stopped working\" rather than as correct memoization.

"},{"location":"reference/use-debug-value/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-debug-value/#nothing-is-logged","title":"Nothing is logged","text":"

Almost always debug mode. The REACTPY_DEBUG environment variable is read when reactpy.config is first imported, so setting os.environ after ReactPy is already imported has no effect \u2014 start the process with REACTPY_DEBUG=1, or set the variable before importing anything from reactpy.

The option object can also be flipped at any point, which is the version that works from inside a test or a REPL:

from reactpy.config import REACTPY_DEBUG\n\nREACTPY_DEBUG.current = True\n

If debug is on and you still see nothing, the logger is being filtered. ReactPy logs at DEBUG level under reactpy.core.hooks; a configuration that shows only warnings will hide these lines.

"},{"location":"reference/use-debug-value/#it-logs-on-every-render","title":"It logs on every render","text":"

Your message changes every render. A timestamp, id(self), a freshly built object compared by identity, or a counter you increment during rendering all do this. Make the message describe state rather than the render.

"},{"location":"reference/use-debug-value/#it-stopped-logging","title":"It stopped logging","text":"

The message has not changed since the last line, which is the hook working as designed. If you expected a change, either the value genuinely did not change or your function is memoized on dependencies that never move \u2014 pass dependencies=None briefly to confirm which.

"},{"location":"reference/use-debug-value/#my-component-fails-to-render-after-adding-it","title":"My component fails to render after adding it","text":"

You passed something whose != raises. The comparison is not guarded, so the exception becomes a render error. Pass plain data instead, and format it lazily in a function if it is large.

"},{"location":"reference/use-deferred-value/","title":"Use deferred value","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-effect/","title":"Use Effect","text":""},{"location":"reference/use-effect/#overview","title":"Overview","text":"

use_effect is a ReactPy hook that lets you synchronize a component with an external system.

use_effect(effect)\nuse_effect(effect, dependencies=[value])\n
"},{"location":"reference/use-effect/#reference","title":"Reference","text":""},{"location":"reference/use-effect/#use_effecteffect-dependencies","title":"use_effect(effect, dependencies=...)","text":"

Call use_effect at the top level of your component to run an effect after rendering.

from reactpy import use_effect\n
"},{"location":"reference/use-effect/#parameters","title":"Parameters","text":"
  • effect: A function taking no arguments. ReactPy calls it after the component renders. It may return a clean-up function (also taking no arguments), which ReactPy runs before the next time this effect runs and again when the component unmounts.

    The effect must be pure, and it must not be declared async def \u2014 ReactPy raises for that; use use_async_effect instead.

  • dependencies (optional): Controls when the effect re-runs. Unlike React, this argument has four distinct settings, and the default is not React's:

    Value Behaviour (verified) omitted (...) Inferred. ReactPy reads the free variables of your closure (core/hooks.py, _try_to_infer_closure_values) and re-runs when any of them changes. A closure that reads nothing runs once. dependencies=[] Runs once, after mount. dependencies=[a, b] Runs when a or b changes. dependencies=None Runs after every render.

    \"Changes\" means strictly_equal (core/hooks.py), not is: the two values must share a type, two locally-defined functions or lambdas are compared by bytecode, and everything else falls through to == with is as the last resort. A rebuilt list or dict with equal contents does not re-run the effect. Use Memo documents the same function in full.

"},{"location":"reference/use-effect/#returns","title":"Returns","text":"

None when given the effect, or a decorator when called without one.

"},{"location":"reference/use-effect/#caveats","title":"Caveats","text":"
  • The default is inference, not \"every render\". In React, omitting the dependency array means the effect runs after every render. In ReactPy it means \"re-run when the variables my closure reads change\". Code ported from React will therefore behave differently \u2014 pass dependencies=None if you really want every-render behaviour, though usually you want to be explicit instead.
  • Inference is structural, not declared, and it does not compare by id. A closure that happens to read an extra local variable gains an extra dependency, and the comparison is strictly_equal \u2014 type, then bytecode for local functions, then == \u2014 so a rebuilt-but-equal container counts as unchanged. If the effect runs more or less often than you expect, look at which names it actually reads, and then at how those values compare.
  • Effects run on the server, after rendering, not in the browser. There is no DOM to touch: document and window do not exist here. There is no document or window to reach for, and no ReactPy equivalent of attaching a listener to a node \u2014 see Responding to Events for the supported way to react to the browser.
  • Children's effects run before their parent's, and siblings run in reverse order. Verified output: ['child effect b', 'child effect a', 'parent effect']. Two effects in one component run in declaration order. Do not rely on React's parent-first or source-order guarantees.
  • Passing async def to use_effect fails loudly: TypeError: use_effect does not support async functions. Use use_async_effect instead. The error lands in the model's error field and the component stops rendering.
  • An exception raised inside an effect body is logged on the server only; the model carries no error and the UI looks unchanged. Check the server log.
  • Effects are not a substitute for computing values. Derive them during render or with use_memo.
  • There is no use_layout_effect, use_insertion_effect, or use_imperative_handle. Nothing runs synchronously during the render phase.
"},{"location":"reference/use-effect/#usage","title":"Usage","text":""},{"location":"reference/use-effect/#importing-use_effect","title":"Importing use_effect","text":"
from reactpy import use_effect\n
"},{"location":"reference/use-effect/#describing-an-effect","title":"Describing an effect","text":"

Pass the setup function directly:

app.pydata.py Run
@component\ndef live_feed():\n    latest, set_latest = use_state(\"waiting for a message...\")\n\n    def effect():\n        # Runs once on mount; the returned function runs on unmount.\n        unsubscribe = ticker.subscribe(\"news\", set_latest)\n        return unsubscribe\n\n    use_effect(effect, dependencies=[])\n\n    async def handle_click(event):\n        ticker.publish(\"news\", \"Hello!\")\n\n    return html.div(\n        html.h1(latest),\n        html.button(\n            {\"onClick\": handle_click},\n            \"Publish\",\n        ),\n    )\n
\"\"\"A stand-in for any external system with subscribe/unsubscribe semantics.\"\"\"\n\n\nclass Ticker:\n    def __init__(self):\n        self.topics = {}\n\n    def subscribe(self, topic, callback):\n        self.topics.setdefault(topic, []).append(callback)\n\n        def unsubscribe():\n            self.topics[topic].remove(callback)\n\n        return unsubscribe\n\n    def publish(self, topic, message):\n        for callback in list(self.topics.get(topic, [])):\n            callback(message)\n\n    def count(self):\n        return sum(len(v) for v in self.topics.values())\n\n\nticker = Ticker()\n
# TODO\n

Or use it as a decorator, which is equivalent:

@use_effect\ndef effect():\n    print(\"mounted\")\n
"},{"location":"reference/use-effect/#telling-reactpy-when-to-re-run-the-effect","title":"Telling ReactPy when to re-run the effect","text":"

Pass a dependency list to re-run only when something relevant changes:

app.pydata.py Run
@component\ndef live_feed():\n    topic, set_topic = use_state(\"news\")\n    latest, set_latest = use_state(\"\")\n\n    def effect():\n        # Re-runs whenever `topic` changes; the returned clean-up unsubscribes\n        # the previous topic first, so you never keep two subscriptions.\n        unsubscribe = ticker.subscribe(topic, set_latest)\n        return unsubscribe\n\n    use_effect(effect, dependencies=[topic])\n\n    async def handle_topic_click(event):\n        set_topic(\"sports\" if topic == \"news\" else \"news\")\n\n    async def handle_click(event):\n        ticker.publish(topic, \"ping\")\n\n    return html.div(\n        html.p(f\"Subscribed to: {topic}\"),\n        html.p(latest or \"(nothing yet)\"),\n        html.button(\n            {\"onClick\": handle_topic_click},\n            \"Switch topic\",\n        ),\n        html.button({\"onClick\": handle_click}, \"Publish\"),\n    )\n
\"\"\"A stand-in for any external system with subscribe/unsubscribe semantics.\"\"\"\n\n\nclass Ticker:\n    def __init__(self):\n        self.topics = {}\n\n    def subscribe(self, topic, callback):\n        self.topics.setdefault(topic, []).append(callback)\n\n        def unsubscribe():\n            self.topics[topic].remove(callback)\n\n        return unsubscribe\n\n    def publish(self, topic, message):\n        for callback in list(self.topics.get(topic, [])):\n            callback(message)\n\n    def count(self):\n        return sum(len(v) for v in self.topics.values())\n\n\nticker = Ticker()\n
# TODO\n

Switching topic tears down the old subscription before creating the new one \u2014 verified, the subscriber count stays at 1 across switches and drops to 0 on unmount.

"},{"location":"reference/use-effect/#omitting-the-dependencies-argument","title":"Omitting the dependencies argument","text":"
use_effect(effect)               # inferred from what the closure reads\n

The example below makes inference visible. The effect reads count but not label, so renaming re-renders the component without re-running the effect:

app.py Run
@component\ndef logger():\n    count, set_count = use_state(0)\n    label, set_label = use_state(\"counter\")\n    runs, set_runs = use_state([])\n\n    def effect():\n        # No `dependencies` argument, so ReactPy infers them from the names\n        # this closure reads. Only `count` is a real dependency here -- the\n        # setter is stable, and `label` is not read at all. So pressing\n        # Rename re-renders the component but does NOT re-run this effect.\n        set_runs(lambda previous: [*previous, f\"effect ran with count={count}\"])\n\n    use_effect(effect)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_label_click(event):\n        set_label(label + \"!\")\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button({\"onClick\": handle_label_click}, \"Rename\"),\n        html.p(f\"Label: {label}\"),\n        html.ul([html.li({\"key\": i}, line) for i, line in enumerate(runs)]),\n    )\n
# TODO\n

Pressing Rename twice adds no new log line \u2014 verified. Be aware this works because of which names the closure reads; it is implicit, so many teams prefer to spell the list out.

"},{"location":"reference/use-effect/#passing-an-empty-dependency-array","title":"Passing an empty dependency array","text":"
use_effect(effect, dependencies=[])   # once, after mount\n

For mount-only work \u2014 subscribing, starting a timer, reading a config \u2014 say so explicitly.

"},{"location":"reference/use-effect/#passing-none","title":"Passing None","text":"
use_effect(effect, dependencies=None)  # after every render\n

This is the closest ReactPy gets to React's \"no array\" behaviour. It re-runs the effect after every render, verified. Reach for it rarely: it usually means the effect is doing work that belongs in render or in use_memo.

"},{"location":"reference/use-effect/#adding-clean-up","title":"Adding clean-up","text":"

Return a function from the effect. ReactPy calls it before the next run of that effect and when the component unmounts:

app.py Run
@component\ndef app():\n    connected, set_connected = use_state(False)\n\n    async def handle_connected_click(event):\n        set_connected(not connected)\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_connected_click},\n            \"Connect\" if not connected else \"Disconnect\",\n        ),\n        *([] if not connected else [connection()]),\n    )\n
# TODO\n

Connecting and disconnecting runs the pair in order \u2014 verified: connected, disconnected, connected across connect / disconnect / reconnect.

Clean-up is the difference between a leak and a working app

Because ReactPy components live in long-lived server processes, a forgotten subscription is not a brief flash of stale data \u2014 it is a permanent leak that outlives every browser tab that opened it. Every subscribe, every timer, every held connection needs a clean-up function.

"},{"location":"reference/use-effect/#effects-and-the-request-lifecycle","title":"Effects and the request lifecycle","text":"

An effect runs in the connection's own task, after the render that scheduled it. It can call setters, and those schedule further renders. Two consequences:

  • An effect that awaits gives the event loop control. Long awaits belong in use_async_effect, which is built for it.
  • Setting state from an effect that depends on that same state loops forever. If an effect seems to spin, read what it sets and what it depends on.
"},{"location":"reference/use-effect/#reading-the-latest-state-inside-an-effect","title":"Reading the latest state inside an effect","text":"

An effect closes over the render that created it. If it then runs for a long time \u2014 an interval callback, a subscription handler \u2014 it can act on values that are no longer current. Two ways to handle it:

  • Put the value in the dependency list, so a change tears the effect down and starts a new one with fresh values.
  • Keep the value in a use_ref box and read ref.current when you need it. The box persists across renders, so readers see the newest value \u2014 as long as the write happens somewhere legal. Do not assign ref.current from the component body: a render-phase write is exactly the impurity Use Ref warns about, and ReactPy has no Strict Mode to catch it. Assign it where the value is already produced \u2014 the event handler that changed it \u2014 or in its own effect, which runs after the render.

If an effect reads state it does not declare, that is usually a sign the effect should be re-created when that state changes \u2014 reach for the dependency list first.

"},{"location":"reference/use-imperative-handle/","title":"Use imperative handle","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-insertion-effect/","title":"Use insertion effect","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-layout-effect/","title":"Use layout effect","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-location/","title":"Use Location","text":""},{"location":"reference/use-location/#overview","title":"Overview","text":"

use_location is a ReactPy hook that returns the URL the browser is currently showing.

location = use_location()\n

Your components run on the server and know nothing about the browser unless you ask. use_location is how you ask: it reports the page the visitor is looking at, so navigation, breadcrumbs, and query-driven views can be built from it.

"},{"location":"reference/use-location/#reference","title":"Reference","text":""},{"location":"reference/use-location/#use-location","title":"use_location()","text":"

Call it at the top level of a component. It takes no arguments.

from reactpy import use_location\n
"},{"location":"reference/use-location/#returns","title":"Returns","text":"

A reactpy.types.Location \u2014 a plain dataclass with two fields, both required:

  • path: The URL path, mirroring window.location.pathname. For example \"/reports/2024/q1\".
  • query_string: The query string, mirroring window.location.search \u2014 including the leading \"?\". For example \"?tab=2&user=ada\", or \"\" when there is no query.

It is mutable and compares by value. Import the type with from reactpy.types import Location; it is not exported from the reactpy namespace.

"},{"location":"reference/use-location/#caveats","title":"Caveats","text":"
  • The \"?\" is part of the value. parse_qs(location.query_string) mis-parses the first parameter, producing a key \"?tab\" instead of \"tab\". Strip it: parse_qs(location.query_string.lstrip(\"?\")). See the example below.
  • It is not the ASGI scope's path. use_scope()[\"path\"] is the ReactPy websocket endpoint \u2014 \"/reactpy/\" \u2014 for every component on every page. use_location().path is the page the visitor is on. Verified in Chrome: visiting /some/deep/page?tab=2&user=ada gives Location(path='/some/deep/page', query_string='?tab=2&user=ada') while the scope still says \"/reactpy/\".
  • The browser reports it, so it is untrusted. The value arrives as parameters on the websocket URL and ReactPy does not validate it against your routes. Treat it as input: match it, never interpolate it into a \"href\" or a filesystem path without checking.
  • It needs a live connection. Called in a test's plain Layout render, it raises RuntimeError: No backend established a connection.. See working without a connection.
  • It does not exist under client-side rendering. With ReactPyCsr, Python runs in the browser but no ReactPy connection object is established, so the hook raises the same RuntimeError. Verified in Chrome against a CSR app. Read window.location through JavaScript there instead.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return.
"},{"location":"reference/use-location/#usage","title":"Usage","text":""},{"location":"reference/use-location/#reading-query-parameters","title":"Reading query parameters","text":"

query_string arrives exactly as the browser shows it, which means the leading \"?\" is attached to the first parameter's name. That is the one detail that trips people up.

app.py Run
@component\ndef product_page():\n    location = use_location()\n\n    # `query_string` mirrors `window.location.search`, so it *keeps* the\n    # leading \"?\". Strip it first or the first parameter gets a mangled name:\n    # parse_qs(\"?tab=2&sort=price\") yields the key \"?tab\", not \"tab\".\n    params = parse_qs(location.query_string.lstrip(\"?\"))\n    tab = params.get(\"tab\", [\"overview\"])[0]\n    sort = params.get(\"sort\", [\"name\"])[0]\n\n    return html.div(\n        html.h1(f\"Products \u2014 {tab}\"),\n        html.p(f\"sorted by: {sort}\"),\n        html.p(f\"raw query string: {location.query_string!r}\"),\n        html.p(f\"path: {location.path}\"),\n    )\n
# TODO\n

With ?tab=sales&sort=price this renders \"Products \u2014 sales\", sorted by price. With no query string at all it falls back to the defaults.

"},{"location":"reference/use-location/#building-navigation-from-the-current-path","title":"Building navigation from the current path","text":"

Splitting the path gives you the trail from the site root to wherever the visitor is.

app.py Run
@component\ndef breadcrumb():\n    location = use_location()\n\n    # `path` mirrors `window.location.pathname`, so a component rendered on the\n    # ReactPy server can describe the page the browser is actually on -- useful\n    # for navigation that needs to know where it is.\n    parts = [part for part in location.path.split(\"/\") if part]\n\n    return html.nav(\n        html.a({\"href\": \"/\"}, \"home\"),\n        *[\n            html.span(\" / \", html.a({\"href\": \"/\" + \"/\".join(parts[: i + 1])}, part))\n            for i, part in enumerate(parts)\n        ],\n    )\n
# TODO\n

On /reports/2024/q1 this produces links to /reports, /reports/2024, and /reports/2024/q1. Build the URLs yourself rather than pasting path segments in raw if they can contain anything you did not choose.

"},{"location":"reference/use-location/#working-without-a-connection","title":"Working without a connection","text":"

A component that reads the location should say what it shows when there is none \u2014 a unit test, a reactpy_to_string call, or a CSR page all take this path.

@component\ndef page_title():\n    try:\n        location = use_location()\n    except RuntimeError:\n        return html.h1(\"(no browser yet)\")\n    return html.h1(f\"at {location.path}\")\n

Verified both ways through Layout: with no provider it renders the fallback; with one it renders the path.

"},{"location":"reference/use-location/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-location/#my-query-parameters-never-match","title":"My query parameters never match","text":"

The leading \"?\". parse_qs(\"?tab=2\") gives {\"?tab\": [\"2\"]}. Strip it before parsing.

"},{"location":"reference/use-location/#it-says-reactpy","title":"It says /reactpy/","text":"

You are reading use_scope()[\"path\"], which is the websocket endpoint. Use use_location().path for the page.

"},{"location":"reference/use-location/#it-raises-in-my-tests","title":"It raises in my tests","text":"

Expected: a bare Layout render has no connection. Either wrap the call as shown above, or inject one \u2014 see use_connection.

"},{"location":"reference/use-location/#it-raises-inside-a-reactpycsr-app","title":"It raises inside a ReactPyCsr app","text":"

Client-side rendering establishes no connection, so there is nothing to report. Read the URL from JavaScript instead, or move that component server-side.

"},{"location":"reference/use-memo/","title":"Use Memo","text":""},{"location":"reference/use-memo/#overview","title":"Overview","text":"

use_memo is a ReactPy hook that caches the result of a calculation, recomputing it only when one of its listed dependencies changes.

cached_value = use_memo(calculate_value, dependencies=[dep_1, dep_2])\n

Use it when a calculation is genuinely expensive and the inputs it depends on change less often than the component renders. It caches a value; it does not stop anything from rendering.

What use_memo does not do in ReactPy

It does not prevent a child component from re-rendering. ReactPy has no render bailout and no memo: a child's body runs whenever its parent re-renders, even with identical props, and even if you cache the child element itself with use_memo. Texts ported from React that use useMemo to \"skip re-rendering of components\" describe a mechanism that is not here. use_memo saves the computation, nothing else.

"},{"location":"reference/use-memo/#reference","title":"Reference","text":""},{"location":"reference/use-memo/#use-memo","title":"use_memo(function, dependencies)","text":"

Call use_memo at the top level of your component to cache a calculation.

from reactpy import use_memo\n
"},{"location":"reference/use-memo/#parameters","title":"Parameters","text":"
  • function: A function taking no arguments that returns the value you want. ReactPy calls it only when it must. It must be pure: same inputs, same output, no side effects.
  • dependencies: The values the calculation reads. ReactPy recomputes when any of them changes. It accepts three shapes:

    You pass Meaning dependencies=[a, b] Recompute when a or b changes. omitted (the default) ReactPy reads the function's closure and infers the free variables. dependencies=None Recompute on every render.
"},{"location":"reference/use-memo/#returns","title":"Returns","text":"

The current value \u2014 either freshly computed, or the one cached from the last render in which a dependency changed.

"},{"location":"reference/use-memo/#caveats","title":"Caveats","text":"
  • The decorated name holds the value, not a function. The decorator form is the call form under another name, and the call form returns the value -- so after decorating, my_value is the cached result, and calling it raises TypeError: 'int' object is not callable. To keep the name callable, give the inner function its own name and pass it to use_memo.
  • Caching lasts for the lifetime of the component. A cached value survives as long as the component stays mounted; there is no way to invalidate it from inside. Mounting the component under a new \"key\" throws it away, because that unmounts and remounts the component and every hook starts over.
  • You must list every value the function reads. ReactPy does not check, and does not warn. A missing dependency produces a silently stale value, not an error. See I removed a dependency and nothing complained.
  • Comparison is not plain identity. Dependencies go through reactpy.core.hooks.strictly_equal, which requires the two values to be the same type first, compares two locally-defined functions or lambdas by their code objects (so a fresh, structurally identical lambda counts as unchanged), and otherwise compares with ==, falling back to is only if that raises. Two practical consequences: a freshly built list or dict with equal contents does not force a recompute, and a value that changes type between renders \u2014 1 then 1.0, or 0 then False \u2014 always counts as changed.
  • Mutating a dependency in place defeats the cache. Change an object and pass the same object back, and it compares equal to itself, so the cached value is kept. Build a new object instead \u2014 see Updating Objects in State.
  • A ref never invalidates a memo. Writing to some_ref.current does not change how the ref compares, so listing a use_ref box in dependencies accomplishes nothing.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check; a hook that runs on some renders but not others produces a scrambled component and a cryptic IndexError.
"},{"location":"reference/use-memo/#usage","title":"Usage","text":""},{"location":"reference/use-memo/#skipping-an-expensive-calculation","title":"Skipping an expensive calculation","text":"

Give use_memo the function that does the work, and list what it reads. The leaderboard sorts seven rows here, which is nothing \u2014 but it is the same shape you would use for a sort over ten thousand, and the counter on screen shows exactly how often the sort runs.

Press Change highlight: the component re-renders, the sort does not run, and sorted 1 time(s) stays put. Press Everyone scores +1: players changes, so the sort runs again.

app.py Run
ROSTER = [\n    (\"ada\", 91),\n    (\"grace\", 120),\n    (\"alan\", 77),\n    (\"edsger\", 64),\n    (\"katherine\", 143),\n    (\"dorothy\", 88),\n    (\"margaret\", 155),\n]\n\nsorts = []  # demo-only: lets you *see* how often `rank` actually runs\n\n\n@component\ndef leaderboard():\n    players, set_players = use_state(\n        [{\"name\": name, \"score\": score} for name, score in ROSTER]\n    )\n    highlight, set_highlight = use_state(\"ada\")\n\n    def rank():\n        # The work you want to skip: sorting the whole table. Listing\n        # `dependencies=[players]` runs this only when the roster changes, so\n        # changing the highlight leaves the sorted list alone.\n        sorts.append(1)\n        return sorted(players, key=lambda player: -player[\"score\"])\n\n    ranked = use_memo(rank, dependencies=[players])\n\n    async def handle_highlight_click(event):\n        set_highlight(\"grace\" if highlight == \"ada\" else \"ada\")\n\n    async def handle_players_click(event):\n        set_players([{**player, \"score\": player[\"score\"] + 1} for player in players])\n\n    return html.div(\n        html.h1(\"Leaderboard\"),\n        html.ol([html.li(f\"{player['name']}: {player['score']}\") for player in ranked]),\n        html.p(f\"sorted {len(sorts)} time(s)\"),\n        html.p(f\"highlighted: {highlight}\"),\n        html.button(\n            {\"onClick\": handle_highlight_click},\n            \"Change highlight\",\n        ),\n        html.button(\n            {\"onClick\": handle_players_click},\n            \"Everyone scores +1\",\n        ),\n    )\n
# TODO\n

The counter (sorts) and both buttons exist only to make the caching observable. In real code you would keep the use_memo call and drop the rest.

"},{"location":"reference/use-memo/#how-expensive-is-expensive","title":"How expensive is \"expensive\"?","text":"

Enough to measure. Sorting a small list, building a dict from a handful of items, or formatting a string is not worth it \u2014 the hook has a cost of its own, and a dependency list you must keep correct is a cost too.

Reach for it when you recognize one of these:

  • A sort, filter, or search over a large collection.
  • Parsing: JSON, CSV, a template, a source file.
  • Building a structure that then gets walked repeatedly.
  • Anything you have already timed and found annoying.

If you have not measured it, prefer the plain calculation. Adding use_memo later is a one-line change precisely because the calculation is already in its own function.

"},{"location":"reference/use-memo/#should-you-add-use_memo-everywhere","title":"Should you add use_memo everywhere?","text":"

No. Each one is a dependency list you now have to maintain correctly, and a wrong list is a silent bug rather than a loud one. Memoize where you have measured, not everywhere.

"},{"location":"reference/use-memo/#omitting-the-dependency-list","title":"Omitting the dependency list","text":"

Leave out dependencies and ReactPy infers them from the free variables in the function's closure. This is a ReactPy feature with no React equivalent, and it removes the most common class of mistake, because a value the function reads is by definition a free variable.

The word count below reads text but not upper, so toggling the case re-renders the component without re-running the count. Watch counted 1 time(s).

app.py Run
counts = []  # demo-only: lets you *see* how often `count` actually runs\n\n\n@component\ndef word_count():\n    text, set_text = use_state(\"reactpy is a python ui library\")\n    upper, set_upper = use_state(False)\n\n    # No `dependencies` argument at all. ReactPy inspects the function's\n    # closure and infers the free variables it reads -- here that is `text`,\n    # but not `upper`, so toggling the case does not redo the count.\n    def count():\n        counts.append(1)\n        return len(text.split())\n\n    total = use_memo(count)\n\n    async def handle_upper_click(event):\n        set_upper(not upper)\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.h1(f\"{total} words\"),\n        html.p(text.upper() if upper else text),\n        html.p(f\"counted {len(counts)} time(s)\"),\n        html.button({\"onClick\": handle_upper_click}, \"Toggle case\"),\n        html.input(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n            }\n        ),\n    )\n
# TODO\n

Two catches. Inference only sees closure variables, so a module-level name, or anything reached through an object, is invisible to it. And inference is a convenience for a function defined inside the component \u2014 pass the list explicitly when the function comes from elsewhere.

"},{"location":"reference/use-memo/#recomputing-on-every-render","title":"Recomputing on every render","text":"

dependencies=None opts out of caching entirely: the function runs on every render. That is what you want when the calculation reads something no dependency list can express, such as a module-level mutable.

The label below reads config, which lives at module scope. Inference only sees closure variables, so it would pick up count and suffix and miss config entirely \u2014 the label would keep a stale prefix whenever config changed. dependencies=None sidesteps the question: label() runs each render.

app.py Run
# Module-level, so it is *not* a closure variable and ReactPy cannot infer it\n# as a dependency.\nconfig = {\"prefix\": \"count\"}\n\ncalls = []  # demo-only\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n    suffix, set_suffix = use_state(\"\")\n\n    def label():\n        # Reads `config`, which ReactPy cannot see as a dependency. With\n        # inferred (i.e. empty) dependencies this would be computed once and\n        # then never notice an edited `config`. `dependencies=None` says: this\n        # depends on the render itself, so recompute every time.\n        calls.append(1)\n        return f\"{config['prefix']}: {count}{suffix}\"\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_suffix_click(event):\n        set_suffix(\"!\")\n\n    return html.div(\n        html.h1(use_memo(label, dependencies=None)),\n        html.p(f\"label() ran {len(calls)} time(s)\"),\n        html.button({\"onClick\": handle_count_click}, \"Count +1\"),\n        html.button({\"onClick\": handle_suffix_click}, \"Set suffix\"),\n    )\n
# TODO\n
"},{"location":"reference/use-memo/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-memo/#my-memo-value-never-updates","title":"My memo value never updates","text":"

In order of likelihood:

  • You mutated a dependency instead of replacing it. Same object in, equal to itself, cached value kept. Build a new object.
  • You listed a ref as a dependency. Mutating some_ref.current never invalidates anything. Use state, or list the value itself.
  • The dependency compares equal to its replacement. Two [1, 2, 3] lists are equal, so rebuilding one each render changes nothing.
  • You left the value out of the list. See the next item.
"},{"location":"reference/use-memo/#i-removed-a-dependency-and-nothing-complained","title":"I removed a dependency and nothing complained","text":"

Because nothing checks. The example below omits tax_rate from the list, and it seems to work: press Add 10% tax twice and the rate climbs while the total stays frozen at whatever a 0% rate produced. Then press Add an item \u2014 a real dependency \u2014 and the total jumps to 12.00, silently incorporating both tax increases you could see never happened.

app.py Run
@component\ndef totals():\n    items, set_items = use_state([1, 2, 3])\n    tax_rate, set_tax_rate = use_state(0.0)\n\n    # `tax_rate` is read here but deliberately left out of `dependencies`.\n    # ReactPy does not complain. The cached total just keeps the rate from the\n    # render that last computed it, so \"Add 10% tax\" appears to do nothing --\n    # until something that *is* a dependency changes, and the stale number\n    # jumps to the right one all at once.\n    def grand_total():\n        return sum(items) * (1 + tax_rate)\n\n    total = use_memo(grand_total, dependencies=[items])\n\n    async def handle_tax_rate_click(event):\n        set_tax_rate(tax_rate + 0.1)\n\n    async def handle_items_click(event):\n        set_items([*items, len(items) + 1])\n\n    return html.div(\n        html.h1(f\"total: {total:.2f}\"),\n        html.p(f\"tax rate: {tax_rate:.0%}\"),\n        html.button({\"onClick\": handle_tax_rate_click}, \"Add 10% tax\"),\n        html.button(\n            {\"onClick\": handle_items_click},\n            \"Add an item\",\n        ),\n    )\n
# TODO\n

This is the failure mode to watch for: a stale value that eventually arrives, so it reads as lag rather than as a bug. List everything the function reads, or omit dependencies and let ReactPy infer them.

"},{"location":"reference/use-memo/#my-calculation-runs-on-every-render-anyway","title":"My calculation runs on every render anyway","text":"
  • You passed dependencies=None.
  • A dependency really is new each render \u2014 commonly an object constructed in the component body above the use_memo call.
  • A dependency is an object whose == is identity-based and which you rebuild each time.
"},{"location":"reference/use-memo/#i-wanted-to-stop-a-child-from-re-rendering","title":"I wanted to stop a child from re-rendering","text":"

use_memo cannot do this in ReactPy, and caching the child element does not help either \u2014 a cached element is still re-rendered. ReactPy re-renders children unconditionally. If a child is genuinely expensive, the levers are to make its own calculations cheap (memoize inside it) or to avoid rendering it at all behind a conditional.

"},{"location":"reference/use-reducer/","title":"Use Reducer","text":""},{"location":"reference/use-reducer/#overview","title":"Overview","text":"

use_reducer is a ReactPy hook that holds state behind a function: you send an action, and a reducer turns the current state plus that action into the next state.

state, dispatch = use_reducer(reducer, initial_value)\n

Reach for it when a component's updates are more than \"set this to that\" \u2014 several fields that change together, transitions that depend on what is already there, or update logic you want out of the component so it can be tested as a plain function.

"},{"location":"reference/use-reducer/#reference","title":"Reference","text":""},{"location":"reference/use-reducer/#use-reducer","title":"use_reducer(reducer, initial_value)","text":"

Call use_reducer at the top level of your component.

from reactpy import use_reducer\n
"},{"location":"reference/use-reducer/#parameters","title":"Parameters","text":"
  • reducer: (state, action) -> new_state. A pure function: given the same two arguments it returns the same result, and it does not touch anything outside them. It must return a new object rather than modifying state \u2014 see the reducer must not mutate.
  • initial_value: The state to start with. Pass a function taking no arguments to build it lazily; ReactPy calls it on the first render only. See avoiding recreating the initial state.
"},{"location":"reference/use-reducer/#returns","title":"Returns","text":"

A two-item tuple, normally unpacked:

  • state: The current state, as of the start of this render.
  • dispatch: A function that takes an action and schedules the update. The same object on every render \u2014 verified, one dispatcher across four renders \u2014 so it is safe to pass into a child or a dependency list.
"},{"location":"reference/use-reducer/#caveats","title":"Caveats","text":"
  • Define the reducer at module level. ReactPy builds the dispatcher once, on the first render, and the dispatcher closes over the reducer you passed then. A reducer defined inside the component is a new function object each render and ReactPy keeps using the first one, so any value it closes over is frozen at its first-render value. Measured: four renders, four distinct reducer objects passed in, and the state kept reporting seen_n=0. This differs from React, which re-reads the reducer every render. Anything the reducer needs must arrive in state or action. See The reducer never changes.
  • use_reducer is use_state underneath. The dispatcher calls the state setter with lambda last_state: reducer(last_state, action), so every rule from use_state applies here \u2014 replaced, never merged; equal values schedule no render; mutations vanish silently. See The reducer must not mutate.
  • The reducer runs eagerly, inside dispatch \u2014 not later during the render. Verified by ordering statements around the call. A reducer that raises is swallowed the same way any event-handler error is: logged on the server as Failed to execute event handler, with nothing in the browser console.
  • Actions are yours; ReactPy never inspects them. A dict with a \"type\" key is convention, not a requirement \u2014 you can dispatch a string, a number, or a tuple.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check; a hook that runs on some renders but not others produces a scrambled component and a cryptic IndexError.
"},{"location":"reference/use-reducer/#usage","title":"Usage","text":""},{"location":"reference/use-reducer/#managing-complex-state-with-a-reducer","title":"Managing complex state with a reducer","text":"

One reducer holds the whole shape, and each behaviour is a branch chosen by the action. Because it is an ordinary function of two arguments, you can test it without rendering anything.

Press Add task twice, task 1, then Clear done, and watch the reducer's branches run:

app.py Run
def todos_reducer(state, action):\n    \"\"\"One function per behaviour, chosen by the action's type.\n\n    Defined at module level on purpose: ReactPy builds the dispatcher once, on\n    the first render, and keeps that reducer -- a reducer defined inside the\n    component would keep its first-render closure forever.\n\n    Every branch returns a NEW dict. Returning the mutated `state` would be\n    dropped without a word.\n    \"\"\"\n    if action[\"type\"] == \"add\":\n        return {\n            **state,\n            \"items\": [*state[\"items\"], {\"text\": action[\"text\"], \"done\": False}],\n        }\n    if action[\"type\"] == \"toggle\":\n        items = [\n            {**item, \"done\": not item[\"done\"]} if i == action[\"index\"] else item\n            for i, item in enumerate(state[\"items\"])\n        ]\n        return {**state, \"items\": items}\n    if action[\"type\"] == \"clear_done\":\n        return {**state, \"items\": [i for i in state[\"items\"] if not i[\"done\"]]}\n    return state\n\n\n@component\ndef todo_list():\n    state, dispatch = use_reducer(todos_reducer, {\"items\": []})\n\n    async def handle_add_click(event):\n        dispatch({\"type\": \"add\", \"text\": f\"task {len(state['items']) + 1}\"})\n\n    async def handle_clear_done_click(event):\n        dispatch({\"type\": \"clear_done\"})\n\n    def make_toggle(index):\n        # A factory rather than a bound-argument lambda: a handler must be a\n        # coroutine, and a coroutine cannot be a lambda.\n        async def toggle(event):\n            dispatch({\"type\": \"toggle\", \"index\": index})\n\n        return toggle\n\n    return html.div(\n        html.h1(f\"To-do ({sum(1 for i in state['items'] if not i['done'])} left)\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": i},\n                    html.button(\n                        {\"onClick\": make_toggle(i)},\n                        state[\"items\"][i][\"text\"],\n                    ),\n                )\n                for i in range(len(state[\"items\"]))\n            ]\n        ),\n        html.button(\n            {\"onClick\": handle_add_click},\n            \"Add task\",\n        ),\n        html.button({\"onClick\": handle_clear_done_click}, \"Clear done\"),\n    )\n
# TODO\n

Three things to notice in todos_reducer. It is at module level, so the closure trap above never arises. Every branch builds a new dict with {**state, ...} \u2014 none of them mutate. And the fallback return state makes an unhandled action a deliberate no-op rather than a crash.

"},{"location":"reference/use-reducer/#the-reducer-must-not-mutate","title":"The reducer must not mutate","text":"

This is the failure mode most likely to cost you an afternoon, and it is silent. A reducer that modifies state and returns it hands back the very object ReactPy already holds; the comparison finds them equal and schedules nothing. No error, no warning, nothing in the console \u2014 the button just stops working while your data quietly changes underneath.

app.py Run
def counter_reducer(state, action):\n    # Always return a NEW object. `use_reducer` is built on `use_state`, which\n    # compares what you return against what it already holds and schedules no\n    # render when the two are equal. Mutating `state` here and returning it\n    # would update the data, schedule nothing, and fail with no error and\n    # nothing in the browser console.\n    return {**state, \"count\": state[\"count\"] + 1}\n\n\n@component\ndef counter():\n    state, dispatch = use_reducer(counter_reducer, {\"count\": 0})\n\n    async def handle_click(event):\n        dispatch({\"type\": \"inc\"})\n\n    return html.div(\n        html.h1(state[\"count\"]),\n        html.button({\"onClick\": handle_click}, \"Add 1\"),\n    )\n
# TODO\n

Return {**state, \"count\": state[\"count\"] + 1}, not state[\"count\"] += 1; return state. The same applies one level down: {**state, \"items\": state[\"items\"] + [new]}, never state[\"items\"].append(new). See Updating Objects in State and Updating Arrays in State.

"},{"location":"reference/use-reducer/#the-reducer-never-changes","title":"The reducer never changes","text":"

Because the first reducer is the only one that ever runs, a reducer that closes over component state reads a value frozen at the first render \u2014 permanently, with no warning.

Run this, press Add step twice, then Bump step, then Add step again:

app.py Run
@component\ndef stepper():\n    step, set_step = use_state(1)\n\n    def reducer(state, action):\n        # BUG: this reducer closes over `step`, but ReactPy builds the\n        # dispatcher once, on the first render, and keeps that reducer\n        # forever. Bump `step` to 3 and this keeps adding 1.\n        return {**state, \"total\": state[\"total\"] + step}\n\n    state, dispatch = use_reducer(reducer, {\"total\": 0})\n\n    async def handle_click(event):\n        dispatch({\"type\": \"add\"})\n\n    async def handle_step_click(event):\n        set_step(step + 1)\n\n    return html.div(\n        html.p(f\"total: {state['total']}\"),\n        html.p(f\"step: {step}\"),\n        html.button({\"onClick\": handle_click}, \"Add step\"),\n        html.button({\"onClick\": handle_step_click}, \"Bump step\"),\n    )\n
# TODO\n

step goes to 2 on screen, but the total keeps rising by 1. The reducer still holds the step from the render that created it. Two ways out: keep the reducer at module level and put step in the state, or send it in the action \u2014 dispatch({\"type\": \"add\", \"amount\": step}).

"},{"location":"reference/use-reducer/#avoiding-recreating-the-initial-state","title":"Avoiding recreating the initial state","text":"

Pass a function and ReactPy calls it once; pass a call and it runs on every render, with every result but the first discarded. Measured across four renders: 1 call versus 4. The counter on screen reports the total, and it stays at 1 no matter what you press.

app.py Run
loads = []  # demo-only: shows how often the initializer actually runs\n\n\ndef load_config():\n    \"\"\"Stand-in for anything slow: reading a file, querying a database.\"\"\"\n    loads.append(1)\n    return {\"rows\": [{\"name\": n} for n in (\"alpha\", \"beta\", \"gamma\")], \"selected\": None}\n\n\ndef reducer(state, action):\n    if action[\"type\"] == \"select\":\n        return {**state, \"selected\": action[\"name\"]}\n    return state\n\n\n@component\ndef report():\n    # Pass a function as the initial state and ReactPy calls it once, on the\n    # first render. Passing `load_config()` instead would call it on every\n    # render and throw every result but the first away.\n    state, dispatch = use_reducer(reducer, load_config)\n\n    async def handle_click(event):\n        dispatch({\"type\": \"select\", \"name\": state[\"rows\"][0][\"name\"]})\n\n    return html.div(\n        html.h1(f\"{len(state['rows'])} rows\"),\n        html.p(f\"selected: {state['selected'] or '-'}\"),\n        html.p(f\"initializer ran {len(loads)} time(s)\"),\n        html.button(\n            {\"onClick\": handle_click},\n            \"Select first\",\n        ),\n    )\n
# TODO\n
"},{"location":"reference/use-reducer/#use_reducer-or-use_state","title":"use_reducer or use_state?","text":"

Prefer use_state. It is shorter and there is no reducer-closure rule to remember. Switch when one of these is true:

  • Several pieces of state always change together, and updating them through separate setters lets the component render in between with an inconsistent mix.
  • The next state depends on the current one in a way you keep writing wrong.
  • The update logic has grown branches you want to name, or unit-test as a plain function.
  • Different parts of a tree need to trigger the same transitions \u2014 passing dispatch around is tidier than passing several setters.
"},{"location":"reference/use-reducer/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-reducer/#my-dispatch-does-nothing","title":"My dispatch does nothing","text":"

Check the reducer's return value first \u2014 this is almost always a mutation. return state after modifying state schedules nothing. Also check for a branch that returns the state unchanged: an action whose \"type\" no branch matches falls through to your return state, which is correctly a no-op.

"},{"location":"reference/use-reducer/#my-reducer-sees-an-old-value-of-some-state","title":"My reducer sees an old value of some state","text":"

You defined it inside the component, so ReactPy is running the first-render version with the first-render closure. Move it to module level and pass the value through state or the action.

"},{"location":"reference/use-reducer/#the-initial-value-seems-to-be-rebuilt-every-render","title":"The initial value seems to be rebuilt every render","text":"

You passed build_initial_state() instead of build_initial_state. ReactPy needs the function itself to know it may skip the work.

"},{"location":"reference/use-reducer/#my-reducer-raised-and-nothing-said-so","title":"My reducer raised and nothing said so","text":"

Expected. Reducer errors land in the server log as Failed to execute event handler and are otherwise swallowed; the state is left unassigned. Look at the server output, not the browser console.

"},{"location":"reference/use-reducer/#i-want-an-updater-function-instead-of-an-action","title":"I want an updater function instead of an action","text":"

That is what use_state gives you: set_value(lambda previous: ...). If that is all you need, use_reducer is ceremony you do not owe yourself.

"},{"location":"reference/use-ref/","title":"Use Ref","text":""},{"location":"reference/use-ref/#overview","title":"Overview","text":"

use_ref is a ReactPy hook that gives you a mutable box which survives re-renders. Writing to it never causes a render.

value_box = use_ref(initial_value)\n

A ref is for values your component uses but never renders. It is a plain Python object holding one field, so reading and writing it is just attribute access \u2014 no setter, no scheduling, no re-render.

There is no DOM ref in ReactPy

React's useRef is often used to grab a DOM node \u2014 inputRef.current.focus(), videoRef.current.play(), myDiv.current.scrollIntoView(). That use does not exist in ReactPy. Your components run on the server, where there is no DOM, and ReactPy has no mechanism for handing a browser node to Python. There is no ref_current attribute, no callback ref, nothing to attach a node to. Anything you write as a \"ref_...\" attribute is an unknown attribute the client silently ignores.

Express browser behavior through state and ordinary attributes instead: {\"autoFocus\": True}, {\"value\": text}, {\"disabled\": not ready}. If you need genuinely imperative browser code, that belongs in a client-side app, where your Python runs in the browser.

"},{"location":"reference/use-ref/#reference","title":"Reference","text":""},{"location":"reference/use-ref/#use-ref","title":"use_ref(initial_value)","text":"

Call use_ref at the top level of your component to declare a value box.

from reactpy import use_ref\n
"},{"location":"reference/use-ref/#parameters","title":"Parameters","text":"
  • initial_value: The value the box starts with. It can be any Python value, including a mutable one.
"},{"location":"reference/use-ref/#returns","title":"Returns","text":"

A reactpy.utils.Ref object \u2014 the same object on every render of that component. It has two members:

  • current: The value. Read and write it directly: value_box.current = new_value. Reading it does not schedule a render.
  • set_current(new_value): Assigns current and returns the value it replaced. Because it is a call rather than an assignment, it fits where a statement cannot go \u2014 inside a comprehension, or as part of a larger expression.
"},{"location":"reference/use-ref/#caveats","title":"Caveats","text":"
  • initial_value is evaluated on every render. use_ref(build_something_expensive()) calls build_something_expensive() each time the component renders, then throws all but the first result away. Verified: four renders, four calls. To build something once, start empty and fill it in:

    index = use_ref(None)\nif index.current is None:\n    index.current = build_something_expensive()\n
  • Writing to a ref never triggers a render. That is the point, and also the trap: if the value is something the screen shows, it will not update. See Ref versus state.

  • Mutating a ref does not invalidate anything. Dependency comparison treats the box as unchanged no matter what you write into it, because it compares equal to itself. Listing a ref in a dependencies list is therefore useless \u2014 the effect or memo will never re-run because of it. Verified: an effect with dependencies=[box] ran once while its box.current was written to three times.
  • A ref is unhashable. hash(use_ref(0)) raises TypeError, so it cannot be a dict key or a set member. Its == compares contents: two separate Ref(0) boxes are equal.
  • Render value_box.current, not the box. html.p(my_ref) does not raise; it renders the repr, so the reader sees Ref(0) on the page. Passing a ref as an attribute value fails at serialization instead.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check: shuffling which hooks run between renders yields a scrambled component and a cryptic IndexError, not a helpful message.
  • ReactPy has no Strict Mode, so nothing runs your component twice to catch impure code. A ref written to during rendering is exactly as unsafe here as in React, and nothing will warn you. See Keeping Components Pure.
"},{"location":"reference/use-ref/#usage","title":"Usage","text":""},{"location":"reference/use-ref/#ref-versus-state","title":"Ref versus state","text":"

The question is not \"which is cheaper\" but who needs to know. If changing the value must update the screen, it is state. If only the code needs it, it is a ref.

The two counters below start together and then drift apart, because only one of them causes a render. The ref counter's new value sits in the box unseen until something else renders the component.

app.py Run
@component\ndef counter():\n    # Two buttons, one counter each. Only the state one causes a render, so the\n    # numbers only come back into sync on a render triggered by something else.\n    state_count, set_state_count = use_state(0)\n    ref_count = use_ref(0)\n\n    async def bump_state(event=None):\n        set_state_count(state_count + 1)\n\n    async def bump_ref(event=None):\n        ref_count.current += 1  # writes in place: no render is scheduled\n\n    return html.div(\n        html.p(f\"state: {state_count}\"),\n        html.p(f\"ref: {ref_count.current}\"),\n        html.button({\"onClick\": bump_state}, \"Increment state\"),\n        html.button({\"onClick\": bump_ref}, \"Increment ref\"),\n    )\n
# TODO\n

The usual split:

You need... Use A value the UI renders use_state A value only handlers or effects read use_ref A value that drives the UI and is written by a long-lived loop Both: state for the UI, ref for the loop Something built once, then read-only use_ref, with the guard A mutable value the UI renders use_state \u2014 replace the object instead of mutating it; see Updating Objects in State"},{"location":"reference/use-ref/#remembering-a-value-built-once","title":"Remembering a value built once","text":"

Because the box is the same object across renders, it is the natural place for something expensive to construct and cheap to read: a parsed file, an index, a client object. Start it empty so the expensive call is not repeated on every render.

app.py Run
def build_word_index():\n    \"\"\"Stand-in for something slow: reading a file, opening a connection, etc.\"\"\"\n    return {word: len(word) for word in \"react py is a python ui library\".split()}\n\n\n@component\ndef word_counter():\n    # Built once, on the first render, and then reused for the life of the\n    # component -- no `dependencies`, no invalidation, nothing to keep in sync.\n    index: Ref[dict[str, int] | None] = use_ref(None)\n    if index.current is None:\n        index.current = build_word_index()\n\n    return html.div(\n        html.h1(\"Word lengths\"),\n        html.ul(\n            [\n                html.li(f\"{word}: {length}\")\n                for word, length in sorted(index.current.items())\n            ]\n        ),\n    )\n
# TODO\n

If the value should be rebuilt when some input changes, a ref is the wrong tool \u2014 that is what a dependencies list is for. See use_memo.

"},{"location":"reference/use-ref/#sharing-a-value-with-a-long-lived-effect","title":"Sharing a value with a long-lived effect","text":"

An use_async_effect loop keeps running across renders, so it closes over the state values from the render that started it. A ref sidesteps this: it is the same object every render, so the loop reads whatever the handlers wrote after it started.

The stopwatch below keeps the start timestamp in a ref. The ticking effect reads it and the buttons write it, none of which needs a render of its own. The value the screen shows, elapsed, is state.

app.py Run
@component\ndef stopwatch():\n    # What the UI renders lives in state. What only the handlers and the\n    # ticking effect read -- and which must never trigger a render of its own\n    # -- lives in a ref.\n    elapsed, set_elapsed = use_state(0.0)\n    running, set_running = use_state(False)\n    started_at: Ref[float | None] = use_ref(None)\n\n    async def tick():\n        # The ref is the same object on every render, so this long-lived loop\n        # always sees whatever the button handlers wrote into it.\n        while started_at.current is not None:\n            await asyncio.sleep(0.1)\n            set_elapsed(time.monotonic() - started_at.current)\n\n    use_async_effect(tick, dependencies=[running])\n\n    async def start(event=None):\n        started_at.current = time.monotonic()\n        set_running(True)\n\n    async def stop(event=None):\n        if started_at.current is not None:\n            # Write the exact final value; the ticking effect only samples\n            # every 0.1s, so relying on it alone would leave the display\n            # slightly behind.\n            set_elapsed(time.monotonic() - started_at.current)\n            started_at.current = None\n        set_running(False)\n\n    async def reset(event=None):\n        started_at.current = None\n        set_running(False)\n        set_elapsed(0.0)\n\n    return html.div(\n        html.h1(f\"{elapsed:.1f}s\"),\n        html.button(\n            {\"onClick\": stop if running else start},\n            \"Stop\" if running else \"Start\",\n        ),\n        html.button({\"onClick\": reset}, \"Reset\"),\n    )\n
# TODO\n
"},{"location":"reference/use-ref/#writing-where-an-expression-is-required","title":"Writing where an expression is required","text":"

box.current = value is an assignment, which is a statement, so it cannot appear where an expression is required \u2014 inside a comprehension, or as a value. set_current is the call-shaped form of the same write, and it hands back what you replaced.

app.py Run
@component\ndef theme_picker():\n    # `set_current` is the call-shaped version of `ref.current = value`, and it\n    # hands back the value it replaced. The return value is what makes the swap\n    # below one expression: an assignment cannot be an argument to set_theme().\n    previous = use_ref(\"dark\")\n    theme, set_theme = use_state(\"light\")\n\n    def pick(name):\n        # A factory: it returns a fresh coroutine per button, so each button\n        # gets its own handler and the theme it belongs to is bound by closure.\n        async def choose_theme(event):\n            previous.set_current(theme)  # record the theme being left\n            set_theme(name)\n\n        return choose_theme\n\n    async def swap_back(event):\n        set_theme(previous.set_current(theme))\n\n    return html.div(\n        html.h1(f\"{theme} theme\"),\n        html.p(f\"you were just using: {previous.current}\"),\n        html.button({\"onClick\": swap_back}, \"Swap back\"),\n        html.button({\"onClick\": pick(\"light\")}, \"Light\"),\n        html.button({\"onClick\": pick(\"dark\")}, \"Dark\"),\n    )\n
# TODO\n

pick is a factory: it returns a fresh coroutine per button, so each button gets its own handler and the theme it belongs to is bound by the closure. swap_back is where set_current earns its place \u2014 set_theme(previous.set_current(theme)) exchanges the two in one expression, which an assignment could not do because an assignment is not a value. The re-render still comes from set_theme, not from the write to the ref.

"},{"location":"reference/use-ref/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-ref/#my-ref-value-is-stale","title":"My ref value is stale","text":"

Check which of these you did:

  • You rendered the box, not its contents. html.p(my_ref) prints Ref(...). Use my_ref.current.
  • You expected the write to re-render. A ref never schedules a render. If the screen must change, that value is state.
  • You listed the ref as a dependency. Mutating current does not invalidate effects or memos \u2014 the box always compares equal to itself. Put the value in the dependency list, or use state.
  • You read a ref through an old closure. Inside a long-lived effect, re-read box.current on each pass instead of binding it to a local once.
"},{"location":"reference/use-ref/#my-expensive-setup-runs-on-every-render","title":"My expensive setup runs on every render","text":"

You passed it as the argument: use_ref(build()) calls build() every render. Move it behind a guard:

box = use_ref(None)\nif box.current is None:\n    box.current = build()\n
"},{"location":"reference/use-ref/#i-need-to-focus-an-input-or-scroll-to-an-element","title":"I need to focus an input, or scroll to an element","text":"

There is no way to do this from a ReactPy server component, and no \"ref_current\" attribute will add one. Use the declarative attributes the browser already understands \u2014 \"autoFocus\", \"checked\", \"value\", \"disabled\" \u2014 and reach for a client-side app if you truly need imperative browser access.

"},{"location":"reference/use-scope/","title":"Use Scope","text":""},{"location":"reference/use-scope/#overview","title":"Overview","text":"

use_scope is a ReactPy hook that returns the ASGI connection scope behind the current websocket.

scope = use_scope()\n

Use it for what the server knows about the connection: who the peer is, which headers arrived, which server accepted the request. For the page the visitor is looking at, use use_location instead.

"},{"location":"reference/use-scope/#reference","title":"Reference","text":""},{"location":"reference/use-scope/#use-scope","title":"use_scope()","text":"

Call it at the top level of a component. It takes no arguments.

from reactpy import use_scope\n
"},{"location":"reference/use-scope/#returns","title":"Returns","text":"

The dict at use_connection().scope \u2014 the raw ASGI scope, with the same shape as any other ASGI app's scope. It is a live dictionary: nothing copies it for you.

Read ASGI: Websockets for the specification; the keys below are what uvicorn's websocket implementation actually delivered, captured from a live Chrome session against ReactPy:

Key What it holds \"type\" \"websocket\" for these connections. \"path\" The ReactPy websocket endpoint, e.g. \"/reactpy/\". Not the page. \"headers\" A list of (bytes, bytes) pairs, lower-cased by the server. \"client\" (ip, port) of the peer, or None if unknown. \"server\" (host, port) that accepted the connection. \"scheme\" \"ws\" or \"wss\". \"query_string\" The websocket URL's query string, as bytes \u2014 this carries ReactPy's own path/qs parameters. \"raw_path\" The request path, as bytes, un-decoded. \"root_path\" The ASGI root path, normally \"\". \"http_version\" e.g. \"1.1\". \"asgi\" {\"version\": ..., \"spec_version\": ...}. \"subprotocols\" Requested websocket subprotocols, usually []. \"state\" A dict shared across the connection's tasks; empty unless something put values in it. \"extensions\" Server-specific extras, e.g. {\"websocket.http.response\": {}} under uvicorn.

Which extras appear depends on the server: \"state\" and that \"extensions\" entry are uvicorn/websockets additions. Read them defensively, with scope.get(...).

"},{"location":"reference/use-scope/#caveats","title":"Caveats","text":"
  • \"path\" is not the page. For every component in the app it is the websocket endpoint \u2014 \"/reactpy/\" under the default path_prefix. Verified: on the page /some/deep/page?tab=2&user=ada, the scope still says \"/reactpy/\". Use use_location().path for the page.
  • Headers are bytes. Both halves of each pair are bytes and lower-cased, so compare against b\"user-agent\" and .decode() the value before using it. There is no dict of headers; see the example.
  • \"query_string\" is the websocket's, not the page's. It contains ReactPy's own path= and qs= parameters. Parse the page's query with use_location().
  • None is possible. \"client\" can be absent or None behind a proxy or in-process transport. scope[\"client\"][0] will raise on None; check first.
  • It needs a live connection. In a plain Layout render it raises RuntimeError: No backend established a connection.
  • It does not exist under client-side rendering. With ReactPyCsr there is no connection object, so the hook raises the same RuntimeError.
  • Like every hook, call it only at the top level.
"},{"location":"reference/use-scope/#usage","title":"Usage","text":""},{"location":"reference/use-scope/#reading-the-peer-and-a-header","title":"Reading the peer and a header","text":"app.py Run
def header(scope, name):\n    \"\"\"Read one request header out of an ASGI scope.\n\n    Headers arrive as a list of ``(bytes, bytes)`` pairs, lower-cased by the\n    server, so both halves need decoding before you can compare them.\n    \"\"\"\n    wanted = name.lower().encode()\n    for key, value in scope.get(\"headers\", []):\n        if key == wanted:\n            return value.decode()\n    return None\n\n\n@component\ndef visitor():\n    scope = use_scope()\n    # Note this is the *websocket* scope. Its \"path\" is the ReactPy websocket\n    # path, not the page the visitor is on -- use use_location() for that.\n    # `client` is legitimately absent or None behind some proxies, so read it\n    # through a local and check it rather than subscripting the scope directly.\n    client = scope.get(\"client\")\n    peer = client[0] if client else \"unknown\"\n    return html.div(\n        html.h1(f\"Hello, {peer}\"),\n        html.p(f\"browser: {header(scope, 'user-agent') or 'unknown'}\"),\n        html.p(f\"connection type: {scope['type']}\"),\n    )\n
# TODO\n

header() exists because the ASGI scope hands you raw bytes: it lower-cases the name, matches on bytes, and decodes the value. The component reads scope[\"client\"] defensively because a proxy can leave it unset.

"},{"location":"reference/use-scope/#what-belongs-here-and-what-does-not","title":"What belongs here and what does not","text":"You want Use The visitor's IP address use_scope()[\"client\"] A request header use_scope()[\"headers\"] The page the visitor is on use_location The query string use_location The object mediating the connection use_connection"},{"location":"reference/use-scope/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-scope/#my-header-lookups-always-return-none","title":"My header lookups always return None","text":"

You compared against a str. The keys are bytes and lower-cased: (b\"user-agent\", b\"Mozilla/5.0 ...\").

"},{"location":"reference/use-scope/#it-raises-in-my-tests","title":"It raises in my tests","text":"

A bare Layout render establishes no connection. Wrap the call in try/except RuntimeError, or inject a connection \u2014 see use_connection.

"},{"location":"reference/use-scope/#every-page-shows-reactpy","title":"Every page shows /reactpy/","text":"

That is the scope's \"path\": the websocket endpoint, identical for all components. Use use_location().path.

"},{"location":"reference/use-scope/#i-need-the-request-path-before-the-websocket-opens","title":"I need the request path before the websocket opens","text":"

use_scope only exists inside a component, which means inside an established websocket. For pre-connection decisions, handle it in ASGI middleware in front of the ReactPy app \u2014 see ReactPyMiddleware.

"},{"location":"reference/use-state/","title":"Use State","text":""},{"location":"reference/use-state/#overview","title":"Overview","text":"

use_state is a ReactPy hook that lets you add a state variable to your component.

value, set_value = use_state(initial_value)\n
"},{"location":"reference/use-state/#reference","title":"Reference","text":""},{"location":"reference/use-state/#use_stateinitial_value","title":"use_state(initial_value)","text":"

Call use_state at the top level of your component to declare a state variable.

from reactpy import use_state\n
"},{"location":"reference/use-state/#parameters","title":"Parameters","text":"
  • initial_value: The value you want the state to start with. It can be any Python value. Alternatively, pass a function taking no arguments and returning the initial value; ReactPy calls it on the first render only, which avoids rebuilding an expensive value on every render. See avoiding recreating the initial state.
"},{"location":"reference/use-state/#returns","title":"Returns","text":"

use_state returns a reactpy.types.State \u2014 a named tuple of two items, which is normally unpacked:

  • value: The current state, as of the start of this render.
  • set_value: A function that updates the state and schedules a re-render. See set_value.

Because State is a named tuple you can also reach the halves explicitly (state.value, state.set_value), but unpacking is the convention.

"},{"location":"reference/use-state/#caveats","title":"Caveats","text":"
  • use_state is a hook, so call it only at the top level of your component or of your own hooks \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check for this: changing which hooks run between renders produces a scrambled component and a cryptic IndexError: list index out of range rather than a helpful message. If you need conditional state, extract a component.
  • initial_value is used only on the first render. Later changes to the expression are ignored \u2014 see resetting state with a key.
  • Do not pass a function you intend to store. A callable initial_value is treated as an initializer and called. use_state(lambda value: value * 2) does not store the lambda; ReactPy calls it with no arguments and the component fails. To keep a function in state, wrap it (use_state(lambda: my_function)) or store it in a use_ref.
  • Unlike React, ReactPy has no Strict Mode, so your initializer is called exactly once \u2014 verified: one call across four renders. Nothing will run it twice to expose impurity for you, so test purity yourself if it matters.
"},{"location":"reference/use-state/#set-value","title":"set_value(next_value)","text":"

The set_value function returned by use_state updates the state and schedules a re-render.

"},{"location":"reference/use-state/#parameters_1","title":"Parameters","text":"
  • next_value: The value you want the state to become. Any Python value, or a function taking one argument and returning the next state. That function receives the current value, runs eagerly inside the setter, and must be pure. See updating state based on the previous state.
"},{"location":"reference/use-state/#returns_1","title":"Returns","text":"

None.

"},{"location":"reference/use-state/#caveats_1","title":"Caveats","text":"
  • Setting state does not change the variable you already have in this render \u2014 it schedules a new one. See State as a Snapshot.
  • Setting state to a value equal to the current one does not re-render. The comparison is core/hooks.py, strictly_equal \u2014 and it is not an identity check: the two values must share a type, two locally-defined functions or lambdas are compared by their code, and everything else is compared with ==, falling back to identity only if that raises. So a brand-new object is dropped too whenever it merely compares equal: setting [1, 2, 3] while the state is an equal list renders nothing, and the same goes for any class whose __eq__ matches. Only values with identity-based __eq__ \u2014 plain class instances, say \u2014 re-render when you replace them.
  • set_value replaces the state; it never merges a partial object into it. Verified: setting {\"a\": 9} over {\"a\": 1, \"b\": 2} yields {\"a\": 9}. Update objects with set_value({**value, \"k\": v}) \u2014 see Updating Objects in State.
  • Mutating the current value and passing it back does nothing. The comparison finds the value equal to itself, so no render is scheduled \u2014 and there is no error, no warning, and nothing in the console. set_value(items.append(x)) also stores None, because most mutating list methods return None.
  • If the updater function raises, the error is logged on the server as Failed to execute event handler and swallowed; the state is left unassigned and the UI simply stops updating. Check the server log, not the browser console.
  • set_value is a stable object: the same callable is returned on every render of that component. Verified across four renders. That makes it safe to put in a dependency list or pass to a child without causing extra renders.
"},{"location":"reference/use-state/#usage","title":"Usage","text":""},{"location":"reference/use-state/#adding-state-to-a-component","title":"Adding state to a component","text":"

Call use_state at the top level of your component, and use the returned value while rendering:

app.py Run
@component\ndef counter():\n    step, set_step = use_state(1)\n\n    async def handle_add(event):\n        set_step(step + 1)\n\n    return html.div(\n        html.h1(step),\n        html.button({\"onClick\": handle_add}, \"Add 1\"),\n    )\n
# TODO\n

Each state variable gets its own setter, so prefer several narrow variables over one crowded object:

first_name, set_first_name = use_state(\"Ada\")\nlast_name, set_last_name = use_state(\"Lovelace\")\nage, set_age = use_state(42)\n
"},{"location":"reference/use-state/#counter-number","title":"Counter (number)","text":"

To update a number based on its current value, use a function:

async def handle_add(event):\n    set_step(lambda step: step + 1)\n

Or read the value from this render directly when you don't need the latest: set_step(step + 1).

"},{"location":"reference/use-state/#text-field-string","title":"Text field (string)","text":"

A controlled input reads its \"value\" from state and writes back through \"onChange\":

app.py Run
@component\ndef text_field():\n    first_name, set_first_name = use_state(\"Ada\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": first_name,\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.h1(f\"Hello, {first_name}!\"),\n    )\n
# TODO\n

Type into the field and the heading follows. Note the key names: \"value\" and \"onChange\" are DOM attribute names inside the attributes dictionary, while the Python names around them are snake_case.

Reading which field an event came from is possible \u2014 await event.target.name asks the page for it \u2014 but it costs a round trip, so when the handler already knows which field it belongs to, bind that in Python instead: a named handler per field, a closure, or a functools.partial. See Responding to Events.

"},{"location":"reference/use-state/#checkbox-boolean","title":"Checkbox (boolean)","text":"

await event.target.checked tells you whether the box is ticked:

app.py Run
@component\ndef checkbox():\n    enabled, set_enabled = use_state(False)\n\n    async def handle_change(event):\n        # `checked` is read from the live element, like any other property.\n        set_enabled(await event.target.checked)\n\n    return html.div(\n        html.label(\n            html.input(\n                {\"type\": \"checkbox\", \"checked\": enabled, \"onChange\": handle_change}\n            ),\n            \"Enabled\",\n        ),\n        html.p(f\"Status: {'on' if enabled else 'off'}\"),\n    )\n
# TODO\n

Tick the box and the status line flips. checked is fetched from the live element when the handler awaits it, in the same message as any other property would be \u2014 nothing arrives up front, so a handler which never reads it never pays for it.

"},{"location":"reference/use-state/#form-two-variables","title":"Form (two variables)","text":"

Two inputs, two state variables, no interference between them:

app.py Run
@component\ndef form():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.label(\n            \"First name: \",\n            html.input(\n                {\n                    \"value\": first_name,\n                    \"onChange\": handle_first_name_change,\n                }\n            ),\n        ),\n        html.label(\n            \"Last name: \",\n            html.input(\n                {\n                    \"value\": last_name,\n                    \"onChange\": handle_last_name_change,\n                }\n            ),\n        ),\n        html.h1(f\"Hello, {first_name} {last_name}!\"),\n    )\n
# TODO\n
"},{"location":"reference/use-state/#updating-state-based-on-the-previous-state","title":"Updating state based on the previous state","text":"

A common case is updating state based on the value that came before. Pass a function instead of a value:

set_count(lambda count: count + 1)\n

ReactPy calls your function with the current state and assigns the result.

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_add_three(event):\n        # Updaters receive the current value, so all three increments land.\n        set_count(lambda previous: previous + 1)\n        set_count(lambda previous: previous + 1)\n        set_count(lambda previous: previous + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_add_three}, \"+3\"),\n    )\n
# TODO\n

Click \"+3\" and the counter reaches 3, because each updater sees what the previous one produced \u2014 verified. Had these been set_count(count + 1) instead, all three would compute from the same render-time count and the result would be 1. See Queueing a Series of State Updates.

"},{"location":"reference/use-state/#is-using-an-updater-always-preferred","title":"Is using an updater always preferred?","text":"

No. If you are setting state to a value that does not depend on previous state, pass the value. Updaters are for when the next value is derived from the last, or when several updates to the same variable must compose within one handler.

"},{"location":"reference/use-state/#passing-the-updater-function","title":"Passing the updater function","text":"

The function must be pure \u2014 it should only compute and return the next value. Do not send requests, mutate the argument, or set other state inside it.

# Correct: pure, returns the next value\nset_count(lambda count: count + 1)\n\n# Wrong: mutates, and returns None\nset_items(lambda items: items.append(\"new\"))\n
"},{"location":"reference/use-state/#passing-the-next-state-directly","title":"Passing the next state directly","text":"

When the value comes from outside, pass it plainly:

set_first_name(\"Ada\")\n
"},{"location":"reference/use-state/#updating-objects-and-arrays-in-state","title":"Updating objects and arrays in state","text":"

Pass a new object; there is no merge. For dicts use unpacking, for lists use a comprehension or slicing:

async def handle_first_name(event):\n    set_person({**person, \"firstName\": await event.target.value})\n\n\nasync def handle_add(event):\n    set_items([*items, {\"id\": next_id, \"text\": text}])\n

Full treatment, including nested updates and the silent-drop failure mode, is on Updating Objects in State and Updating Arrays in State.

"},{"location":"reference/use-state/#avoiding-recreating-the-initial-state","title":"Avoiding recreating the initial state","text":"

ReactPy ignores initial_value after the first render, but the expression still runs on every render. If building it is expensive, pass a function:

app.py Run
@component\ndef list_page():\n    state, set_state = use_state(create_expensive_state)\n\n    return html.div(\n        html.h1(f\"{len(state['items'])} items\"),\n        html.p(state[\"items\"][0]),\n    )\n
# TODO\n

create_expensive_state is called once, on mount \u2014 verified (one call across four renders).

"},{"location":"reference/use-state/#passing-the-initializer-function","title":"Passing the initializer function","text":"
state, set_state = use_state(create_expensive_state)   # called once\n
"},{"location":"reference/use-state/#passing-the-initial-state-directly","title":"Passing the initial state directly","text":"
state, set_state = use_state(create_expensive_state())  # built every render\n

Correct, but wasteful. Note again that a bare function is interpreted as an initializer, which is why the second form calls it explicitly.

"},{"location":"reference/use-state/#resetting-state-with-a-key","title":"Resetting state with a key","text":"

ReactPy normally preserves state between re-renders, matching children by position and key. To force a component to start fresh, give its call site a different key:

app.py Run
@component\ndef inbox():\n    email, set_email = use_state(\"ada@example.com\")\n\n    async def handle_email_click(event):\n        set_email(\"grace@example.com\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_email_click},\n            \"Switch account\",\n        ),\n        counter(email, key=email),\n    )\n
# TODO\n

Switching accounts resets the counter to zero, because a changed key means a different component as far as ReactPy is concerned. Verified: a keyed child bumped to 2 came back at 0 after its key changed, while the identical child rendered without a key kept its count.

"},{"location":"reference/use-state/#storing-information-from-previous-renders","title":"Storing information from previous renders","text":"

To keep a history, read the previous list through an updater so it composes correctly, and capture this render's value:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    history, set_history = use_state([])\n\n    async def handle_add(event):\n        # Read history through an updater: `history` in this closure is the\n        # value from the render that created the handler, not the latest.\n        set_history(lambda previous: [*previous, count])\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_add}, \"Add 1\"),\n        html.p(\"Previous values: \" + (\", \".join(map(str, history)) or \"-\")),\n    )\n
# TODO\n

Clicking twice records Previous values: 0, 1 \u2014 verified. The history name in the closure would still be the render-time value, which is exactly why the updater form matters here. For genuinely mutable, non-rendering values, use use_ref instead.

"},{"location":"reference/use-sync-external-store/","title":"Use sync external store","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-transition/","title":"Use transition","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"about/changelog/","title":"Changelog","text":"

Notable changes to ReactPy, newest first. Read the Unreleased section for what has landed since the last release, and the versioned sections for what shipped in each.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Note

Entries use the Keep a Changelog categories \u2014 Added, Changed, Deprecated, Removed, Fixed, Security \u2014 and versions follow Semantic Versioning. Pre-release versions carry a bN suffix, and behaviour can shift between betas without a major-version bump, so pin an exact version if you depend on one.

The authoritative list of shipped artefacts, with downloads and commit ranges, is GitHub Releases; the published packages are on PyPI. This page is the curated narrative of what changed, not the release log.

"},{"location":"about/changelog/#unreleased","title":"Unreleased","text":""},{"location":"about/changelog/#added","title":"Added","text":"
  • Added support for Python 3.12, 3.13, and 3.14.
  • Added type hints to reactpy.html attributes.
  • Added support for nested components in web modules
  • Added support for inline JavaScript as event handlers or other attributes that expect a callable via reactpy.types.InlineJavaScript
  • Event functions can now call event.preventDefault(), event.stopPropagation(), and event.stopImmediatePropagation() methods directly on the event data object, rather than using the @event decorator.
  • Event data now supports accessing properties via dot notation (ex. await event.target.value).
  • Added support for partial functions in EventHandler
  • Added reactpy.types.Event to provide type hints for the standard data function argument (for example async def on_click(event: Event): ...).
  • Added asgi and jinja installation extras (for example pip install reactpy[asgi, jinja]).
  • Added reactpy.executors.asgi.ReactPy that can be used to run ReactPy in standalone mode via ASGI.
  • Added reactpy.executors.asgi.ReactPyCsr that can be used to run ReactPy in standalone mode via ASGI, but rendered entirely client-sided.
  • Added reactpy.executors.asgi.ReactPyMiddleware that can be used to utilize ReactPy within any ASGI compatible framework.
  • Added reactpy.templatetags.ReactPyJinja that can be used alongside ReactPyMiddleware to embed several ReactPy components into your existing application. This includes the following template tags: {% component %}, {% pyscript_component %}, and {% pyscript_setup %}.
  • Added reactpy.pyscript_component that can be used to embed ReactPy components into your existing application.
  • Added reactpy.use_async_effect hook.
  • Added reactpy.Vdom primitive interface for creating VDOM dictionaries.
  • Added reactpy.reactjs.component_from_file to import ReactJS components from a file.
  • Added reactpy.reactjs.component_from_url to import ReactJS components from a URL.
  • Added reactpy.reactjs.component_from_string to import ReactJS components from a string.
  • Added reactpy.reactjs.component_from_npm to import ReactJS components from NPM.
  • Added reactpy.h as a shorthand alias for reactpy.html.
  • Added reactpy.config.REACTPY_MAX_QUEUE_SIZE to configure the maximum size of all ReactPy asyncio queues (e.g. receive buffer, send buffer, event buffer) before ReactPy begins waiting until a slot frees up. This can be used to constraint memory usage.
  • Events now support debounce and throttle, configurable per event via event.debounce = <milliseconds> and EventHandler(fn, throttle=<milliseconds>) respectively.
    • debounce waits until activity stops, then fires once. Default is 200 ms on input/select/textarea, 0 ms elsewhere.
    • throttle caps the rate how often an event is allowed to execute. No default; opt in per event.
  • Added a live event proxy which lets event handlers read properties from, and call methods on, the browser objects which triggered them (ex. await event.target.value, await event.target.focus()) rather than being limited to whatever was serializable at the moment the event was dispatched.
    • Only an id and the event's type travel with the event. Whatever a handler reaches past is fetched from the browser on demand, one round trip at a time, and resolved against the object itself.
    • Properties which are in flight at the same moment go out as a single message, and await event.read(\"target.value\", \"key\") reads several at once. Two handlers which want the same property at the same moment share that one message, and one which walks away from an answer it only borrowed leaves the message in place for the one which asked.
    • A read belongs to the moment it is awaited: it reports the current state of the browser object, not the state it held when the event was dispatched.
    • Whatever a read found is remembered against the event, so reading one property twice costs one round trip - and a handler which wants what the browser holds by now asks again with await event.read(\"target.value\", refresh=True).
    • Whatever comes back out of a method call can be walked too: the page keeps what the call answered for as long as it keeps the event, so a reference into a result resolves against that answer rather than against the method.
    • Unanswered requests are bounded by reactpy.config.REACTPY_EVENT_PROXY_TIMEOUT, reactpy.config.REACTPY_EVENT_PROXY_MAX_TIMEOUTS, and reactpy.config.REACTPY_EVENT_PROXY_MAX_DEPTH.
    • What that budget counts is unanswered messages rather than properties: a batch which asked for four things and got no answer at all costs one of it, not four. The same depth bounds an argument handed to a client side method as well as the data which comes back.
    • A client which reconnects is a fresh chance at live access: binding a new sender brings it back if an earlier one was switched off for going quiet.
"},{"location":"about/changelog/#changed","title":"Changed","text":"
  • The key attribute is now stored within attributes in the VDOM spec.
  • Substitute client-side usage of react with preact.
  • Script elements no longer support behaving like effects. They now strictly behave like plain HTML scripts.
  • The reactpy.html module has been modified to allow for auto-creation of any HTML nodes. For example, you can create a <data-table> element by calling html.data_table().
  • Change set_state comparison method to check equality with == more consistently.
  • Add support for rendering @component children within vdom_to_html.
  • Renamed the use_location hook's search attribute to query_string.
  • Renamed the use_location hook's pathname attribute to path.
  • Renamed reactpy.config.REACTPY_DEBUG_MODE to reactpy.config.REACTPY_DEBUG.
  • ReactPy no longer auto-converts snake_case props to camelCase. It is now the responsibility of the user to ensure that props are in the correct format.
  • Custom JS components will now automatically assume you are using ReactJS in the absence of a bind function.
  • Refactor layout rendering logic to improve readability and maintainability.
  • The JavaScript package @reactpy/client now exports React and ReactDOM, which allows third-party components to re-use the same React instance as ReactPy.
  • reactpy.html will now automatically flatten lists recursively (ex. reactpy.html([\"child1\", [\"child2\"]]))
  • reactpy.utils.reactpy_to_string will now retain the user's original casing for data-* and aria-* attributes.
  • reactpy.utils.string_to_reactpy has been upgraded to handle more complex scenarios without causing ReactJS rendering errors.
  • reactpy.core.vdom._CustomVdomDictConstructor has been moved to reactpy.types.CustomVdomConstructor.
  • reactpy.core.vdom._EllipsisRepr has been moved to reactpy.types.EllipsisRepr.
  • reactpy.types.VdomDictConstructor has been renamed to reactpy.types.VdomConstructor.
  • REACTPY_ASYNC_RENDERING can now de-duplicate renders where necessary.
  • REACTPY_ASYNC_RENDERING is now defaulted to True for up to 40x performance improvements in environments with high concurrency.
  • Event handlers must be coroutines. Whatever is handed to EventHandler, @event, or an on_* attribute has to be awaitable, and anything which is not is refused with a TypeError as it is made into a handler. This is what makes it safe for a handler to read live event data without stalling the layout, and to call set_state() while it waits.
"},{"location":"about/changelog/#deprecated","title":"Deprecated","text":"
  • reactpy.web.module_from_file is deprecated. Use reactpy.reactjs.component_from_file instead.
  • reactpy.web.module_from_url is deprecated. Use reactpy.reactjs.component_from_url instead.
  • reactpy.web.module_from_string is deprecated. Use reactpy.reactjs.component_from_string instead.
  • reactpy.web.export is deprecated. Use reactpy.reactjs.component_from_* instead.
  • reactpy.web.* is deprecated. Use reactpy.reactjs.* instead.
"},{"location":"about/changelog/#removed","title":"Removed","text":"
  • Removed support for Python 3.9 and 3.10.
  • Removed the ability to import reactpy.html.* elements directly. You must now call html.* to access the elements.
  • Removed backend specific installation extras (such as pip install reactpy[starlette]).
  • Removed support for async functions within reactpy.use_effect hook. Use reactpy.use_async_effect instead.
  • Removed deprecated function module_from_template.
  • Removed deprecated exception type reactpy.core.serve.Stop.
  • Removed deprecated component reactpy.widgets.hotswap.
  • Removed the event-to-object package. The client no longer serializes an event when it dispatches one, so nothing was producing that snapshot any more (see the live event proxy above).
  • Removed reactpy.sample module.
  • Removed reactpy.svg module. Contents previously within reactpy.svg.* can now be accessed via reactpy.html.svg.*.
  • Removed reactpy.html._ function. Use reactpy.html(...) or reactpy.html.fragment(...) instead.
  • Removed reactpy.run. See the documentation for the new method to run ReactPy applications.
  • Removed reactpy.backend.*. See the documentation for the new method to run ReactPy applications.
  • Removed reactpy.core.types module. Use reactpy.types instead.
  • Removed reactpy.utils.str_to_bool.
  • Removed reactpy.utils.html_to_vdom. Use reactpy.utils.string_to_reactpy instead.
  • Removed reactpy.utils.vdom_to_html. Use reactpy.utils.reactpy_to_string instead.
  • Removed reactpy.vdom. Use reactpy.Vdom instead.
  • Removed reactpy.core.make_vdom_constructor. Use reactpy.Vdom instead.
  • Removed reactpy.core.custom_vdom_constructor. Use reactpy.Vdom instead.
  • Removed reactpy.Layout top-level re-export. Use reactpy.core.layout.Layout instead.
  • Removed reactpy.types.LayoutType. Use reactpy.types.BaseLayout instead.
  • Removed reactpy.types.ContextProviderType. Use reactpy.types.ContextProvider instead.
  • Removed reactpy.core.hooks._ContextProvider. Use reactpy.types.ContextProvider instead.
  • Removed reactpy.web.utils. Use reactpy.reactjs.utils instead.
"},{"location":"about/changelog/#fixed","title":"Fixed","text":"
  • Fixed a bug where script elements would not render to the DOM as plain text.
  • Fixed a bug where the key property provided within server-side ReactPy code was failing to propagate to the front-end JavaScript components.
  • Fixed a bug where RuntimeError(\"Hook stack is in an invalid state\") errors could be generated when using a webserver that reuses threads.
  • Fixed a bug where events on controlled inputs (e.g. html.input({\"onChange\": ...})) could be lost during rapid actions.
  • Fixed a bug where a render which happened while an event handler was still running could write a stale value back over a controlled input, because the event which prompted it had been acknowledged before the handler was out of the way.
  • Fixed a bug where event-proxy-response messages were dropped by reactpy.executors.asgi.ReactPyMiddleware rather than being routed to the layout which asked for them, leaving every live event read to hang until it timed out.
  • Fixed a bug where a component could stop re-rendering for the rest of its life after a scheduled render was cancelled, since the internal 'a render is already scheduled' latch was never released.
  • Allow for ReactPy and ReactJS components to be arbitrarily inserted onto the page with any possible hierarchy.
"},{"location":"about/changelog/#110-2024-11-24","title":"1.1.0 - 2024-11-24","text":""},{"location":"about/changelog/#fixed_1","title":"Fixed","text":"
  • Fixed broken module_from_template due to a recent release of requests.
  • Fixed module_from_template not working when using Flask backend.
  • Fixed UnicodeDecodeError when using reactpy.web.export.
  • Fixed needless unmounting of JavaScript components during each ReactPy render.
  • Fixed missing event[\"target\"][\"checked\"] on checkbox inputs.
  • Fixed missing static files on sdist Python distribution.
"},{"location":"about/changelog/#added_1","title":"Added","text":"
  • Allow concurrently rendering discrete component trees - enable this experimental feature by setting REACTPY_ASYNC_RENDERING=true. This improves the overall responsiveness of your app in situations where larger renders would otherwise block smaller renders from executing.
"},{"location":"about/changelog/#changed_1","title":"Changed","text":"
  • Previously None, when present in an HTML element, would render as the string \"None\". Now None will not render at all. This is now equivalent to how None is handled when returned from components.
  • Move hooks from reactpy.backend.hooks into reactpy.core.hooks.
"},{"location":"about/changelog/#deprecated_1","title":"Deprecated","text":"
  • The Stop exception. Recent releases of anyio have made this exception difficult to use since it now raises an ExceptionGroup. This exception was primarily used for internal testing purposes and so is now deprecated.
  • Deprecate reactpy.backend.hooks since the hooks have been moved into reactpy.core.hooks.
"},{"location":"about/changelog/#102-2023-07-03","title":"1.0.2 - 2023-07-03","text":""},{"location":"about/changelog/#fixed_2","title":"Fixed","text":"
  • Fix rendering bug when children change positions.
"},{"location":"about/changelog/#101-2023-06-16","title":"1.0.1 - 2023-06-16","text":""},{"location":"about/changelog/#changed_2","title":"Changed","text":"
  • Warn and attempt to fix missing mime types, which can result in reactpy.run not working as expected.
  • Rename reactpy.backend.BackendImplementation to reactpy.backend.BackendType.
  • Allow reactpy.run to fail in more predictable ways.
"},{"location":"about/changelog/#fixed_3","title":"Fixed","text":"
  • Better traceback for JSON serialization errors.
  • Explain that JS component attributes must be JSON.
  • Fix reactpy.run port assignment sometimes attaching to in-use ports on Windows.
  • Fix reactpy.run not recognizing fastapi.
"},{"location":"about/changelog/#100-2023-03-14","title":"1.0.0 - 2023-03-14","text":""},{"location":"about/changelog/#changed_3","title":"Changed","text":"
  • Reverts PR 841 as per the conclusion in discussion 916, but preserves the ability to declare attributes with snake_case.
  • Reverts PR 886 due to issue 896.
  • Revamped element constructor interface. Now instead of passing a dictionary of attributes to element constructors, attributes are declared using keyword arguments. For example, instead of writing:
"},{"location":"about/changelog/#deprecated_2","title":"Deprecated","text":"
  • Declaration of keys via keyword arguments in standard elements. A script has been added to automatically convert old usages where possible.
"},{"location":"about/changelog/#removed_1","title":"Removed","text":"
  • Accidental import of reactpy.testing.
"},{"location":"about/changelog/#fixed_4","title":"Fixed","text":"
  • Minor issues with camelCase rewrite CLI utility.
  • Minor type hint issue with VdomDictConstructor.
  • Stale event handlers after disconnect/reconnect cycle.
  • Fixed CLI not registered as entry point.
  • Unification of component and VDOM constructor interfaces.
"},{"location":"about/changelog/#0440-2023-01-27","title":"0.44.0 - 2023-01-27","text":""},{"location":"about/changelog/#deprecated_3","title":"Deprecated","text":"
  • reactpy.widgets.hotswap. The function has no clear uses outside of some internal applications.
"},{"location":"about/changelog/#removed_2","title":"Removed","text":"
  • Ability to access element value from events via event['value'] key. Use event['target']['value'] instead.
  • Old misspelled option REACTPY_WED_MODULES_DIR.
"},{"location":"about/changelog/#0430-2023-01-09","title":"0.43.0 - 2023-01-09","text":""},{"location":"about/changelog/#deprecated_4","title":"Deprecated","text":"
  • ComponentType.(). This method was implemented based on reading the React/Preact source code.
"},{"location":"about/changelog/#fixed_5","title":"Fixed","text":"
  • Nested context does not update value if outer context should not render.
  • Detached model state on render of context consumer if unmounted and context value does not change.
"},{"location":"about/changelog/#0420-2022-12-02","title":"0.42.0 - 2022-12-02","text":""},{"location":"about/changelog/#added_2","title":"Added","text":"
  • Ability to customize the <head> element of ReactPy's built-in client.
  • vdom_to_html utility function.
  • Ability to subscribe to changes that are made to mutable options.
  • del_html_head_body_transform to remove <html>, <head>, and <body> while preserving children.
  • Support for form element serialization
"},{"location":"about/changelog/#fixed_6","title":"Fixed","text":"
  • REACTPY_DEBUG_MODE is now mutable and can be changed at runtime.
  • Fix html_to_vdom improperly removing <html>, <head>, and <body> nodes.
"},{"location":"about/changelog/#removed_3","title":"Removed","text":"
  • Removed reactpy.html.body as it is currently unusable due to technological limitations.
  • Removed REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY option.
  • Removed serve_static_files option from backend configuration.
"},{"location":"about/changelog/#deprecated_5","title":"Deprecated","text":"
  • module_from_template.
"},{"location":"about/changelog/#0410-2022-11-01","title":"0.41.0 - 2022-11-01","text":""},{"location":"about/changelog/#changed_4","title":"Changed","text":"
  • The hooks use_location and use_scope are no longer implementation specific and are now available as top-level imports.
  • Backend implementations now strip any URL prefix in the pathname for use_location.
  • use_state now returns a named tuple with value and set_value fields.
"},{"location":"about/changelog/#added_3","title":"Added","text":"
  • New use_connection hook which returns a Connection object containing location, scope, and carrier.
"},{"location":"about/changelog/#0402-2022-09-13","title":"0.40.2 - 2022-09-13","text":""},{"location":"about/changelog/#changed_5","title":"Changed","text":"
  • Avoid the use of JSON patch for diffing models.
"},{"location":"about/changelog/#0401-2022-09-11","title":"0.40.1 - 2022-09-11","text":""},{"location":"about/changelog/#fixed_7","title":"Fixed","text":"
  • Child models after a component fail to render.
"},{"location":"about/changelog/#0400-2022-08-13","title":"0.40.0 - 2022-08-13","text":""},{"location":"about/changelog/#fixed_8","title":"Fixed","text":"
  • Fix edge cases where html_to_vdom can fail to convert HTML.
  • Conditionally rendered components cannot use contexts.
  • Use strict equality check for text, numeric, and binary types in hooks.
  • Accidental mutation of old model causes invalid JSON Patch.
"},{"location":"about/changelog/#changed_6","title":"Changed","text":"
  • Set default timeout on Playwright page for testing.
  • Track contexts in hooks as state.
  • Remove non-standard name argument from create_context.
"},{"location":"about/changelog/#added_4","title":"Added","text":"
  • asgiref as a dependency.
  • lxml as a dependency.
"},{"location":"about/changelog/#0390-2022-06-20","title":"0.39.0 - 2022-06-20","text":""},{"location":"about/changelog/#fixed_9","title":"Fixed","text":"
  • No module named 'reactpy.server' from reactpy.run.
  • Setting appropriate MIME type for web modules in sanic server implementation.
"},{"location":"about/changelog/#changed_7","title":"Changed","text":"
  • Renamed various: reactpy.testing.server to reactpy.testing.backend, ServerFixture to BackendFixture, DisplayFixture.server to DisplayFixture.backend.
  • Removed exports_default parameter from module_from_template.
"},{"location":"about/changelog/#added_5","title":"Added","text":"
  • Ability to specify versions with module templates (e.g. module_from_template(\"react@^17.0.0\", ...)).
"},{"location":"about/changelog/#0381-2022-04-15","title":"0.38.1 - 2022-04-15","text":""},{"location":"about/changelog/#fixed_10","title":"Fixed","text":"
  • Missing file extension was causing a problem with WebPack.
"},{"location":"about/changelog/#0380-2022-04-15","title":"0.38.0 - 2022-04-15","text":"

No changes.

"},{"location":"about/changelog/#0372-2022-03-27","title":"0.37.2 - 2022-03-27","text":""},{"location":"about/changelog/#changed_8","title":"Changed","text":"
  • Renamed proto modules to types and added a top-level reactpy.types module.
"},{"location":"about/changelog/#fixed_11","title":"Fixed","text":"
  • Fixed a typo that caused ReactPy to use the insecure ws web-socket protocol on pages loaded with https instead of the secure wss protocol.
"},{"location":"about/changelog/#0371-2022-03-05","title":"0.37.1 - 2022-03-05","text":"

No changes.

"},{"location":"about/changelog/#0370-2022-02-27","title":"0.37.0 - 2022-02-27","text":""},{"location":"about/changelog/#added_6","title":"Added","text":"
  • Support for keys in HTML fragments.
  • Use Context Hook.
"},{"location":"about/changelog/#fixed_12","title":"Fixed","text":"
  • React warning about set state in unmounted component.
  • Missing reset of schedule_render_later flag.
"},{"location":"about/changelog/#0363-2022-02-18","title":"0.36.3 - 2022-02-18","text":""},{"location":"about/changelog/#fixed_13","title":"Fixed","text":"
  • All child states wiped upon any child key change.
  • Allow NoneType returns within components.
"},{"location":"about/changelog/#0362-2022-02-02","title":"0.36.2 - 2022-02-02","text":""},{"location":"about/changelog/#fixed_14","title":"Fixed","text":"
  • Hot fix for newly introduced DeprecatedOption.
"},{"location":"about/changelog/#0361-2022-02-02","title":"0.36.1 - 2022-02-02","text":""},{"location":"about/changelog/#fixed_15","title":"Fixed","text":"
  • Fix Key Error when Cleaning Up Event Handlers.
  • Update Script Tag Behavior.
"},{"location":"about/changelog/#changed_9","title":"Changed","text":"
  • Renamed configuration option REACTPY_WED_MODULES_DIR to REACTPY_WEB_MODULES_DIR.
"},{"location":"about/changelog/#0360-2022-01-30","title":"0.36.0 - 2022-01-30","text":""},{"location":"about/changelog/#added_7","title":"Added","text":"
  • New http.script element which can behave similarly to a standard HTML <script> or, if no attributes are given, operate similarly to an effect.
"},{"location":"about/changelog/#fixed_16","title":"Fixed","text":"
  • State mismatch during component update.
  • Implement a script tag.
"},{"location":"about/changelog/#0354-2022-01-27","title":"0.35.4 - 2022-01-27","text":""},{"location":"about/changelog/#fixed_17","title":"Fixed","text":"
  • Keys for elements at the root of a component were not being tracked. Thus key changes for elements at the root did not trigger unmounts.
"},{"location":"about/changelog/#0353-2022-01-27","title":"0.35.3 - 2022-01-27","text":""},{"location":"about/changelog/#changed_10","title":"Changed","text":"
  • Elements which changed type are now always deeply unmounted.
"},{"location":"about/changelog/#0352-2022-01-26","title":"0.35.2 - 2022-01-26","text":""},{"location":"about/changelog/#fixed_18","title":"Fixed","text":"
  • Allow children with the same key to vary in type.
  • Client always looks for server at \"/\".
  • Web modules get double file extensions with v0.35.x.
"},{"location":"about/changelog/#0351-2022-01-18","title":"0.35.1 - 2022-01-18","text":""},{"location":"about/changelog/#fixed_19","title":"Fixed","text":"
  • Re-add accidentally deleted py.typed file to distribution.
"},{"location":"about/changelog/#0350-2022-01-18","title":"0.35.0 - 2022-01-18","text":""},{"location":"about/changelog/#added_8","title":"Added","text":"
  • Default key for all elements and components is now their index amongst their siblings.
  • use_linked_inputs widget.
  • Starlette server implementation.
"},{"location":"about/changelog/#changed_11","title":"Changed","text":"
  • REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY now defaults to 1.
"},{"location":"about/changelog/#fixed_20","title":"Fixed","text":"
  • Support Starlette Server.
  • Fix unhandled case in module_from_template.
  • Hide \"Children\" within REACTPY_DEBUG_MODE key warnings.
  • Bug in Element Key Identity.
  • Add iFrame to reactpy.html.
  • React warning from module_from_template.
"},{"location":"about/changelog/#0340-2021-12-16","title":"0.34.0 - 2021-12-16","text":""},{"location":"about/changelog/#changed_12","title":"Changed","text":"
  • Element value must now be accessed via event['target']['value'] instead of event['value'].
  • Added event[\"currentTarget\"] and event[\"relatedTarget\"] keys to the event dictionary.
"},{"location":"about/changelog/#fixed_21","title":"Fixed","text":"
  • Move target attributes to event['target'].
"},{"location":"about/changelog/#0333-2021-10-08","title":"0.33.3 - 2021-10-08","text":""},{"location":"about/changelog/#added_9","title":"Added","text":"
  • Warning stating that REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY=1 will become the default in a future release.
  • Ability to use the default export from a JavaScript module when calling module_from_template by specifying exports_default=True.
"},{"location":"about/changelog/#fixed_22","title":"Fixed","text":"
  • Memory leak in SharedClientStateServer.
  • Cannot use default export in react template.
"},{"location":"about/changelog/#0332-2021-09-05","title":"0.33.2 - 2021-09-05","text":""},{"location":"about/changelog/#fixed_23","title":"Fixed","text":"
  • Memory leak caused by event handlers that were not being removed when components updated.
"},{"location":"about/changelog/#0331-2021-09-02","title":"0.33.1 - 2021-09-02","text":""},{"location":"about/changelog/#fixed_24","title":"Fixed","text":"
  • Regression where the root element of the layout could not be updated.
"},{"location":"about/changelog/#0330-2021-09-02","title":"0.33.0 - 2021-09-02","text":""},{"location":"about/changelog/#fixed_25","title":"Fixed","text":"
  • Client-side error in mount-01d35dc3.js.
  • Style cannot be updated.
  • Displaying error messages in the client via __error__ tag can leak secrets.
  • Examples broken in docs.
"},{"location":"about/changelog/#changed_13","title":"Changed","text":"
  • Modified the Custom JavaScript Component interface to add a create() function to the bind() interface.
"},{"location":"about/changelog/#0320-2021-08-20","title":"0.32.0 - 2021-08-20","text":""},{"location":"about/changelog/#changed_14","title":"Changed","text":"
  • Custom component interface now expects a single bind() function that returns an object with render() and unmount() functions.
"},{"location":"about/changelog/#fixed_26","title":"Fixed","text":"
  • Docs broken on Firefox.
  • URL resolution for web modules does not consider urls starting with /.
  • Query params in package name for module_from_template not stripped.
  • Search broken in docs.
  • Move src/reactpy/client out of Python package.
"},{"location":"about/changelog/#0310-2021-07-14","title":"0.31.0 - 2021-07-14","text":""},{"location":"about/changelog/#changed_15","title":"Changed","text":"
  • Layout is now a prototype and Layout.update is no longer a public API.
  • Refactored the relationship between LifeCycleHook and Layout.
  • ComponentTypes no longer needs a unique id attribute.
"},{"location":"about/changelog/#0301-2021-07-13","title":"0.30.1 - 2021-07-13","text":""},{"location":"about/changelog/#fixed_27","title":"Fixed","text":"
  • Warning if key is parameter of component render function.
"},{"location":"about/changelog/#0300-2021-06-28","title":"0.30.0 - 2021-06-28","text":""},{"location":"about/changelog/#changed_16","title":"Changed","text":"
  • Removed all runtime reliance on NPM.
  • reactpy.client is removed in favor of a stripped down reactpy.web module.
  • REACTPY_CLIENT_BUILD_DIR config option is replaced with REACTPY_WEB_MODULES_DIR.
"},{"location":"about/changelog/#0290-2021-06-20","title":"0.29.0 - 2021-06-20","text":""},{"location":"about/changelog/#changed_17","title":"Changed","text":"
  • Moved the runtime client build directory to a \"user data\" directory.
  • Custom JS component interface has been reworked.
"},{"location":"about/changelog/#0280-2021-06-01","title":"0.28.0 - 2021-06-01","text":""},{"location":"about/changelog/#added_10","title":"Added","text":"
  • Support for currentTime attribute of audio/video elements.
  • Support for the files attribute from the target of input elements.
  • Model children are now passed to the JavaScript mount() function.
  • mountLayoutWithWebSocket function to @reactpy/client.
"},{"location":"about/changelog/#changed_18","title":"Changed","text":"
  • Refactored existing server implementations as functions adhering to a protocol.
  • Switched to a monorepo-style structure for JavaScript.
  • Use a loadImportSource() function instead of trying to infer the path to dynamic modules.
"},{"location":"about/changelog/#0270-2021-05-14","title":"0.27.0 - 2021-05-14","text":""},{"location":"about/changelog/#added_11","title":"Added","text":"
  • mount(element, component, props) function which can be used to bind new elements to the DOM.
"},{"location":"about/changelog/#0260-2021-05-07","title":"0.26.0 - 2021-05-07","text":""},{"location":"about/changelog/#fixed_28","title":"Fixed","text":"
  • Fix for situations where a Layout can raise an error when a component whose state has been deleted is rendered.
"},{"location":"about/changelog/#0250-2021-04-30","title":"0.25.0 - 2021-04-30","text":""},{"location":"about/changelog/#changed_19","title":"Changed","text":"
  • Refactored layout dispatcher by switching from a class-based approach to one that leverages pure functions.
"},{"location":"about/changelog/#0240-2021-04-18","title":"0.24.0 - 2021-04-18","text":""},{"location":"about/changelog/#added_12","title":"Added","text":"
  • Components and elements can now have \"identity\" - their state can be preserved across updates.
  • REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY feature flag.
"},{"location":"about/changelog/#0231-2021-04-02","title":"0.23.1 - 2021-04-02","text":""},{"location":"about/changelog/#fixed_29","title":"Fixed","text":"
  • Non-deterministic return order in install().
"},{"location":"about/changelog/#0230-2021-04-01","title":"0.23.0 - 2021-04-01","text":""},{"location":"about/changelog/#added_13","title":"Added","text":"
  • Changelog to docs.
  • Automatically reconnect to server.
  • Allow no reconnect in client.
  • Cleaner way to specify import sources.
  • FastAPI render server.
"},{"location":"about/changelog/#0221-2021-01-02","title":"0.22.1 - 2021-01-02","text":""},{"location":"about/changelog/#fixed_30","title":"Fixed","text":"
  • Fix missing find_available_port arg.
"},{"location":"about/changelog/#0220-2020-12-24","title":"0.22.0 - 2020-12-24","text":""},{"location":"about/changelog/#fixed_31","title":"Fixed","text":"
  • Fix doc build.
  • Better server error capture in tests.
  • Fix type annotations.
  • Fix dark mode widget view.
"},{"location":"about/changelog/#changed_20","title":"Changed","text":"
  • Use Nox to run tests.
  • Add .nox to gitignore.
"},{"location":"about/changelog/#0210-2020-12-16","title":"0.21.0 - 2020-12-16","text":""},{"location":"about/changelog/#added_14","title":"Added","text":"
  • Tornado render server.
  • Render server documentation.
"},{"location":"about/changelog/#changed_21","title":"Changed","text":"
  • Renamed element to component.
"},{"location":"about/changelog/#0201-2020-11-13","title":"0.20.1 - 2020-11-13","text":""},{"location":"about/changelog/#fixed_32","title":"Fixed","text":"
  • Avoid sanic import.
"},{"location":"about/changelog/#0200-2020-11-12","title":"0.20.0 - 2020-11-12","text":""},{"location":"about/changelog/#added_15","title":"Added","text":"
  • Flask render server.
"},{"location":"about/changelog/#changed_22","title":"Changed","text":"
  • Moved idom_client_react into separate repo.
  • Removed mountLayoutWithWebSocket.
  • Removed more client implementation docs.
"},{"location":"about/changelog/#fixed_33","title":"Fixed","text":"
  • Fix missing server event.
"},{"location":"about/changelog/#0190-2020-10-19","title":"0.19.0 - 2020-10-19","text":""},{"location":"about/changelog/#fixed_34","title":"Fixed","text":"
  • No cover last server error.
"},{"location":"about/changelog/#changed_23","title":"Changed","text":"
  • Refined test tooling.
  • Added all to testing module.
"},{"location":"about/changelog/#0181-2020-09-25","title":"0.18.1 - 2020-09-25","text":""},{"location":"about/changelog/#fixed_35","title":"Fixed","text":"
  • Fix missing find_available_port arg.
"},{"location":"about/changelog/#0180-2020-09-23","title":"0.18.0 - 2020-09-23","text":""},{"location":"about/changelog/#added_16","title":"Added","text":"
  • Module components should be accessible as attrs.
"},{"location":"about/changelog/#fixed_36","title":"Fixed","text":"
  • Fix doc import source URL.
  • Minor JS fixes.
  • Fix idom install.
  • Better URL join logic in client.
"},{"location":"about/changelog/#changed_24","title":"Changed","text":"
  • Strip down test tooling.
  • Avoid issues with bundlers.
"},{"location":"about/changelog/#0171-2020-08-14","title":"0.17.1 - 2020-08-14","text":""},{"location":"about/changelog/#fixed_37","title":"Fixed","text":"
  • Fix idom install.
"},{"location":"about/changelog/#0170-2020-08-08","title":"0.17.0 - 2020-08-08","text":""},{"location":"about/changelog/#added_17","title":"Added","text":"
  • Import source base URL with vdom fallback.
"},{"location":"about/changelog/#fixed_38","title":"Fixed","text":"
  • Fix types.
  • Fix mypy testing.
"},{"location":"about/changelog/#changed_25","title":"Changed","text":"
  • Bumped idom_client_react version.
  • Use universal wheel.
"},{"location":"about/changelog/#0160-2020-06-26","title":"0.16.0 - 2020-06-26","text":""},{"location":"about/changelog/#fixed_39","title":"Fixed","text":"
  • Put setuptools_scm back in setup.py.
  • Fix pyproject.toml.
"},{"location":"about/changelog/#changed_26","title":"Changed","text":"
  • Remove framework classifier.
  • Only check last error in display context.
  • Refactor and cleanup tests.
  • Add public test tooling.
"},{"location":"about/changelog/#0150-2020-05-02","title":"0.15.0 - 2020-05-02","text":""},{"location":"about/changelog/#added_18","title":"Added","text":"
  • More docs.
  • pyproject.toml support.
"},{"location":"about/changelog/#fixed_40","title":"Fixed","text":"
  • Fix coverage.
  • Fix misc bugs.
  • Fix mypy.
"},{"location":"about/changelog/#changed_27","title":"Changed","text":"
  • Improve docs.
  • Improve log messages.
  • Rename config \"item\" to \"entry\".
  • Upgrade snowpack to max possible.
  • More tests for client management.
"},{"location":"about/changelog/#0143-2020-04-10","title":"0.14.3 - 2020-04-10","text":""},{"location":"about/changelog/#fixed_41","title":"Fixed","text":"
  • Fix JSON patch in-place (broke some components).
  • Revert bad JSON patch usage.
"},{"location":"about/changelog/#0142-2020-03-30","title":"0.14.2 - 2020-03-30","text":""},{"location":"about/changelog/#fixed_42","title":"Fixed","text":"
  • Fix displayed version in docs.
"},{"location":"about/changelog/#0141-2020-03-20","title":"0.14.1 - 2020-03-20","text":""},{"location":"about/changelog/#fixed_43","title":"Fixed","text":"
  • Remove extra spaces in element children.
"},{"location":"about/changelog/#0140-2020-03-20","title":"0.14.0 - 2020-03-20","text":""},{"location":"about/changelog/#added_19","title":"Added","text":"
  • idom.run utility.
"},{"location":"about/changelog/#fixed_44","title":"Fixed","text":"
  • Fix types.
  • Fix doc examples.
  • Simplify server constructor.
"},{"location":"about/changelog/#0130-2020-02-15","title":"0.13.0 - 2020-02-15","text":""},{"location":"about/changelog/#added_20","title":"Added","text":"
  • Well-defined client implementation layer.
"},{"location":"about/changelog/#fixed_45","title":"Fixed","text":"
  • Fix types.
  • Fix tests.
"},{"location":"about/changelog/#changed_28","title":"Changed","text":"
  • Remove ability to install modules at runtime via idom.Module.
"},{"location":"about/changelog/#0120-2020-01-27","title":"0.12.0 - 2020-01-27","text":""},{"location":"about/changelog/#added_21","title":"Added","text":"
  • Utility methods for LayoutUpdate (start, stop).
  • useStateRef hook.
"},{"location":"about/changelog/#changed_29","title":"Changed","text":"
  • Move Jupyter out of base idom into idom-jupyter package.
"},{"location":"about/changelog/#0113-2020-01-14","title":"0.11.3 - 2020-01-14","text":""},{"location":"about/changelog/#fixed_46","title":"Fixed","text":"
  • Add utility method tests to LayoutUpdate.
"},{"location":"about/changelog/#0112-2020-01-14","title":"0.11.2 - 2020-01-14","text":""},{"location":"about/changelog/#added_22","title":"Added","text":"
  • Test dispatcher start and stop.
"},{"location":"about/changelog/#0111-2019-12-20","title":"0.11.1 - 2019-12-20","text":""},{"location":"about/changelog/#changed_30","title":"Changed","text":"
  • Bump JS to 0.4.2.
"},{"location":"about/changelog/#0110-2019-12-18","title":"0.11.0 - 2019-12-18","text":""},{"location":"about/changelog/#added_23","title":"Added","text":"
  • MountLayout utility function.
"},{"location":"about/changelog/#fixed_47","title":"Fixed","text":"
  • Fix issues.
  • Correct package.json for idom-client-react.
"},{"location":"about/changelog/#changed_31","title":"Changed","text":"
  • Rename StateDispatcher to ViewDispatcher.
  • Remove private=true from imports.
  • Require anyio>=2.0.
  • Rename static/ directory to app/.
"},{"location":"about/changelog/#0104-2019-12-01","title":"0.10.4 - 2019-12-01","text":""},{"location":"about/changelog/#fixed_48","title":"Fixed","text":"
  • Fix docs.
"},{"location":"about/changelog/#0103-2019-12-01","title":"0.10.3 - 2019-12-01","text":""},{"location":"about/changelog/#fixed_49","title":"Fixed","text":"
  • Publish package before docs.
"},{"location":"about/changelog/#0102-2019-11-20","title":"0.10.2 - 2019-11-20","text":""},{"location":"about/changelog/#fixed_50","title":"Fixed","text":"
  • Fix up tests.
"},{"location":"about/changelog/#added_24","title":"Added","text":"
  • Material UI example.
  • Concept of build cache.
"},{"location":"about/changelog/#0101-2019-11-08","title":"0.10.1 - 2019-11-08","text":""},{"location":"about/changelog/#fixed_51","title":"Fixed","text":"
  • Upgrade anyio (see https://github.com/agronholm/anyio/issues/155).
"},{"location":"about/changelog/#0100-2019-11-07","title":"0.10.0 - 2019-11-07","text":""},{"location":"about/changelog/#added_25","title":"Added","text":"
  • Fallbacks to imported JS in examples.
"},{"location":"about/changelog/#fixed_52","title":"Fixed","text":"
  • Fix doc updates.
  • Better URL join logic.
"},{"location":"about/changelog/#changed_32","title":"Changed","text":"
  • Removed async event handler.
  • Renamed Renderer to Dispatcher.
  • Removed AbstractElement.id.
"},{"location":"about/changelog/#092-2019-10-14","title":"0.9.2 - 2019-10-14","text":""},{"location":"about/changelog/#fixed_53","title":"Fixed","text":"
  • Fix docs and improve doc example runner.
"},{"location":"about/changelog/#091-2019-10-10","title":"0.9.1 - 2019-10-10","text":""},{"location":"about/changelog/#fixed_54","title":"Fixed","text":"
  • Add setuptools_scm as docs requirement.
"},{"location":"about/changelog/#090-2019-10-06","title":"0.9.0 - 2019-10-06","text":""},{"location":"about/changelog/#added_26","title":"Added","text":"
  • Documentation for async effects.
  • Fix effect cleanup timing.
  • Test async effect.
"},{"location":"about/changelog/#fixed_55","title":"Fixed","text":"
  • Fix layout test.
  • Fix type annotations.
"},{"location":"about/changelog/#changed_33","title":"Changed","text":"
  • Element render functions don't need to be async.
  • Dropped Python 3.6 support.
"},{"location":"about/changelog/#070-2020-08-09","title":"0.7.0 - 2020-08-09","text":""},{"location":"about/changelog/#added_27","title":"Added","text":"
  • use_reducer hook.
  • use_effect tests.
  • Tests for use_ref and use_callback.
  • HookCatcher utility.
  • Layout tests.
"},{"location":"about/changelog/#fixed_56","title":"Fixed","text":"
  • Fix bug in use_memo.
  • Fix types for render and layout.
  • Fix flake8 ignore doc examples.
"},{"location":"about/changelog/#changed_34","title":"Changed","text":"
  • Rename top level examples dir to notebooks.
  • Remove unused test script.
  • Effects run just before and after full render.
  • Simplified rendering queue.
  • Removed abstract layout (renamed to Layout.dispatch).
"},{"location":"about/changelog/#removed_4","title":"Removed","text":"
  • Drag-drop example.
"},{"location":"about/changelog/#060-2020-06-20","title":"0.6.0 - 2020-06-20","text":""},{"location":"about/changelog/#fixed_57","title":"Fixed","text":"
  • Various bug fixes from early development.
"},{"location":"about/changelog/#added_28","title":"Added","text":"
  • Core infrastructure for component rendering.
"},{"location":"about/changelog/#051-2019-10-24","title":"0.5.1 - 2019-10-24","text":""},{"location":"about/changelog/#fixed_58","title":"Fixed","text":"
  • Fix tests and event handlers.
"},{"location":"about/changelog/#050-2019-06-13","title":"0.5.0 - 2019-06-13","text":""},{"location":"about/changelog/#added_29","title":"Added","text":"
  • use_effect hook.
  • Initial idom-jupyter support.
  • Basic client-side rendering.
  • createContext support.
  • useCallback and useMemo.
  • Initial testing infrastructure.
"},{"location":"about/changelog/#fixed_59","title":"Fixed","text":"
  • Fix various rendering and state management bugs.
"},{"location":"about/changelog/#removed_5","title":"Removed","text":"
  • Legacy state management.
"},{"location":"about/changelog/#042-2019-03-20","title":"0.4.2 - 2019-03-20","text":""},{"location":"about/changelog/#fixed_60","title":"Fixed","text":"
  • Revert \"make vdom a simple dict\".
"},{"location":"about/changelog/#041-2019-02-26","title":"0.4.1 - 2019-02-26","text":""},{"location":"about/changelog/#fixed_61","title":"Fixed","text":"
  • Fix tests.
"},{"location":"about/changelog/#040-2019-02-02","title":"0.4.0 - 2019-02-02","text":""},{"location":"about/changelog/#added_30","title":"Added","text":"
  • Core functionality for idom.
"},{"location":"about/changelog/#changed_35","title":"Changed","text":"
  • Restructure around idom-core and idom-legacy.
"},{"location":"about/changelog/#030-2018-12-18","title":"0.3.0 - 2018-12-18","text":""},{"location":"about/changelog/#added_31","title":"Added","text":"
  • Core functionality with hooks (useState, useEffect, etc.).
"},{"location":"about/changelog/#fixed_62","title":"Fixed","text":"
  • Fix initial rendering issues.
"},{"location":"about/changelog/#020-2018-10-22","title":"0.2.0 - 2018-10-22","text":""},{"location":"about/changelog/#added_32","title":"Added","text":"
  • Basic functionality.
"},{"location":"about/changelog/#fixed_63","title":"Fixed","text":"
  • Initial bug fixes.
"},{"location":"about/changelog/#012-2018-08-03","title":"0.1.2 - 2018-08-03","text":""},{"location":"about/changelog/#fixed_64","title":"Fixed","text":"
  • Fix import issues.
"},{"location":"about/changelog/#010-2018-07-30","title":"0.1.0 - 2018-07-30","text":""},{"location":"about/changelog/#added_33","title":"Added","text":"
  • Initial release.
"},{"location":"about/code/","title":"Contributing Code","text":""},{"location":"about/code/#overview","title":"Overview","text":"

ReactPy takes pull requests from anyone, and most of what gets rejected is not wrong \u2014 it is unreviewable. A short branch, a focused diff, and the checks run before you open the pull request is what makes a change easy to say yes to. This page covers the mechanics: where the code lives, how to branch, and what CI will run against you.

You will learn

  • Which branch a change belongs on, and why that matters here more than elsewhere
  • The commit and pull request conventions this repository enforces
  • What CI checks, before CI tells you
"},{"location":"about/code/#know-which-repository-you-are-in","title":"Know which repository you are in","text":"

This documentation site and the library are separate bodies of work, and the commands that work in one do nothing in the other.

Change Repository / branch Library code, hooks, layout, backends reactive-python/reactpy \u2192 main JavaScript client (src/js/) Same, main These documentation pages (docs/src/, docs/examples/) reactive-python/reactpy \u2192 new-docs

The new-docs branch holds only docs/ \u2014 the package and tests/ were removed from it. A test command run there reports success without running anything.

Pitfall

Confirm the branch before you start typing, not after. A library fix committed onto the docs branch looks fine locally and becomes a rebase chore later. git branch --show-current costs nothing and settles it.

"},{"location":"about/code/#before-you-start","title":"Before you start","text":"

Look for an open discussion or issue that matches what you intend to do. If one exists, comment there before writing code \u2014 a maintainer who has already thought about the problem will usually redirect you in one sentence, and that sentence is cheaper than a rejected pull request. If none exists, open a discussion first.

For anything larger than a bug fix, expect the answer to be \"show me the design first\". A draft pull request is the right medium for that: it runs CI, it is reviewable, and it says plainly that it is not ready to merge.

"},{"location":"about/code/#branch-commit-push","title":"Branch, commit, push","text":"

Fork the repository if you do not have write access, then branch from an up-to-date main:

git checkout -b fix-my-change main\n

Name the branch for the change, not for you. fix-key-not-resetting-on-provider beats patch-1 and survives a month of review.

Match the convention of the branch you are landing on; they differ. Documentation commits on new-docs use Conventional Commits subjects \u2014 docs(learn): author X for ReactPy v2, docs(examples): fix include-marker cropping. Library history on main instead uses plain sentence subjects, because pull requests there are squash-merged and the summary is written at merge time \u2014 Fix PyScript root component not hiding after render error (#1347). In both cases the subject is the thing a reader scans to find the functional change in a long history, so write it for that reader.

One commit per logical change. If a commit only makes sense next to a later one, squash them before you push rather than asking the reviewer to mentally reassemble them.

"},{"location":"about/code/#the-checklist","title":"The checklist","text":"

The repository ships a pull request template; fill it in rather than deleting it.

  • [ ] Tests have been developed for bug fixes or new functionality.
  • [ ] The changelog has been updated, if necessary.
  • [ ] Documentation has been updated, if necessary.
  • [ ] GitHub Issues closed by this pull request have been linked.

By submitting, you agree that your contribution complies with the project's open source licenses.

Tests are not optional for a behaviour change. A bug fix should arrive with the test that reproduces the bug \u2014 write it first and watch it fail, then fix and watch it pass. A test that has never failed proves nothing about the thing it claims to protect.

The changelog rule is narrower than it looks: CHANGELOG.md gets an entry under Unreleased for user-facing changes only. Test-only, docs-only, and CI-only changes get none. Adding one for those is a small annoyance to the person doing the release, who then has to notice and delete it. See Changelog.

"},{"location":"about/code/#what-ci-runs","title":"What CI runs","text":"

Understanding the gate is cheaper than discovering it in a failed run.

hatch test --parallel --cover --retries 10          # coverage job\nhatch test --parallel --retries 10                  # ubuntu, macos, windows \u00d7 3.11\u20133.14\nhatch fmt src/reactpy --check                       # lint + format\nhatch run python:type_check                         # pyright\nhatch run javascript:test\nhatch run javascript:check\n

Run the equivalents locally before you push \u2014 the same commands, not approximations of them:

hatch fmt --check\nhatch run python:type_check\nhatch test -k the_test_you_touched\n

CI retries flaky tests, which is a mercy and a trap: a genuinely racy test can go green on the third attempt and stay in the tree. If a test fails once and passes on retry, that is a bug in the test \u2014 usually a bare asyncio.sleep where poll(...) belongs.

A pull request into new-docs gets no CI at all, for two independent reasons. GitHub runs pull_request workflows from the base branch, and the only workflow that exists on new-docs is the docs deploy, which triggers on push \u2014 so nothing matches a PR. And the test-documentation job in check.yml on main is disabled outright with if: 0 (temporarily, while the Sphinx-to-MkDocs transition finishes), so even a PR targeting main runs lint and Python tests but never builds the docs. A docs change is verified by you or by nobody \u2014 run the mkdocs build --strict set from Running Tests and read its exit code. The one thing that does run: pushing to new-docs deploys with mkdocs gh-deploy --force, no --strict \u2014 expect a change to be live the moment it merges, whether or not it builds cleanly.

"},{"location":"about/code/#working-with-reviewers","title":"Working with reviewers","text":"

Respond to every comment, including the ones you are not acting on. \"Not taking this because X\" is a complete reply and saves a round trip; silence invites a re-review of something you already considered.

Push fixes as new commits rather than force-pushing over the reviewed range. Reviewers read the delta between your last push and this one; a force-push that rewrites history they already read makes them re-read the whole diff, and they will notice when they skip it.

If a review asks for something you believe is wrong, say so once, with evidence, and then defer if the maintainer still wants it. Review is a conversation with an asymmetry: they own the file after you leave.

Note

When a review is posted by an automated tool, the maintainers still own the decision. Do not treat \"the bot did not complain\" as approval, and do not treat a bot comment as a demand \u2014 respond on the merits, the same as any other comment.

"},{"location":"about/code/#after-merge","title":"After merge","text":"

Documentation changes on new-docs deploy to the live site on push, so a mistake there is visible immediately rather than at the next release. That is a reason to run the build check locally, not a reason to be nervous about contributing.

Library changes reach users only through a release, which the maintainers cut. Do not ask when your fix will ship, and do not close a closed-issue loop by demanding a release \u2014 the tag is a maintainer's call.

"},{"location":"about/code/#recap","title":"Recap","text":"
  • Library work on main; documentation work on new-docs. Check the branch first.
  • Start from a discussion; a one-sentence redirect beats a rejected pull request.
  • Conventional Commit subjects, one logical change per commit, branch named for the change.
  • Tests accompany behaviour changes, and they must have failed first.
  • Changelog entries for user-facing changes only.
  • Run CI's exact commands locally before pushing; treat a flaky test as a bug.
"},{"location":"about/code/#related","title":"Related","text":"
  • Running Tests \u2014 the commands, and how to read their failures
  • Contributing Documentation \u2014 page conventions and the examples workflow
  • Changelog \u2014 format, categories, and what counts as user-facing
  • Community \u2014 where to raise the issue before you write the fix
"},{"location":"about/community/","title":"Community","text":""},{"location":"about/community/#overview","title":"Overview","text":"

ReactPy is built in the open. Questions, bug reports, and ideas all have a home, and the same places are where you get help when an example on this site stops behaving. This page is a map of those places and what belongs in each.

You will learn

  • Where to ask a question, report a bug, and propose a feature
  • Which channels are official, and which are just links people repeat
  • What the code of conduct expects, and what to include in a report
"},{"location":"about/community/#where-everything-happens","title":"Where everything happens","text":"

The ReactPy repository on GitHub is the centre of gravity. Everything else points back to it.

You want to Go to Ask how to do something Discussions \u2192 Question Report something broken Discussions \u2192 Problem Propose a feature Discussions \u2192 Ideas Show off what you built Discussions \u2192 Share Publish a package you made Discussions \u2192 Downloadable Packages Track a confirmed defect Issues Chat in real time Discord

Discussions is the front door, not Issues. The repository's issue-template configuration disables blank issues and points new visitors at Discussions instead, so a question opened as an issue gets redirected rather than answered. Open an issue once a defect is confirmed and someone has reproduced it \u2014 the discussion that came first usually becomes the link inside it.

"},{"location":"about/community/#chat","title":"Chat","text":"

There is a Discord server for ReactPy, announced in Discussion #997 and linked from the repository README. It is the fastest place to get an answer to a \"why is this doing that\" question, and quiet enough that nobody minds a beginner question.

Note

An invite URL is mutable third-party state. If discord.gg/uNb5P4hA9X ever stops resolving, read the current invite from the README badge or from Discussion #997 \u2014 those two are maintained together \u2014 rather than trusting a search result, which may point at a cloned server.

Discord is good for conversation and bad for searchability. A solution that exists only in chat is invisible to the next person with the same problem. If a chat thread solves something real, post it to Discussions afterwards; if it exposes an error on this site, that is a documentation contribution.

"},{"location":"about/community/#code-of-conduct","title":"Code of conduct","text":"

Participation is governed by the Contributor Covenant code of conduct, which lives at the repository root. It applies everywhere the project is discussed \u2014 Discussions, Issues, pull requests, Discord, and any other space where ReactPy maintainers show up.

The practical version: assume good faith, criticise code rather than people, and accept that \"this is obvious\" is rarely true of the person asking. The enforcement section of the document names the contact route for escalating a problem.

"},{"location":"about/community/#packages-outside-this-repository","title":"Packages outside this repository","text":"

ReactPy's core deliberately stays small, so much of what you might expect to find here lives in sibling repositories. Each has its own issue tracker \u2014 do not file a Django problem against the core repo.

  • reactpy-router \u2014 URL routing
  • reactpy-django \u2014 Django integration
  • reactpy-jupyter \u2014 Jupyter integration
  • reactpy-js-component-template \u2014 starting point for a custom JavaScript component

Community-built packages are announced in Discussions \u2192 Downloadable Packages. Those are third-party: they are not reviewed before being posted, they are not pinned against a ReactPy release, and an unanswered thread there says nothing about the library. See Tools, Libraries, and Packages for how to evaluate one.

"},{"location":"about/community/#supporting-the-project","title":"Supporting the project","text":"

ReactPy is maintained by volunteers on their own time. GitHub Sponsors is the route the project uses, matching the maintainers listed in the repository's funding configuration.

Sponsoring buys maintenance, not priority. It does not queue your issue and does not entitle anyone to a feature. If you need a specific behaviour on a deadline, the reliable path is to write it \u2014 or fund someone to write it \u2014 and open a pull request.

"},{"location":"about/community/#what-to-include-when-you-ask","title":"What to include when you ask","text":"

Most unanswered questions are unanswered because they cannot be reproduced. Four things turn a vague report into one someone can act on:

  • The exact ReactPy version. Run pip show reactpy and paste it. Behaviour shifts between pre-releases, so 2.0.0b13 and 2.0.0 are different answers.
  • The Python version and operating system.
  • The smallest snippet that still misbehaves. Cutting an app down to one component usually finds the bug before you post it.
  • Both sides of the comparison: what happened, and what you expected. For a rendering problem, include what the browser showed, not only what the code says.

Pitfall

\"It doesn't work\" with no traceback is not a question anyone can answer, and the error you think is irrelevant often is not the relevant one. Include the full traceback and the server-side console output. ReactPy logs failing event handlers and effects on the server while the browser stays silent, so a UI that merely stops responding usually has a server line that explains it \u2014 see Creating VDOM Event Handlers.

"},{"location":"about/community/#recap","title":"Recap","text":"
  • Ask in Discussions; open an issue only for a confirmed defect.
  • Real-time chat is on Discord; follow up anything durable in Discussions so it stays searchable.
  • The code of conduct governs every one of those spaces.
  • Integration packages live in sibling repos; file bugs against the repo that owns the behaviour.
  • Reproducible reports get answers: versions, smallest failing snippet, and the server log.
"},{"location":"about/community/#related","title":"Related","text":"
  • Contributing Code \u2014 turning a bug you found into a merged fix
  • Contributing Documentation \u2014 fixing a page that misled you
  • Running Tests \u2014 the commands to run before opening a pull request
  • Tools, Libraries, and Packages \u2014 choosing between built-in and third-party
"},{"location":"about/docs/","title":"Contributing Documentation","text":""},{"location":"about/docs/#overview","title":"Overview","text":"

Most of these pages were ported from the ReactJS documentation, and the port is the whole job: a page is done when every claim in it is true of ReactPy and every snippet in it runs. That is a lower bound on effort and an upper bound on trustworthiness, which is the trade this project made deliberately. This page is how to do the work and how to know it is finished.

You will learn

  • What makes a ported page \"done\", and how the \ud83d\udea7 marker works
  • The page structure, admonitions, and link conventions this site uses
  • Why snippets live in docs/examples/ instead of inside the markdown
"},{"location":"about/docs/#where-things-live","title":"Where things live","text":"
docs/\n\u251c\u2500\u2500 mkdocs.yml          # nav: \u2014 every page must be listed here\n\u251c\u2500\u2500 src/                # the pages, in markdown\n\u2502   \u251c\u2500\u2500 learn/          #   tutorials and concepts\n\u2502   \u251c\u2500\u2500 reference/      #   API reference\n\u2502   \u2514\u2500\u2500 about/          #   this section\n\u251c\u2500\u2500 examples/           # runnable Python, one directory per page slug\n\u2514\u2500\u2500 overrides/          # template overrides\n

Page files are kebab-case.md; example directories are the same name with underscores (learn/scaling-up-with-reducer-and-context.md \u2192 examples/scaling_up_with_reducer_and_context/). A new page must be added to nav: in mkdocs.yml, but the build will not tell you when you forget: a page missing from nav: is still built and served, and mkdocs build --strict exits 0. The only trace is an INFO line listing the unlisted pages. Read it: every page currently on that list carries a Planned / Undeveloped banner in its body and can be ignored, which is exactly how a page you did mean to publish would hide among them. Adding the entry is what puts the page in the site's navigation, which is the actual reason to do it.

"},{"location":"about/docs/#what-done-means","title":"What \"done\" means","text":"

A page is authored, not migrated. Concretely:

  • Every code sample is ReactPy that runs. No JSX, no className= outside an attribute dict, no hook that does not exist.
  • Every behavioural claim has been checked against the package or its tests \u2014 by reading the source, and for anything about rendering or events, by running it.
  • React-only sections are removed or rewritten, not translated.
  • The \ud83d\udea7 badge is gone from its nav: entry.

Note

\ud83d\udea7 in the navigation means \"this is still a ReactJS page with the React names swapped\". It is a promise to the reader that the content is unreliable, so removing it is the meaningful act \u2014 the badge should never come off before the checks above are true. \ud83d\udeab means the page is blocked on something outside this project and is parked; commented-out nav: entries work the same way.

"},{"location":"about/docs/#port-do-not-rename","title":"Port, do not rename","text":"

The pages derive from react.dev. Editing means deleting what is false as much as replacing what is named differently, and the failure mode is a page that reads fluently and is wrong.

Things that do not survive a rename:

  • Hooks that do not exist. ReactPy has twelve hooks. There is no use_id, use_transition, use_deferred_value, use_layout_effect, use_insertion_effect, use_imperative_handle, or use_sync_external_store. Some of the react.dev pages for them are parked as \ud83d\udeab; use_id has no page at all, because the upstream issue was closed as not planned. Never port a page that depends on one as if it worked.
  • Naming rules. Components are snake_case and lowercase \u2014 my_button, not MyButton. React's capital-letter rule does not apply, because ReactPy distinguishes a component from a tag by the call, not the case. Attribute keys stay camelCase (className, onClick), because those pass through to the DOM untouched.
  • JSX mechanics. Children are positional arguments after an optional attribute dict. There is no JSX, and no PSX in this release.
  • StrictMode and memo. Neither exists. React's double-invoke warnings and its \"skip re-rendering unchanged children\" optimisation have no ReactPy equivalent, so port those sections as a warning that ReactPy will not catch the problem for you.
  • && rendering. {cond && <X/>} has no safe Python spelling \u2014 False and X leaks the word False into the page, since only None renders nothing. Rewrite such examples rather than transliterating them.
  • Dependency arrays. use_effect, use_memo, and use_callback infer dependencies from the closure when you omit the argument. React's \"omit the array to run after every render\" is simply wrong here.

Pitfall

A page can pass every automated check while teaching something false, because the checks are syntactic. The ReactJS FIXME that sat in thinking-in-react.md claiming checked was not delivered was wrong \u2014 a checkbox onChange handler reads it back as await event.target.checked. The only defence is running the example against a real layout and reading what the client actually answered.

"},{"location":"about/docs/#page-structure","title":"Page structure","text":"

Every authored page opens the same way: an ## Overview heading, an intro paragraph in a <p class=\"intro\"> block, then a You will learn summary.

## Overview\n\n<p class=\"intro\" markdown>\n\nOne or two paragraphs that say what the page solves and why it matters in ReactPy specifically.\n\n</p>\n\n!!! summary \"You will learn\"\n\n    -   Bullet\n    -   Bullet\n

Then the body as ## and ### sections, closing with ## Recap (bulleted, factual) and ## Related (links with a dash and a phrase explaining each).

Admonitions come from the Material theme plus assets/css/admonition.css. The set in use:

Admonition For !!! summary \"You will learn\" Objectives, once, after the intro !!! abstract \"Note\" Clarification that is not a warning !!! warning \"Pitfall\" A mistake the reader is likely to make !!! info \"Deep Dive\" Deeper mechanics a reader can skip !!! example \"Terminal\" A shell session

ReactJS's <Pitfall>, <DeepDive>, <Recap>, <Challenges>, <Solution>, and <Hint> JSX tags have no MkDocs equivalent. Convert them to admonitions or headings; never copy them through, since an unrecognised tag renders as literal text.

Links are relative .md paths from the current file \u2014 ../reference/use-state.md, ./extracting-state-logic-into-a-reducer.md#the-rule-reactpy-adds \u2014 not site-absolute paths like /learn/..., which break under versioned builds and make --strict unable to verify them.

Code fences: ```python linenums=\"0\" for fragments, bare ```python for full-file tabs, and hl_lines=\"3-4 7\" to mark the lines under discussion. Inline code that is Python gets the #!python prefix (\\#!python use_state``) so it highlights.

"},{"location":"about/docs/#examples-are-the-source-of-truth","title":"Examples are the source of truth","text":"

Snippets are not typed into the markdown. They are real files under docs/examples/<page_slug>/, pulled in by the include plugin:

=== \"app.py\"\n\n    ```python\n\n    @component\n    def my_button():\n        return html.button(\"I'm a button!\")\n\n    ```\n\n=== \":material-play: Run\"\n\n    ```python\n    # TODO\n    ```\n

The rules that make this work:

  • The file is a complete, importable module \u2014 black-formatted at 88 columns, imports at the top.
  • A # start marker on its own line is what the include begins after; # end stops it. Crop at the marker so the marker itself never renders on the page.
  • A file with no marker is included whole.
  • Multiple variants of one idea go in sibling directories (task_state_props/, task_context/, task_wiring/), one tabbed group per example.
  • The :material-play: Run tab is a standing placeholder. Keep it.

Because the file is included, a snippet cannot drift from a runnable example \u2014 but nothing in the build proves the example behaves. Mount it and check the DOM before you write \"this renders a list\". When a page compares several versions, drive all of them through the same click sequence and assert the same observable output; \"it looks the same\" is how two versions of an example quietly disagree.

Also list children of a component with key= when they come from a list \u2014 html.ul([row(item, key=item[\"id\"]) for item in items]). Omitting it renders fine and emits a debug-mode warning, which is exactly the lesson the page about keys is trying to teach.

"},{"location":"about/docs/#verifying-a-page","title":"Verifying a page","text":"

From the repository root:

hatch fmt --check docs/\npyright --pythonpath <your-env>/bin/python docs/\ncd docs && mkdocs build --strict; echo \"exit=$?\"\n

Then run the examples themselves and open the built page in a browser. mkdocs build --strict catches broken internal links and unresolvable includes \u2014 both abort the build with a nonzero exit. Two things it does not catch: a page missing from nav: (an INFO line, exit 0), and a link whose #anchor does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing.

You are the only gate. The single workflow on new-docs runs mkdocs gh-deploy --force on push, and gh-deploy does not pass --strict \u2014 a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live.

Pitfall

Do not trust a component that renders without error. Set REACTPY_DEBUG=1 when probing: with debug off, a component that raised produces {\"tagName\": \"\", \"error\": \"\"} \u2014 a blank region and a 200 response. See Running Tests for the harness traps that make a broken example look green.

"},{"location":"about/docs/#process","title":"Process","text":"

Branch from new-docs, one page per branch, and use the same Conventional Commit subjects as the history there \u2014 docs(learn): author X for ReactPy v2. A commit that removes a \ud83d\udea7 badge from nav: should be the same commit that authored the page, never a separate \"mark done\" commit that could be cherry-picked alone.

No CHANGELOG.md entry: documentation work is not user-facing in the sense the changelog means. See Changelog.

Note

docs/src/dictionary.txt exists for the spellcheck plugin, which is currently commented out in mkdocs.yml. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off.

"},{"location":"about/docs/#recap","title":"Recap","text":"
  • A page is done when it runs and every claim is verified \u2014 then remove its \ud83d\udea7 badge.
  • Port, do not rename: delete JSX, non-existent hooks, StrictMode, memo, && rendering, and React's dependency-array semantics.
  • Fixed structure: Overview, intro paragraph, You will learn, sections, Recap, Related.
  • Relative .md links; admonitions instead of ReactJS's JSX tags.
  • Snippets are includes from docs/examples/, cropped at # start, formatted at 88 columns.
  • --strict checks links and nothing about behaviour. Run the example.
"},{"location":"about/docs/#related","title":"Related","text":"
  • Running Tests \u2014 the exact commands and how to read their failures
  • Contributing Code \u2014 branch and pull request conventions
  • Changelog \u2014 what does and does not deserve an entry
  • Community \u2014 where to ask about a page before rewriting it
"},{"location":"about/licenses/","title":"Licenses","text":""},{"location":"about/licenses/#overview","title":"Overview","text":"

ReactPy's source is MIT-licensed. Much of the prose you are reading is derived from the ReactJS documentation, which carries a separate Creative Commons licence, and the two do not cover the same things. This page states which licence applies to what, and what you owe when you reuse either.

You will learn

  • Which licence covers the code, and which covers these pages
  • Why the distinction matters when you copy from one side
  • Where the third-party components in a ReactPy install come from
"},{"location":"about/licenses/#source-code","title":"Source code","text":"

The ReactPy package is released under the MIT licence, copyright Reactive Python and affiliates. The full text lives in the license file at the repository root and is reproduced below.

MIT is permissive: use, copy, modify, merge, publish, distribute, sublicense, and sell, subject to keeping the copyright notice and licence text in copies of the software. It comes with no warranty, and liability is excluded.

## The MIT License (MIT)\n\n#### Copyright (c) Reactive Python and affiliates.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n
"},{"location":"about/licenses/#documentation","title":"Documentation","text":"

These pages are a different body of work from the code, and they are separately encumbered.

Much of this documentation \u2014 including its layout and much of its content \u2014 was created with heavy influence from react.dev, which is licensed under the Creative Commons Attribution 4.0 International licence. ReactPy's behaviour often mirrors React's closely enough that the documents paraphrase, and in places copy, React's language and examples.

Note

CC BY 4.0 is also permissive, but its condition is attribution rather than keeping a licence block. If you copy substantial prose from these pages into your own documentation, credit ReactPy and, where the text is close to React's original, acknowledge the ReactJS documentation as the further source. Attributing to ReactPy alone is not sufficient for the passages that came from React.

The practical boundary: code you write against ReactPy is yours, unencumbered by either licence. These pages are text under a credit-bearing licence. A snippet in docs/examples/ is code that happens to be inside a document \u2014 treat it as MIT, since its purpose is to be pasted into a project.

"},{"location":"about/licenses/#third-party-components","title":"Third-party components","text":"

A working ReactPy install bundles more than this repository's source: the browser-side client, the vendored JavaScript dependencies, and, for client-side rendering, PyScript. Each keeps its own upstream licence, shipped with the distribution.

Check what a release actually contains rather than trusting a summary \u2014 the package metadata and the static/ directory of the release are authoritative, and the licence files travel inside the wheel.

"},{"location":"about/licenses/#licensing-your-own-contribution","title":"Licensing your own contribution","text":"

The pull request template ends with an agreement that contributions comply with the project's open source licences. It is a checkbox, but it is the whole of the provenance control this project has, so it is worth taking literally:

  • Do not paste code you do not have the right to redistribute. Code copied from a licence-incompatible project, or from an answer on a forum, is the common failure.
  • Do not copy substantial prose from react.dev into these pages without acknowledging the source on the page or in the commit.
  • If you adapted something, say what and from where in the pull request description. It costs you a sentence and saves the maintainer an investigation.
"},{"location":"about/licenses/#recap","title":"Recap","text":"
  • Source code: MIT, in LICENSE.md at the repository root.
  • These pages: derived from the ReactJS docs under CC BY 4.0 \u2014 attribution is the obligation.
  • Snippets in docs/examples/ exist to be pasted; treat them as MIT code.
  • Bundled JavaScript and PyScript keep their upstream licences, shipped with the release.
  • Contributions must be yours to license; say what you adapted and from where.
"},{"location":"about/licenses/#related","title":"Related","text":"
  • Contributing Documentation \u2014 how pages here are authored, and where React-derived text comes from
  • Contributing Code \u2014 the pull request checklist, including the licence agreement
  • Community \u2014 how to reach a maintainer about a licensing question
  • Changelog \u2014 the release history
"},{"location":"about/running-tests/","title":"Running Tests","text":""},{"location":"about/running-tests/#overview","title":"Overview","text":"

There are two test suites in this project, and they live in different places. The Python and JavaScript suites exercise ReactPy itself and run through hatch; the documentation checks exercise the pages you are reading and run through mkdocs. Knowing which one a failing command belongs to is most of debugging it.

You will learn

  • How to run the Python test suite, and why it needs a browser installed
  • How to run the documentation build checks yourself, since no CI runs them for you
  • Which command failures are yours and which are environmental

Note

The ReactPy package and its tests/ directory live on the main branch. This documentation site is authored on the new-docs branch, which contains only docs/ \u2014 running a test command there collects zero tests rather than failing, which reads like a pass and is not one. If hatch test reports no tests ran, you are on the wrong branch, not on a clean tree.

"},{"location":"about/running-tests/#prerequisites","title":"Prerequisites","text":"

The development environment is managed by Hatch, which creates and maintains its own virtual environments. Install Hatch, then clone:

git clone https://github.com/reactive-python/reactpy.git\ncd reactpy\n

You also need Bun for the JavaScript packages. Python must be 3.11 or newer \u2014 requires-python in pyproject.toml is >=3.11, and the test matrix runs 3.11 through 3.14.

Pitfall

Do not install the project's dependencies with pip install -r into your own environment. Hatch owns those environments, and a hand-built one drifts from CI in ways that look like your fault. The one exception is the documentation requirements, which are installed separately below.

"},{"location":"about/running-tests/#python-tests","title":"Python tests","text":"

The suite is pytest-based and runs under Hatch:

hatch test                                    # current Python version\nhatch test --all                              # every version in the matrix\nhatch test -k test_use_connection             # a single test by name\n

CI runs hatch test --parallel --retries 10 across three operating systems and four Python versions, and a separate coverage job runs hatch test --parallel --cover --retries 10.

The suite needs a browser. Each test run installs Playwright's Chromium, builds the JavaScript client, and builds a wheel before it invokes pytest \u2014 that is what the hatch-test environment's run script does. A first run is therefore slow, and a failure inside Chromium installation looks identical to a failure in the test itself. Read the top of the output, not the bottom.

playwright install chromium\nplaywright install-deps chromium   # optional; fails harmlessly on some systems\n

Tests are session-scoped around one browser and one backend server, defined in tests/conftest.py, and that file also turns on REACTPY_DEBUG and REACTPY_ASYNC_RENDERING for the whole session. Two consequences:

  • A test that passes alone and fails in the suite may be reading state a previous test left behind. Reproduce with -k before you chase it.
  • Because debug mode is on in tests and off by default in production, some errors surface only in the suite. Debug-mode checks such as REACTPY_CHECK_VDOM_SPEC are exactly this.

Configuration lives under [tool.pytest.ini_options] in pyproject.toml: --strict-config, --strict-markers, xfail_strict, asyncio_mode = \"auto\", testpaths = \"tests\", and a 120-second per-test timeout. xfail_strict means an expected failure that unexpectedly passes is itself a failure, so an xfail you fixed must be deleted rather than left alone.

"},{"location":"about/running-tests/#what-the-helpers-are-for","title":"What the helpers are for","text":"

reactpy.testing exists because a UI is only correct once it renders in a browser. The acceptance bar is that the real artefact behaves, not that a function returned the expected shape.

  • DisplayFixture \u2014 serves a component and gives you a Playwright page to drive it: await display.show(my_component), then display.page.
  • BackendFixture \u2014 the server side, usually paired with DisplayFixture as in conftest.py.
  • poll(...) \u2014 retries a condition until it holds or the timeout expires. Use it instead of asyncio.sleep, which passes vacuously on a slow machine and flakes on a fast one.
  • HookCatcher and StaticEventHandler \u2014 capture a component's life-cycle hook and an event handler's target, for delivering events without a click.
  • capture_reactpy_logs, assert_reactpy_did_log, assert_reactpy_did_not_log \u2014 assert on server-side log output, which is where ReactPy puts swallowed handler and effect errors.

conftest.py also installs an autouse fixture that fails a test if any ReactPy log record carries an exception. That is deliberate: an error logged server-side while the browser stays quiet is the most common way a ReactPy bug hides. If a new test trips it, the exception is real even when the assertion you wrote passes.

"},{"location":"about/running-tests/#javascript-tests","title":"JavaScript tests","text":"

The client packages are tested with Bun:

hatch run javascript:test     # run the JavaScript tests\nhatch run javascript:check    # lint and format check\nhatch run javascript:fix      # lint and write fixes to disk\nhatch run javascript:build    # rebuild the client bundles\n

Changes under src/js/ need a rebuild before Python tests can see them. The test script does this itself, but a manual run against a stale bundle will not reflect your edit.

"},{"location":"about/running-tests/#linting-and-type-checking","title":"Linting and type checking","text":"
hatch fmt                     # format and lint, writing fixes\nhatch fmt --check             # the CI form: report, do not write\nhatch run python:type_check   # pyright over src/reactpy\n

CI runs hatch fmt src/reactpy --check followed by hatch run python:type_check.

Pitfall

Run hatch fmt --check before you commit, not hatch fmt. Bare hatch fmt rewrites files, so a run that \"passes\" has already edited your working tree \u2014 including files you did not intend to touch. Scope it (hatch fmt docs/) if you only want part of the tree.

Formatting is Ruff at a line length of 88. The rules that bite most in this repository are set in pyproject.toml; docs/**/*.py already ignores E402, S102, T201, TID252, RUF059, and INP001, because example files are teaching snippets rather than importable modules.

"},{"location":"about/running-tests/#documentation-checks","title":"Documentation checks","text":"

Run these from the repository root before you commit.

Pitfall

Nothing checks the documentation for you. The only workflow on new-docs installs the requirements and runs mkdocs gh-deploy --force on push \u2014 which does not pass --strict, so a broken internal link exits 0 and deploys anyway. The commands below are the gate only because you run them; there is no CI to catch a miss, and the live site is where an uncaught one appears.

hatch fmt --check docs/             # formatting and lint\ncd docs && mkdocs build --strict    # build, fail on warnings\n

Plus the type check over the example files, which are real Python:

pyright --pythonpath <your-env>/bin/python docs/\n

mkdocs build --strict is the important one: it turns broken internal links and unresolvable includes into build failures, both of which abort with a nonzero exit. It does not fail on two things you would expect it to: a page missing from nav: (only an INFO line, and the build still exits 0 while serving the orphaned page), and a link whose #anchor does not exist inside an otherwise valid target file (also exit 0). Read that nav: list yourself and check anchors by eye. Nor can it tell you whether a snippet works: the examples in docs/examples/ are the source of truth for every snippet on the site, and a snippet that does not run is a docs bug the build will not catch on its own. That part is on you.

Pitfall

Do not judge a build by its log output alone. A grep-style check on the output hides a nonzero exit:

cd docs && mkdocs build --strict; echo \"exit=$?\"\n

In a bash pipeline, use echo \"${PIPESTATUS[0]}\" instead \u2014 a trailing | grep that finds a match makes the pipeline's exit code meaningless.

"},{"location":"about/running-tests/#running-the-site-locally","title":"Running the site locally","text":"
pip install -r docs/requirements.txt\ncd docs && mkdocs serve\n

Then follow the printed URL. Editing any file under docs/src/ or docs/examples/ reloads the page.

The requirements file pins mkdocs-material==9.7.7. Earlier revisions pinned 9.4.0, which pulled in regex~=2022.4 and resolved to a release shipping wheels only through CPython 3.11 \u2014 on newer Pythons that meant a source build and a hard failure without a C compiler. The current pin dropped the regex dependency altogether, so the install works on 3.12 and up. If you are on an older branch and the docs install dies inside regex, that is the reason: use Python 3.11 or raise the pin.

"},{"location":"about/running-tests/#checking-your-work","title":"Checking your work","text":"

Before calling anything done, run the gates in this order and read every failure:

  1. hatch fmt --check docs/ \u2014 formatting and lint
  2. pyright docs/ \u2014 types in the examples
  3. cd docs && mkdocs build --strict \u2014 links, includes, nav
  4. The relevant tests, for code changes
  5. The real user scenario, by hand \u2014 open the page, click the control

Steps 1\u20134 can all pass while the thing you built does not work. A status is not an acceptance test: run the artefact. For a docs example that means mounting it in a browser and confirming the DOM changed, because a ReactPy component can render, error server-side, and show a blank region while the HTTP response still reads 200.

Deep Dive

Two harness traps have made broken examples look green more than once. First, the decorated function is a constructor: counter itself has no .render() and Layout(counter) raises TypeError: Expected a ReactPy component, so you must pass an instance. The instance does have .render(), but calling it outside a Layout raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? for any component that uses a hook \u2014 which silently skips the behaviour you meant to test. Render through async with Layout(counter()) as layout: await layout.render(). Second, ReactPy formats exception text into the model only when REACTPY_DEBUG is on; with it off, a component that raised renders as {\"tagName\": \"\", \"error\": \"\"} \u2014 an empty region and no message. Set the variable when you are probing failures, and unset it when you are checking what a user would actually see.

"},{"location":"about/running-tests/#recap","title":"Recap","text":"
  • Two suites: hatch test for ReactPy (on main), mkdocs build --strict for the docs.
  • hatch test reporting no tests ran means the suite is not there, not that it passed.
  • Python tests need Playwright's Chromium and build a wheel before pytest starts.
  • conftest.py forces debug mode and fails any test that logs an exception.
  • Run hatch fmt --check, never bare hatch fmt, before committing.
  • Check exit codes, not log text; then verify the real behaviour by hand.
"},{"location":"about/running-tests/#related","title":"Related","text":"
  • Contributing Code \u2014 branches, commits, and the pull request checklist
  • Contributing Documentation \u2014 page conventions and the example-file workflow
  • Changelog \u2014 when a change needs an entry
"},{"location":"learn/add-react-to-an-existing-project/","title":"Add React to an Existing Project","text":""},{"location":"learn/add-react-to-an-existing-project/#overview","title":"Overview","text":"

If you want to add some interactivity to your existing project, you don't have to rewrite it in React. Add React to your existing stack, and render interactive React components anywhere.

"},{"location":"learn/add-react-to-an-existing-project/#using-react-for-an-entire-subroute-of-your-existing-website","title":"Using React for an entire subroute of your existing website","text":"

Let's say you have an existing web app at example.com built with another server technology (like Rails), and you want to implement all routes starting with example.com/some-app/ fully with React.

"},{"location":"learn/add-react-to-an-existing-project/#using-an-asgi-subroute","title":"Using an ASGI subroute","text":"

Here's how we recommend to set it up:

  1. Build the React part of your app using one of the ReactPy executors.
  2. Specify /some-app/ as the base path in your executor's kwargs (path_prefix=\"/some-app/\"). The trailing slash is load-bearing: executors/asgi/middleware.py validates the value and a missing slash raises ValueError from the constructor \u2014 Invalid path_prefix of \"/some-app\". URL path must end with a forward slash. \u2014 so the app never starts.
  3. Configure your server or a proxy so that all requests under /some-app/ are handled by your React app.

This ensures the React part of your app can benefit from the best practices baked into those frameworks.

"},{"location":"learn/add-react-to-an-existing-project/#using-static-site-generation-ssg","title":"Using static site generation (SSG)","text":"

Support for SSG is coming in a future version.

"},{"location":"learn/add-react-to-an-existing-project/#using-react-for-a-part-of-your-existing-page","title":"Using React for a part of your existing page","text":"

Let's say you have an existing page built with another Python web technology (ASGI or WSGI), and you want to render interactive React components somewhere on that page.

The exact approach depends on your existing page setup, so let's walk through some details.

"},{"location":"learn/add-react-to-an-existing-project/#using-asgi-middleware","title":"Using ASGI Middleware","text":"

ReactPy supports running as middleware for any existing ASGI application. ReactPy components are embedded into your existing HTML templates using Jinja2. You can use any ASGI framework, however for demonstration purposes we have selected Starlette for the example below.

First, install ReactPy, Starlette, and your preferred ASGI webserver.

Terminal

pip install reactpy[asgi,jinja] starlette uvicorn[standard]\n

Next, configure your ASGI framework to use ReactPy's Jinja2 template tag. The method for doing this will vary depending on the ASGI framework you are using. Below is an example that follow's Starlette's documentation:

Note

The ReactPyJinja extension enables a handful of template tags that allow you to render ReactPy components in your templates. The component tag is used to render a ReactPy SSR component, while the pyscript_setup and pyscript_component tags can be used together to render CSR components.

from jinja2 import Environment, FileSystemLoader\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\nfrom starlette.templating import Jinja2Templates\n\nfrom reactpy.templatetags import ReactPyJinja\n\njinja_templates = Jinja2Templates(\n    env=Environment(\n        loader=FileSystemLoader(\"path/to/my_templates\"),\n        extensions=[ReactPyJinja],\n        autoescape=True,\n    )\n)\n\n\nasync def example_webpage(request):\n    return jinja_templates.TemplateResponse(request, \"my_template.html\")\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", example_webpage)])\n

Now you will need to wrap your existing ASGI application with ReactPy's middleware, define the dotted path to your root components, and render your components in your existing HTML templates.

main.pymy_components.pymy_template.html
from jinja2 import Environment, FileSystemLoader\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\nfrom starlette.templating import Jinja2Templates\n\nfrom reactpy.executors.asgi import ReactPyMiddleware\nfrom reactpy.templatetags import ReactPyJinja\n\njinja_templates = Jinja2Templates(\n    env=Environment(\n        loader=FileSystemLoader(\"path/to/my_templates\"),\n        extensions=[ReactPyJinja],\n        autoescape=True,\n    )\n)\n\n\nasync def example_webpage(request):\n    return jinja_templates.TemplateResponse(request, \"my_template.html\")\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", example_webpage)])\nreactpy_app = ReactPyMiddleware(starlette_app, [\"my_components.hello_world\"])\n
from reactpy import component, html\n\n\n@component\ndef hello_world():\n    return html.div(\"Hello World\")\n
<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <title>ReactPy in Django</title>\n</head>\n\n<body>\n  {% component \"my_components.hello_world\" %}\n</body>\n\n</html>\n

Finally, use your webserver of choice to start ReactPy:

Terminal

uvicorn main:reactpy_app\n
"},{"location":"learn/add-react-to-an-existing-project/#using-wsgi-middleware","title":"Using WSGI Middleware","text":"

Support for WSGI executors is coming in a future version.

"},{"location":"learn/add-react-to-an-existing-project/#external-executors","title":"External Executors","text":"

Note

External executors exist outside ReactPy's core library and have significantly different installation and configuration instructions.

Make sure to follow the documentation for setting up your chosen external executor.

"},{"location":"learn/add-react-to-an-existing-project/#django","title":"Django","text":"

Django is a full-featured web framework that provides a batteries-included approach to web development.

Due to it's batteries-included approach, ReactPy has unique features only available to this executor.

To learn how to configure Django for ReactPy, see the ReactPy-Django documentation.

"},{"location":"learn/choosing-the-state-structure/","title":"Choosing the State Structure","text":""},{"location":"learn/choosing-the-state-structure/#overview","title":"Overview","text":"

Structuring state well is the difference between a component that is pleasant to modify and one that is a constant source of bugs. ReactPy makes these guidelines matter more than they do in React, because it detects state changes with == rather than by identity, and because it re-renders every child of a component that re-renders. Each principle below comes with the failure it prevents, measured.

You will learn

  • When to group state into one object, and what that trade actually costs
  • Why contradictions, redundancy, and duplication each buy you a bug
  • How ReactPy's == comparison changes the \"never mutate state\" rule
  • What a flat state shape does to the cost of an update
"},{"location":"learn/choosing-the-state-structure/#principles-for-structuring-state","title":"Principles for structuring state","text":"

When you write a component that holds state, you choose how many state variables to use and what shape their data has. A poor shape still runs correctly; it just makes mistakes easier to make. Five principles point the other way:

  1. Group related state. If you always update two or more state variables together, merge them into one.
  2. Avoid contradictions in state. If two pieces of state can disagree, you have left room for a bug.
  3. Avoid redundant state. If you can calculate a value from props or existing state during render, do not put it in state.
  4. Avoid duplication in state. Data stored in two places must be kept in sync in two places.
  5. Avoid deeply nested state. To change one leaf you must rebuild every ancestor.

The goal is to make state easy to update without introducing mistakes \u2014 the same reason a database engineer normalizes a schema. To paraphrase Einstein: make your state as simple as it can be, but no simpler.

One fact about ReactPy changes how all five pay off, so it comes first.

"},{"location":"learn/choosing-the-state-structure/#reactpy-compares-state-with","title":"ReactPy compares state with ==","text":"

use_state compares the value you set against the current one with strictly_equal, which falls through to ==. If the two compare equal, nothing re-renders. Verified against a real Layout, one press each:

You set over Renders {\"items\": [1, 2]} \u2014 a brand-new dict {\"items\": [1, 2]} 0 [1, 2, 3] \u2014 a brand-new list [1, 2, 3] 0 [] [] 0 {\"a\": {\"b\": 2}} {\"a\": {\"b\": 1}} 1 [1, 2] (1, 2) 1

The first three rows are the ones to internalise. In React, setting a fresh object always re-renders, because React compares by identity. In ReactPy, a new object that reads the same as the old one is discarded.

That makes the \"never mutate state\" rule stricter, not looser:

rows[\"items\"].append(\"x\")\nset_rows(rows)  # same object, and == equal -> no render at all\n

Verified: three presses of that handler leave the rendered count at n=0 with the component body having run once. The mutation did happen \u2014 the list really grew \u2014 but nothing re-rendered to show it, so the screen and the data now disagree. Build a new object instead ({**rows, \"items\": [*rows[\"items\"], \"x\"]}) and the update becomes visible.

It also means a tuple and a list are different states: [1, 2] over (1, 2) renders, because strictly_equal checks type(x) is type(y) before anything else. Pick one container type per field. See Lifecycle of Reactive Effects for the full comparison rules.

"},{"location":"learn/choosing-the-state-structure/#group-related-state","title":"Group related state","text":"

Should you use several state variables or one object?

Technically either works. But if two state variables always change together, put them in one state variable, so there is no second setter call to forget. Here the two coordinates are separate, and one handler forgets the second call:

app.py Run
@component\ndef marker():\n    x, set_x = use_state(20)\n    y, set_y = use_state(20)\n\n    async def move_diagonally(event):\n        # The classic mistake: this handler remembers x and forgets y.\n        set_x(x + STEP)\n\n    async def move_correctly(event):\n        set_x(x + STEP)\n        set_y(y + STEP)\n\n    async def reset(event):\n        set_x(20)\n        set_y(20)\n\n    return html.div(\n        html.button({\"onClick\": move_diagonally}, \"Move (forgot y)\"),\n        html.button({\"onClick\": move_correctly}, \"Move (both)\"),\n        html.button({\"onClick\": reset}, \"Reset\"),\n        html.div(\n            STYLE,\n            html.div(\n                {\n                    \"style\": {\n                        \"position\": \"absolute\",\n                        \"left\": f\"{x}px\",\n                        \"top\": f\"{y}px\",\n                        \"width\": \"18px\",\n                        \"height\": \"18px\",\n                        \"borderRadius\": \"50%\",\n                        \"background\": \"#d40000\",\n                    }\n                }\n            ),\n        ),\n        html.p({\"id\": \"coords\"}, f\"x={x}  y={y}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Two state variables\"), marker())\n
# TODO\n

Press Move (forgot y): the dot travels horizontally and the readout confirms the split \u2014 x=44 y=20. Every later move compounds it, because each handler builds on the state it already has.

With one object, the mistake has nowhere to live:

app.py Run
@component\ndef marker():\n    position, set_position = use_state({\"x\": 20, \"y\": 20})\n\n    async def move_diagonally(event):\n        # One setter, one object: there is no second call left to forget.\n        set_position({\"x\": position[\"x\"] + STEP, \"y\": position[\"y\"] + STEP})\n\n    async def reset(event):\n        set_position({\"x\": 20, \"y\": 20})\n\n    return html.div(\n        html.button({\"onClick\": move_diagonally}, \"Move diagonally\"),\n        html.button({\"onClick\": reset}, \"Reset\"),\n        html.div(\n            STYLE,\n            html.div(\n                {\n                    \"style\": {\n                        \"position\": \"absolute\",\n                        \"left\": f\"{position['x']}px\",\n                        \"top\": f\"{position['y']}px\",\n                        \"width\": \"18px\",\n                        \"height\": \"18px\",\n                        \"borderRadius\": \"50%\",\n                        \"background\": \"#d40000\",\n                    }\n                }\n            ),\n        ),\n        html.p({\"id\": \"coords\"}, f\"x={position['x']}  y={position['y']}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"One state variable\"), marker())\n
# TODO\n

Two presses of Move diagonally give x=68 y=68. There is no second setter to omit: one set_position call carries both axes, and the dict you build has to say something about each one.

The trade is worth naming, because it is not free. A single object means every update rebuilds the whole object, and position[\"x\"] is noisier than x. ReactPy does not sweeten it with fewer renders: verified, two presses of \"move both\" cost two renders in the separate version and two in the grouped version, and moving only x costs one render in both. Group related state to remove a class of bug, not to save work.

"},{"location":"learn/choosing-the-state-structure/#avoid-contradictions-in-state","title":"Avoid contradictions in state","text":"

A feedback form tracks its send with two booleans:

app.py Run
@component\ndef feedback():\n    text, set_text = use_state(\"\")\n    is_sending, set_is_sending = use_state(False)\n    is_sent, set_is_sent = use_state(False)\n\n    async def submit(event):\n        set_is_sending(True)\n        await asyncio.sleep(DELAY)\n        # Forget either line below and the two flags disagree forever.\n        set_is_sending(False)\n        set_is_sent(True)\n\n    async def submit_with_bug(event):\n        set_is_sending(True)\n        await asyncio.sleep(DELAY)\n        set_is_sent(True)  # set_is_sending(False) was never called\n\n    if is_sent:\n        return html.div(\n            html.h3(\"Thanks for your feedback!\"),\n            html.p({\"id\": \"flags\"}, f\"is_sending={is_sending}  is_sent={is_sent}\"),\n        )\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.p(\"How was your stay at The Prancing Pony?\"),\n        html.textarea(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"disabled\": is_sending,\n            }\n        ),\n        html.br(),\n        html.button({\"onClick\": submit, \"disabled\": is_sending}, \"Send\"),\n        html.button(\n            {\"onClick\": submit_with_bug, \"disabled\": is_sending}, \"Send (buggy)\"\n        ),\n        (html.p(\"Sending...\") if is_sending else None),\n        html.p({\"id\": \"flags\"}, f\"is_sending={is_sending}  is_sent={is_sent}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Two flags\"), feedback())\n
# TODO\n

The code works \u2014 and it permits a state that should be impossible. Press Send (buggy), which sets is_sent without clearing is_sending, and the readout reports:

is_sending=True  is_sent=True\n

That is not hypothetical. Nothing in the data structure prevents those two flags being true together, so the only thing protecting you is remembering every setter in every handler. The readout exists so you can see the disagreement; in a real component you would instead see a spinner that never stops.

Since the flags can never both be true, replace them with one variable holding one of three values:

app.py Run
@component\ndef feedback(speed=\"slow\"):\n    text, set_text = use_state(\"\")\n    status, set_status = use_state(\"typing\")\n\n    async def submit(event):\n        set_status(\"sending\")\n        await asyncio.sleep(DELAYS[speed])\n        set_status(\"sent\")\n\n    # Plain names, not state: they cannot drift apart because they are derived.\n    is_sending = status == \"sending\"\n    is_sent = status == \"sent\"\n\n    if is_sent:\n        return html.div(html.h3(\"Thanks for your feedback!\"))\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.p(\"How was your stay at The Prancing Pony?\"),\n        html.textarea(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"disabled\": is_sending,\n            }\n        ),\n        html.br(),\n        html.button({\"onClick\": submit, \"disabled\": is_sending}, \"Send\"),\n        (html.p(\"Sending...\") if is_sending else None),\n        html.p({\"id\": \"flags\"}, f\"status={status}\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"One status\"), feedback())\n
# TODO\n

status passes through \"sending\" and ends at \"sent\", which swaps the form for a thank-you. There is no way to be in two states at once, because there is only one variable.

You can still write the readable names:

is_sending = status == \"sending\"\nis_sent = status == \"sent\"\n

Those are plain local variables computed during render, not state. They cannot drift, which is exactly why they should not be state.

"},{"location":"learn/choosing-the-state-structure/#avoid-redundant-state","title":"Avoid redundant state","text":"

A check-in form shows a full name built from two fields:

app.py Run
@component\ndef check_in():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n    full_name, set_full_name = use_state(\"Ada Lovelace\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n        set_full_name(await event.target.value + \" \" + last_name)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n        set_full_name(first_name + \" \" + await event.target.value)\n\n    async def rename_only_first(event):\n        # The bug the extra state variable invites: one writer, one update.\n        set_first_name(\"Grace\")\n\n    async def rename_only_last(event):\n        # This one does not touch full_name either.\n        set_last_name(\"Hopper\")\n\n    return html.div(\n        html.h2(\"Let's check you in\"),\n        html.label(\n            {},\n            \"First name: \",\n            html.input({\"value\": first_name, \"onChange\": handle_first_name_change}),\n        ),\n        html.br(),\n        html.label(\n            {},\n            \"Last name: \",\n            html.input({\"value\": last_name, \"onChange\": handle_last_name_change}),\n        ),\n        html.br(),\n        html.button({\"onClick\": rename_only_first}, \"Rename first\"),\n        html.button({\"onClick\": rename_only_last}, \"Rename last\"),\n        html.p({\"id\": \"ticket\"}, \"Your ticket will be issued to: \", html.b(full_name)),\n    )\n\n\n@component\ndef app():\n    return check_in()\n
# TODO\n

It has three state variables, and one is redundant \u2014 full_name is derivable from the other two at any moment. That is not merely wasteful; it is a second place every writer must remember. Press Rename first: first_name becomes \"Grace\", and the ticket still reads Ada Lovelace.

Remove the state variable and calculate it:

app.py Run
@component\ndef check_in():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n\n    # Not state. Every render recomputes it from whatever the two fields hold.\n    full_name = f\"{first_name} {last_name}\"\n\n    async def rename_only_first(event):\n        set_first_name(\"Grace\")\n\n    async def rename_only_last(event):\n        set_last_name(\"Hopper\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.h2(\"Let's check you in\"),\n        html.label(\n            \"First name: \",\n            html.input(\n                {\n                    \"value\": first_name,\n                    \"onChange\": handle_first_name_change,\n                }\n            ),\n        ),\n        html.br(),\n        html.label(\n            \"Last name: \",\n            html.input(\n                {\n                    \"value\": last_name,\n                    \"onChange\": handle_last_name_change,\n                }\n            ),\n        ),\n        html.br(),\n        html.button({\"onClick\": rename_only_first}, \"Rename first\"),\n        html.button({\"onClick\": rename_only_last}, \"Rename last\"),\n        html.p({\"id\": \"ticket\"}, \"Your ticket will be issued to: \", html.b(full_name)),\n    )\n\n\n@component\ndef app():\n    return check_in()\n
# TODO\n

Now Rename first followed by Rename last gives Grace Hopper. The same two buttons cannot go wrong, because there is only one copy of the name.

The difference is not the render count \u2014 both versions render once per press, verified. It is that the derived version has no code path that produces a stale ticket.

Note

Deriving is the default, but not free when a calculation is genuinely expensive. The order is: derive first, then measure. If a render is too slow, cache the value with use_memo rather than promoting it to state \u2014 a cache adds no render, a state variable does. See You Might Not Need an Effect.

"},{"location":"learn/choosing-the-state-structure/#avoid-duplication-in-state","title":"Avoid duplication in state","text":"

A snack menu lets you choose one item and edit the titles. This version stores the selected item as an object:

app.py Run
@component\ndef menu():\n    items, set_items = use_state(INITIAL_ITEMS)\n    selected_item, set_selected_item = use_state(INITIAL_ITEMS[0])\n\n    async def handle_item_change(item_id, event):\n        new_title = await event.target.value\n        set_items(\n            [\n                {**item, \"title\": new_title} if item[\"id\"] == item_id else item\n                for item in items\n            ]\n        )\n\n    def make_change(item):\n        async def change(event):\n            await handle_item_change(item[\"id\"], event)\n\n        return change\n\n    def make_choose(item):\n        async def choose(event):\n            set_selected_item(item)\n\n        return choose\n\n    return html.div(\n        html.h2(\"What's your travel snack?\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": item[\"id\"]},\n                    html.input(\n                        {\n                            \"value\": item[\"title\"],\n                            \"onChange\": make_change(item),\n                        }\n                    ),\n                    html.button(\n                        {\"onClick\": make_choose(item)},\n                        \"Choose\",\n                    ),\n                )\n                for item in items\n            ]\n        ),\n        html.p({\"id\": \"picked\"}, f\"You picked {selected_item['title']}.\"),\n    )\n\n\n@component\ndef app():\n    return menu()\n
# TODO\n

Press Choose on pretzels, then edit that input to chips. The input updates; the line below it does not:

You picked pretzels.\n

selected_item is a second copy of data that also lives inside items. set_items updated one copy and nothing updated the other \u2014 the same bug as the stale full name, one level deeper.

Hold the id instead and look the item up during render:

app.py Run
@component\ndef menu():\n    items, set_items = use_state(INITIAL_ITEMS)\n    selected_id, set_selected_id = use_state(0)\n\n    # Derived: there is exactly one copy of the title, and it lives in items.\n    selected_item = next(item for item in items if item[\"id\"] == selected_id)\n\n    async def handle_item_change(item_id, event):\n        new_title = await event.target.value\n        set_items(\n            [\n                {**item, \"title\": new_title} if item[\"id\"] == item_id else item\n                for item in items\n            ]\n        )\n\n    def make_change(item_id):\n        async def change(event):\n            await handle_item_change(item_id, event)\n\n        return change\n\n    def make_choose(item_id):\n        async def choose(event):\n            set_selected_id(item_id)\n\n        return choose\n\n    return html.div(\n        html.h2(\"What's your travel snack?\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": item[\"id\"]},\n                    html.input(\n                        {\n                            \"value\": item[\"title\"],\n                            \"onChange\": make_change(item[\"id\"]),\n                        }\n                    ),\n                    html.button(\n                        {\"onClick\": make_choose(item[\"id\"])},\n                        \"Choose\",\n                    ),\n                )\n                for item in items\n            ]\n        ),\n        html.p({\"id\": \"picked\"}, f\"You picked {selected_item['title']}.\"),\n    )\n\n\n@component\ndef app():\n    return menu()\n
# TODO\n

Editing the selected row now updates the line below it, because selected_item is a next(...) over items, re-evaluated on every render. The state went from:

  • items = [{\"id\": 0, \"title\": \"pretzels\"}, ...]
  • selected_item = {\"id\": 0, \"title\": \"pretzels\"}

to:

  • items = [{\"id\": 0, \"title\": \"pretzels\"}, ...]
  • selected_id = 0

Only the id is essential; the rest was a copy. The same reasoning turns a selected object into an id, a filter string into its own fields, and a sorted list into an unsorted list plus a sort.

Pitfall

\"Store the id, derive the object\" meets ReactPy's == comparison in one place worth knowing about. A derived object handed to use_effect as a dependency compares equal every render, so the dependency holds and the effect never restarts \u2014 usually what you want. But that also means a dependency cannot detect a change that == cannot see. Verified with a plain class instance held as a dependency and mutated in place: across three presses the component re-rendered each time and the screen showed n=3, while the effect ran once and kept reporting n=0. Use dict and list, or an immutable @dataclass(frozen=True), for anything that appears in a dependency array. See Removing Effect Dependencies.

"},{"location":"learn/choosing-the-state-structure/#avoid-deeply-nested-state","title":"Avoid deeply nested state","text":"

A travel plan is planets, then continents, then countries. The tempting shape mirrors the picture. Two files make the example \u2014 app.py renders it, data.py holds the tree:

app.pydata.py Run
def mark_visited(node, target_id):\n    \"\"\"Copy this node, and recurse \u2014 the whole tree is rebuilt top-down.\"\"\"\n    if node[\"id\"] == target_id:\n        return {**node, \"visited\": True}\n    return {\n        **node,\n        \"childPlaces\": [\n            mark_visited(child, target_id) for child in node[\"childPlaces\"]\n        ],\n    }\n\n\n@component\ndef place_tree(place, on_visit):\n    children = place[\"childPlaces\"]\n    return html.li(\n        {\"key\": place[\"id\"]},\n        place[\"title\"],\n        html.button(\n            {\"onClick\": on_visit}, \"Visited\" if place.get(\"visited\") else \"Visit\"\n        ),\n        (\n            html.ol([place_tree(child, on_visit) for child in children])\n            if children\n            else None\n        ),\n    )\n\n\n@component\ndef travel_plan():\n    plan, set_plan = use_state(INITIAL_PLAN)\n\n    def make_visit(place_id):\n        # A factory, because a handler has to be a coroutine and a coroutine\n        # cannot be a lambda \u2014 this is how the id gets bound instead.\n        async def visit(event):\n            set_plan(mark_visited(plan, place_id))\n\n        return visit\n\n    return html.div(\n        html.h2(\"Places to visit\"),\n        html.ol(\n            [\n                place_tree(place, make_visit(place[\"id\"]))\n                for place in plan[\"childPlaces\"]\n            ]\n        ),\n    )\n\n\n@component\ndef app():\n    return travel_plan()\n
\"\"\"The same tree as nested objects, the shape this section argues against.\"\"\"\n\nINITIAL_PLAN = {\n    \"id\": 0,\n    \"title\": \"(Root)\",\n    \"childPlaces\": [\n        {\n            \"id\": 1,\n            \"title\": \"Earth\",\n            \"childPlaces\": [\n                {\n                    \"id\": 2,\n                    \"title\": \"Africa\",\n                    \"childPlaces\": [\n                        {\"id\": 3, \"title\": \"Botswana\", \"childPlaces\": []},\n                        {\"id\": 4, \"title\": \"Egypt\", \"childPlaces\": []},\n                        {\"id\": 5, \"title\": \"Kenya\", \"childPlaces\": []},\n                    ],\n                },\n                {\n                    \"id\": 10,\n                    \"title\": \"Americas\",\n                    \"childPlaces\": [\n                        {\"id\": 11, \"title\": \"Argentina\", \"childPlaces\": []},\n                        {\"id\": 12, \"title\": \"Brazil\", \"childPlaces\": []},\n                        {\"id\": 13, \"title\": \"Barbados\", \"childPlaces\": []},\n                    ],\n                },\n                {\n                    \"id\": 21,\n                    \"title\": \"Asia\",\n                    \"childPlaces\": [\n                        {\"id\": 22, \"title\": \"Japan\", \"childPlaces\": []},\n                        {\"id\": 23, \"title\": \"Nepal\", \"childPlaces\": []},\n                    ],\n                },\n            ],\n        }\n    ],\n}\n
# TODO\n

Marking one country visited calls mark_visited, which copies the root, copies Earth, copies the continent, and copies every sibling along the way \u2014 a new object for every node between the root and the leaf. Correct, and also easy to get wrong: miss one {**node, ...} and you have mutated shared state, which in ReactPy means the update may compare equal and never render.

Flatten the tree into one dict keyed by id, with child lists of ids:

app.pydata.py Run
def bind_visit(on_visit, place_id):\n    # Handlers are called with the event alone, so the id is bound by a closure\n    # rather than a lambda default \u2014 a handler has to be a coroutine, and a\n    # coroutine cannot be a lambda. The default stays overridable so a parent's\n    # bound handler can be re-bound for a deeper child.\n    async def visit(event, target_id=place_id):\n        await on_visit(event, target_id)\n\n    return visit\n\n\n@component\ndef place_tree(place_id, on_visit, visited):\n    place = PLACES[place_id]\n    children = place[\"childIds\"]\n    return html.li(\n        {\"key\": place_id},\n        place[\"title\"],\n        html.button(\n            {\"onClick\": on_visit}, \"Visited\" if place_id in visited else \"Visit\"\n        ),\n        (\n            html.ol(\n                [\n                    place_tree(child_id, bind_visit(on_visit, child_id), visited)\n                    for child_id in children\n                ]\n            )\n            if children\n            else None\n        ),\n    )\n\n\n@component\ndef travel_plan():\n    # One dict keyed by id, plus one set of visited ids. Marking a place visited\n    # touches exactly one entry \u2014 there is no enclosing object to rebuild.\n    visited, set_visited = use_state(set())\n\n    async def toggle(event, place_id=0):\n        if place_id not in visited:\n            set_visited({*visited, place_id})\n\n    return html.div(\n        html.h2(\"Places to visit\"),\n        html.ol(\n            [\n                place_tree(place_id, bind_visit(toggle, place_id), visited)\n                for place_id in PLACES[0][\"childIds\"]\n            ]\n        ),\n    )\n\n\n@component\ndef app():\n    return travel_plan()\n
\"\"\"The same tree flattened into one dict keyed by id.\"\"\"\n\nPLACES = {\n    0: {\"id\": 0, \"title\": \"(Root)\", \"childIds\": [1]},\n    1: {\"id\": 1, \"title\": \"Earth\", \"childIds\": [2, 10, 21]},\n    2: {\"id\": 2, \"title\": \"Africa\", \"childIds\": [3, 4, 5]},\n    3: {\"id\": 3, \"title\": \"Botswana\", \"childIds\": []},\n    4: {\"id\": 4, \"title\": \"Egypt\", \"childIds\": []},\n    5: {\"id\": 5, \"title\": \"Kenya\", \"childIds\": []},\n    10: {\"id\": 10, \"title\": \"Americas\", \"childIds\": [11, 12, 13]},\n    11: {\"id\": 11, \"title\": \"Argentina\", \"childIds\": []},\n    12: {\"id\": 12, \"title\": \"Brazil\", \"childIds\": []},\n    13: {\"id\": 13, \"title\": \"Barbados\", \"childIds\": []},\n    21: {\"id\": 21, \"title\": \"Asia\", \"childIds\": [22, 23]},\n    22: {\"id\": 22, \"title\": \"Japan\", \"childIds\": []},\n    23: {\"id\": 23, \"title\": \"Nepal\", \"childIds\": []},\n}\n
# TODO\n

The same update now adds one entry to one set, and finding a place is a dict lookup rather than a recursion. The rendering component still walks the tree by id, so the display is unchanged \u2014 only the update path got shorter.

A useful test for whether nesting has gone too far: count the objects an update has to rebuild to change one field. If the answer is \"everything above it\", flatten. If the hierarchy is genuinely part of the data and updates are rare, nesting is fine \u2014 the cost is in writes, not reads.

Deep Dive

Flattening is what databases do, and ReactPy's == comparison gives you a concrete second reason \u2014 though it rewards less than you would expect. A state update is detected by handing the new value to == (core/hooks.py, strictly_equal, falls through to == for a dict). dict.__eq__ first compares lengths, which is free, then walks entries in insertion order and stops at the first difference. So the cost depends on where the changed entry sits, not on how big the object is.

Measured on dicts of 100,000 keys, comparing a copy against its original after changing one entry: about 0.00 ms when the changed key sorts first, about 1.3 ms when it sorts last, and about 3 ms when the two dicts hold identical content. Nested dicts recurse, so a rebuilt branch is compared down to its leaves while untouched sibling branches are still compared entry by entry too \u2014 sharing the sub-dict objects does not make the comparison skip them.

The honest conclusion: comparison cost is real but small and easy to over-engineer around, so flatten for the update-code reasons above rather than for this one. Where the shapes matter, measure instead of guessing \u2014 ReactPy ships no render profiler, so count renders with a use_ref counter in the component body, as the examples on this page do.

"},{"location":"learn/choosing-the-state-structure/#recap","title":"Recap","text":"
  • ReactPy's use_state compares with ==: setting a new object that reads equal to the old one renders nothing. Copy objects rather than mutating them \u2014 verified, mutating leaves the screen and the data disagreeing.
  • type(x) is not type(y) makes (1, 2) and [1, 2] different states, so pick one container type per field.
  • Group state that always changes together. It removes the \"forgot the second setter\" bug; it does not reduce renders (verified: same count both ways).
  • Replace mutually exclusive booleans with one variable holding one of several values, so the impossible state is unrepresentable.
  • Derive anything you can calculate during render. Both versions of the name form render the same number of times; only one of them can show a stale ticket.
  • Store ids, not copies. Edit the selected row and the derived label follows while the stored one does not.
  • Count how many objects an update must rebuild. If nesting forces you to rebuild the path to every leaf, flatten and key by id.
"},{"location":"learn/choosing-the-state-structure/#related","title":"Related","text":"
  • You Might Not Need an Effect \u2014 the same derive-don't-store rule applied to Effects
  • Lifecycle of Reactive Effects \u2014 strictly_equal in full, and why dependency arrays use ==
  • Updating Objects in State and Updating Arrays in State \u2014 the copy idioms this page assumes
  • Queueing a Series of State Updates \u2014 functional updaters, for when two handlers race
  • Extracting State Logic into a Reducer \u2014 the next step once one component holds too many fields
  • Use State \u2014 parameter-level reference
"},{"location":"learn/communicate-data-between-server-and-client/","title":"Communicating Data Between Server and Client","text":""},{"location":"learn/communicate-data-between-server-and-client/#overview","title":"Overview","text":"

ReactPy connects a Python process and a browser tab with one websocket. Everything crossing it is JSON, and nothing is pushed: the server sends rendered models, the client sends events, and a handler which wants to know something about the browser has to ask for it one property at a time. That constraint decides how every kind of data moves \u2014 values the server knows flow down as models, values the user produces flow up as events, and anything else needs a deliberate detour. This page is a map of those routes \u2014 what crosses, what cannot, and the honest workaround for each.

You will learn

  • Which channels actually exist, and the one direction that is not supported
  • How to push a server-side value into a view without the client asking
  • What an event actually gives your handler \u2014 including why file uploads do not work the way people expect
  • How to get real file bytes across, and how to keep blocking code off the loop
"},{"location":"learn/communicate-data-between-server-and-client/#the-channels-that-exist","title":"The channels that exist","text":"Direction Mechanism Your control server \u2192 client the rendered model, sent as layout-update patches total \u2014 return it from the component client \u2192 server an event handler, receiving layout-event total \u2014 read event server \u2192 client, on demand event-proxy-request / event-proxy-response, driven by await on an event indirect \u2014 you ask by awaiting, ReactPy frames the message server \u2192 client, arbitrary message none not supported shared mutable state none the two sides hold different objects

The third row is new, and it is narrower than it looks: the request row exists because a handler pulled, on the back of an event the client chose to send. A read carries no application data and starts no conversation \u2014 see Protocol Structure. Calling a method is the exception which proves the rule: await event.getCoalescedEvents() ships your arguments down as JSON (_event_proxy.py, _begin), which is data travelling the direction the table says does not exist. Reads are the default; a call is a door you have to choose to open.

The server-to-client push row is the one people look for first, and its absence is a design choice, not an omission. A component's Connection has no send, no deliver, no close (User Connection), and the backend is the only code allowed to write to the socket. That keeps the model the single source of truth: if a component could scribble on the wire, the client's tree and the server's model would drift apart with nothing to reconcile them.

A hand-written message is dropped by the client, silently

A custom backend can write anything to the socket, so it is tempting to invent a message type. Verified against a real browser: a {\"type\": \"my-custom\", ...} frame arrives, and the client logs

[ReactPy] Received message without handler Object\n

and discards it. The client dispatches on message.type against a fixed registry \u2014 \"layout-update\" is the only one registered by the mount path \u2014 and an unregistered type warns and returns. Nothing throws, the socket stays up, and the UI never changes. The page's only entry point is mountReactPy, and nothing in that path lets you register a handler of your own: onMessage exists on the client classes, but a page built by mountReactPy never hands you the instance (The client object). Do not build on this path; use one of the routes below.

"},{"location":"learn/communicate-data-between-server-and-client/#down-push-a-value-the-client-never-asked-for","title":"Down: push a value the client never asked for","text":"

Anything you can await on the server becomes a render, and a render reaches the client. An use_async_effect that loops is the whole pattern:

app.py Run
async def fetch_price(symbol):\n    \"\"\"Stand-in for a request to an exchange, a sensor, a queue.\"\"\"\n    await asyncio.sleep(1)\n    return f\"{symbol}: 42.10\"\n\n\n@component\ndef ticker(symbol):\n    price, set_price = use_state(\"loading...\")\n\n    async def poll():\n        while True:\n            try:\n                set_price(await fetch_price(symbol))\n            except Exception as error:\n                set_price(f\"error: {error}\")\n            try:\n                await asyncio.sleep(1)\n            except asyncio.CancelledError:\n                break\n\n    use_async_effect(poll)\n\n    return html.div(html.h1(price))\n
# TODO\n

Verified end to end: the first patch carried [\"loading...\"], and about a second later a second patch arrived with [\"BTC: 42.10\"] \u2014 no client event in between. The client applied it like any other update.

Three things make or break this:

  • Use use_async_effect, not use_effect. An async def passed to use_effect raises TypeError: \\use_effect` does not support async functions. Use `use_async_effect` instead.The effect takes **zero arguments** \u2014 the#!python async def effect(stop)shape from 1.x no longer works. Note where that error surfaces:#!python use_effectraises during the **render**, not inside the effect task, so you get a blank region \u2014 the model is#!python {\"tagName\": \"\", \"error\": \"\"}with debug off, and carries theTypeErrortext with#!python REACTPY_DEBUGon \u2014 and noError in effect` log line at all. That distinguishes it from a raising effect body, which is silent everywhere until unmount (Lifecycle of Reactive Effects).
  • Loop and honour cancellation. A long-lived effect is while True: with except asyncio.CancelledError: break. ReactPy cancels the task on unmount; shield=True opts out if the work must finish.
  • The dependency array is not React's. Omitted means inferred from the closure's free variables, dependencies=[] starts once, dependencies=None restarts every render. Never omit the array expecting \"run after every render\". See Use Async Effect.

Polling is fine for a demo and wrong at scale. For real work, prefer a server-sent queue that the effect awaits \u2014 the effect suspends on await queue.get() and the render happens the moment an item lands, with no timer at all.

"},{"location":"learn/communicate-data-between-server-and-client/#up-read-what-the-client-sends","title":"Up: read what the client sends","text":"

An event handler receives a live proxy onto the browser's event, not a copy of it. The client keeps the object and hands the server a handle; each await on a property is a question asked of the page at that moment (event-proxy.ts, serializeValue). Nothing about the DOM event arrives unless you ask for it, which is the whole point: an <input> used to arrive as the entire HTMLInputElement \u2014 a measured onChange payload carried roughly ninety keys, accept, formAction, validity, and the rest \u2014 of which a handler reads value.

What a read gives back is decided by the client. Plain data comes across as itself. A method comes back as a reference, so await event.target.focus() is a call the client makes rather than a function you receive. An object too deep, too large, or recursive comes back as a reference too, and you reach further with more awaits. Blocked members \u2014 window, document, parentNode, eval, setTimeout \u2014 are refused outright, so a read cannot be used to walk out of the event and into the page.

On submit, the target is the form, and the thing you want is one level down. HTMLFormControlsCollection \u2014 what form.elements holds \u2014 is iterable, and the client hands over anything iterable as a list (serializeValue), so await event.target.elements answers with one entry per control, named or not, each a plain dict of a hundred properties. The form itself is iterable too, so await event.target gives you the same controls; .elements is just the name which says what you mean:

async def handle_submit(event):\n    event.preventDefault()\n    controls = await event.target.elements  # list of control dicts\n    payload = next(\n        (c[\"value\"] for c in controls if c.get(\"name\") == \"payload\"), \"\"\n    )\n

Note what makes that readable: the list arrives as data, so c[\"value\"] is a plain subscript on the answer. The proxy itself is not subscriptable, but the JSON the client sent back is an ordinary list of dicts. The nested back-references inside each control (c[\"form\"], which points at the form again) come through as references rather than copies. The full working example is in Files below, and the wire format is in Protocol Structure.

Because each read is a round trip, how you write them costs time. Awaiting two properties one after another is two trips; await event.read(\"target.value\", \"key\") or an asyncio.gather of them is one. A read you never make costs nothing at all.

"},{"location":"learn/communicate-data-between-server-and-client/#files-what-actually-crosses-the-wire","title":"Files: what actually crosses the wire","text":"

A file input does not send file contents. It sends file metadata. Re-verified in Chrome against a real <input type=\"file\"> \u2014 this is the entire files entry that arrived for a 12-byte upload.txt:

[\n    {\n        \"name\": \"upload.txt\",\n        \"lastModified\": 1792302335371,\n        \"lastModifiedDate\": \"2026-10-18T05:45:35.371Z\",\n        \"webkitRelativePath\": \"\",\n        \"size\": 12,\n        \"type\": \"text/plain\",\n    }\n]\n

No bytes. File exposes its content through methods, and the client hands methods over as references rather than values, so there is nothing to read. There is no FormData in the client and no chunked upload path. If you expected (await event.target.files)[0][\"content\"], it does not exist and never will from this mechanism.

"},{"location":"learn/communicate-data-between-server-and-client/#reading-real-bytes","title":"Reading real bytes","text":"

The route is: read the file in the browser with JavaScript, put the text into a normal form field, and let an ordinary submit carry it. This works \u2014 verified in Chrome, with a 12-character file arriving server-side as 'hello world\\n':

app.py Run
READ_FILE = (\n    \"const file = event.target.files[0];\"\n    \"if (file) {\"\n    \"    file.text().then((text) => {\"\n    \"        document.getElementById('payload').value = text;\"\n    \"    });\"\n    \"}\"\n)\n\n\n@component\ndef text_upload():\n    received, set_received = use_state(\"Choose a file, then send.\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        # Ask for the collection which exists for this purpose: an\n        # HTMLFormControlsCollection is iterable, so the client hands it\n        # over as a list - one dict per control, named or not.\n        controls = await event.target.elements\n        payload = next(\n            (c[\"value\"] for c in controls if c.get(\"name\") == \"payload\"),\n            \"\",\n        )\n        set_received(f\"{len(payload)} characters received\")\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.input({\"type\": \"file\", \"onChange\": READ_FILE}),\n        html.input({\"type\": \"hidden\", \"id\": \"payload\", \"name\": \"payload\", \"value\": \"\"}),\n        html.button({\"type\": \"submit\"}, \"Send\"),\n        html.pre(received),\n    )\n
# TODO\n

The mechanics, each of which matters:

  • The onChange value is a string, which routes it to inlineJavaScript instead of eventHandlers \u2014 that is what makes it run in the page (Creating VDOM Event Handlers).
  • file.text() returns a promise, so the assignment happens in a callback \u2014 which is why a submit button is required. Sending immediately from the change handler races the promise and ships an empty string.
  • The hidden input needs both id (for the JavaScript to find it) and name (for the form walk to include it).
  • event.preventDefault() must appear literally in the handler's body. ReactPy detects it by bytecode inspection; calling a helper that calls it is not detected, and the page navigates away. See the bound-method caveat in Creating VDOM Event Handlers.

Untrusted content and eval

The inline-JavaScript string is eval'd. Build it from constants only \u2014 f\"...{user_input}...\" is an injection point. And anything you read off a user's file is untrusted data: cap the size before you buffer it, and never render it back without escaping.

Binary uploads work the same way via file.arrayBuffer() plus base64, but base64 costs a third of your bandwidth on top of a JSON string over a socket. For anything large, upload to a normal HTTP endpoint and pass the resulting id through state instead.

"},{"location":"learn/communicate-data-between-server-and-client/#blocking-code-and-other-threads","title":"Blocking code and other threads","text":"

Everything a component does runs on the connection's event loop. A synchronous, CPU-bound call inside a render or an effect freezes that connection \u2014 and the handlers on it. Move blocking work with asyncio.to_thread:

app.py Run
def blocking_render(width):\n    \"\"\"A synchronous, CPU-bound library call. Never run this on the loop.\"\"\"\n    time.sleep(0.4)  # stands in for image encoding, model inference, ...\n    return f\"rendered {width}px\"\n\n\n@component\ndef preview(width):\n    result, set_result = use_state(\"rendering...\")\n\n    async def render():\n        # to_thread keeps the loop free; the setter runs back on the loop.\n        set_result(await asyncio.to_thread(blocking_render, width))\n\n    use_async_effect(render, dependencies=[width])\n\n    return html.h1(result)\n
# TODO\n

Verified: the first patch says rendering..., the next says rendered 300px, and the loop stayed responsive across the 0.4 s call. Note that the setter is called from inside the effect, which is already on the loop.

Calling a state setter from another thread loses the update

Handing set_value to a raw threading.Thread is the natural mistake, and it fails with no user-visible symptom. Verified \u2014 the setter raises on the worker thread, and no render ever arrives:

Failed to schedule render via <function _make_life_cycle_state.<locals>.<lambda> ...>\nRuntimeError: no running event loop\nRuntimeWarning: coroutine 'Layout._create_layout_update' was never awaited\n

Nothing appears in the browser console; the component simply never updates. The fix is to capture the loop while you are on it and marshal back:

loop = asyncio.get_running_loop()  # inside the component, on the loop\n\ndef deliver_from_thread(value):\n    loop.call_soon_threadsafe(set_value, value)\n

Verified working \u2014 the update arrived as a normal patch. Prefer asyncio.to_thread when you can, since it removes the bookkeeping entirely.

Do not hang state on the component function

my_component.started = True as a run-once guard looks harmless and is a cross-user leak: the function object is module-scope, so the first visitor flips it for everyone. Give the component a prop, key the child, or guard with use_ref. The general version of this is in VDOM Mutations.

"},{"location":"learn/communicate-data-between-server-and-client/#client-only-data","title":"Client-only data","text":"

The mirror problem: something in the browser that Python cannot see. The proxy narrowed this considerably \u2014 anything reachable from the event object is now readable, so await event.target.scrollTop, await event.target.className, or a value a canvas put on its own element are ordinary awaits. What is still out of reach is anything the page will not hand over from that starting point: computed style of an element you never got an event from, localStorage, geolocation, the clipboard. The client refuses the paths which would lead there anyway (window, document, localStorage), so a handler cannot walk off the event and into the page \u2014 see Protocol Structure.

Your options, in order of preference:

  1. Move the computation server-side. If the value depends only on data you already have, compute it in Python. Server-side rendering is the default for a reason.
  2. Copy it into a form field and submit, exactly as the file example does. Clunky, and it works.
  3. Use the browser API in inline JavaScript and let it act locally \u2014 copy to clipboard, request fullscreen, scroll. Keep it to one call, and never interpolate user input into the string.
  4. Render the data yourself. A chart is usually better as a server-built <svg> or a generated image (reactpy.widgets.image) than as JavaScript in the page.

There is no use_ref-style handle to a DOM node, and {\"ref\": ...} either vanishes or throws in the browser \u2014 see Manipulating the DOM with Refs.

"},{"location":"learn/communicate-data-between-server-and-client/#summary","title":"Summary","text":"
  • Only two channels exist: the model down, events up. An arbitrary server\u2192client message is logged as Received message without handler and dropped.
  • Server push means use_async_effect plus a state setter \u2014 loop, handle CancelledError, and remember the dependency array infers by default.
  • Events are live proxies, not snapshots: each await is a round trip to the browser, a read you never make costs nothing, and submit hands back the form as a list of controls.
  • File inputs send metadata only, never bytes. Read the file in the browser, put the text in a named hidden input, and submit.
  • Use asyncio.to_thread for blocking work. Calling a setter from a raw thread fails with no running event loop and no visible symptom \u2014 marshal with call_soon_threadsafe.
  • State never lives on the component function object; that is a leak across connections.
"},{"location":"learn/communicate-data-between-server-and-client/#related","title":"Related","text":"
  • Protocol Structure \u2014 the message types, target, and seq
  • Use Async Effect and Use Effect \u2014 the push mechanism, and its traps
  • Creating VDOM Event Handlers \u2014 payload shape, inline JavaScript, bytecode detection
  • User Connection \u2014 the read-only connection view
  • Manipulating the DOM with Refs \u2014 client-only capabilities
  • Creating Backends \u2014 where the socket actually lives
"},{"location":"learn/conditional-rendering/","title":"Conditional Rendering","text":""},{"location":"learn/conditional-rendering/#overview","title":"Overview","text":"

Your components will often need to display different things depending on different conditions. In ReactPy, you can conditionally render markup using Python syntax like if statements and conditional expressions.

You will learn

  • How to return different markup depending on a condition
  • How to conditionally include or exclude a piece of markup
  • Conditional syntax shortcuts you'll encounter in ReactPy codebases
"},{"location":"learn/conditional-rendering/#conditionally-returning-markup","title":"Conditionally returning markup","text":"

Let's say you have a packing_list() component rendering several item()s, which can be marked as packed or not. Notice that some of the item() calls have their is_packed prop set to True instead of False. You want to add a checkmark (\u2714) to packed items if is_packed is True.

You can write this as an if/else statement like so:

if is_packed:\n    return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\nreturn html.li({\"className\": \"item\"}, name)\n

If the is_packed prop is True, this code returns a different VDOM tree. With this change, some of the items get a checkmark at the end:

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    if is_packed:\n        return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\n    return html.li({\"className\": \"item\"}, name)\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=True, name=\"Space suit\"),\n            item(is_packed=True, name=\"Helmet with a golden leaf\"),\n            item(is_packed=False, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

Try editing what gets returned in either case, and see how the result changes!

Notice how you're creating branching logic with Python's if and return statements. In ReactPy, control flow (like conditions) is handled by Python.

"},{"location":"learn/conditional-rendering/#conditionally-returning-nothing-with-none","title":"Conditionally returning nothing with None","text":"

In some situations, you won't want to render anything at all. For example, say you don't want to show packed items at all. A component must return something \u2014 in ReactPy, that something is None:

if is_packed:\n    return None\nreturn html.li({\"className\": \"item\"}, name)\n

If is_packed is true, the component will return nothing, None. Otherwise, it will return markup to render.

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    if is_packed:\n        return None\n    return html.li({\"className\": \"item\"}, name)\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=False, name=\"Space suit\"),\n            item(is_packed=False, name=\"Helmet with a golden leaf\"),\n            item(is_packed=True, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

In practice, returning None from a component isn't common because it might surprise a developer trying to render it. More often, you would conditionally include or exclude the component in the parent component. Here's how to do that!

"},{"location":"learn/conditional-rendering/#conditionally-including-markup","title":"Conditionally including markup","text":"

In the previous example, you controlled which (if any!) VDOM tree would be returned by the component. You may already have noticed some duplication in the render output:

html.li({\"className\": \"item\"}, f\"{name} \u2714\")\n

is very similar to

html.li({\"className\": \"item\"}, name)\n

Both of the conditional branches return html.li({\"className\": \"item\"}, ...):

if is_packed:\n    return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\nreturn html.li({\"className\": \"item\"}, name)\n

While this duplication isn't harmful, it could make your code harder to maintain. What if you want to change the className? You'd have to do it in two places in your code! In such a situation, you could conditionally include a little markup to make your code more DRY.

"},{"location":"learn/conditional-rendering/#conditional-ternary-expression","title":"Conditional (ternary) expression","text":"

Python has a compact syntax for writing a conditional expression \u2014 the conditional expression or \"ternary operator\".

Instead of this:

if is_packed:\n    return html.li({\"className\": \"item\"}, f\"{name} \u2714\")\nreturn html.li({\"className\": \"item\"}, name)\n

You can write this:

return html.li({\"className\": \"item\"}, f\"{name} \u2714\" if is_packed else name)\n

You can read it as \"if is_packed is true, then render f\"{name} \u2714\", otherwise render name\".

Now let's say you want to wrap the completed item's text into another HTML tag, like <del> to strike it out. You can add even more newlines and parentheses so that it's easier to nest more markup in each of the cases:

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    return html.li(\n        {\"className\": \"item\"},\n        html.del_(f\"{name} \u2714\") if is_packed else name,\n    )\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=True, name=\"Space suit\"),\n            item(is_packed=True, name=\"Helmet with a golden leaf\"),\n            item(is_packed=False, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

This style works well for simple conditions, but use it in moderation. If your components get messy with too much nested conditional markup, consider extracting child components to clean things up. In ReactPy, markup is a part of your code, so you can use tools like variables and functions to tidy up complex expressions.

Are these two examples fully equivalent?

If you're coming from an object-oriented programming background, you might assume that the two examples above are subtly different because one of them may create two different \"instances\" of html.li. But VDOM dictionaries aren't \"instances\" because they don't hold any internal state and aren't real DOM nodes. They're lightweight descriptions, like blueprints. So these two examples, in fact, are completely equivalent. Preserving and Resetting State goes into detail about how this works.

"},{"location":"learn/conditional-rendering/#excluding-a-child-with-a-conditional","title":"Excluding a child with a conditional","text":"

In JavaScript React you'll often see the logical AND operator used to render a child only when a condition holds:

<li className=\"item\">{name} {isPacked && \"\u2714\"}</li>\n

There is no equivalent of this idiom in ReactPy. Python's and returns one of its operands rather than a renderable-agnostic False, and ReactPy renders False as the literal text \"False\" instead of skipping it:

# WRONG: renders \"Photo of Tam False\" when is_packed is False\nreturn html.li({\"className\": \"item\"}, name, is_packed and \"\u2714\")\n

Reach for a conditional expression that yields an empty list, and unpack it:

return html.li(\n    {\"className\": \"item\"},\n    name,\n    *([\"\u2714\"] if is_packed else []),\n)\n

Or build the children list up front, which reads better as the number of optional children grows:

@component\ndef item(name, is_packed):\n    content = [name]\n    if is_packed:\n        content.append(\"\u2714\")\n    return html.li({\"className\": \"item\"}, *content)\n

Don't put a number on the left side of a conditional expression

The classic messageCount && <p>New messages</p> bug has an exact Python twin. and returns the left operand when it is falsy, and ReactPy renders numbers as text \u2014 so count and html.p(\"New messages\") renders 0 rather than nothing when count is 0. Make the left side a genuine boolean test:

html.p(\"New messages\") if count > 0 else None\n
"},{"location":"learn/conditional-rendering/#conditionally-assigning-markup-to-a-variable","title":"Conditionally assigning markup to a variable","text":"

When the shortcuts get in the way of writing plain code, try using an if statement and a variable. Start by providing the default content you want to display, the name:

item_content = name\n

Use an if statement to reassign a VDOM expression to item_content if is_packed is True:

if is_packed:\n    item_content = html.del_(f\"{name} \u2714\")\n

Embed the variable in the returned VDOM tree, nesting the previously calculated expression inside the parent element:

html.li({\"className\": \"item\"}, item_content)\n

This style is the most verbose, but it's also the most flexible:

app.pystyles.css Run
@component\ndef item(name, is_packed):\n    item_content = name\n    if is_packed:\n        item_content = html.del_(f\"{name} \u2714\")\n    return html.li({\"className\": \"item\"}, item_content)\n\n\n@component\ndef packing_list():\n    return html.section(\n        html.h1(\"Sally Ride's Packing List\"),\n        html.ul(\n            item(is_packed=True, name=\"Space suit\"),\n            item(is_packed=True, name=\"Helmet with a golden leaf\"),\n            item(is_packed=False, name=\"Photo of Tam\"),\n        ),\n    )\n
.item {\n    list-style: none;\n}\n
# TODO\n

Like before, this works not only for text, but for arbitrary markup too.

Tags that collide with Python keywords

del is a Python keyword, so the <del> tag is spelled with a trailing underscore: html.del_. The underscore is stripped before the tag name is emitted, so it still renders <del>. The same rule covers html.import_, html.object, and friends \u2014 and dashes become underscores, so <data-table> is html.data_table.

If you're not familiar with Python, this variety of styles might seem overwhelming at first. However, learning them will help you read and write any Python code \u2014 and not just ReactPy components! Pick the one you prefer for a start, and then consult this reference again if you forget how the other ones work.

"},{"location":"learn/conditional-rendering/#recap","title":"Recap","text":"
  • To conditionally return different markup from a component, use if statements and return.
  • To render nothing, return None from the component.
  • To tidy up duplicated markup, use a conditional expression: x if condition else y.
  • Don't use and to include a child \u2014 ReactPy renders False as the text \"False\". Unpack an empty list instead.
  • For anything more complex, assign children to variables with if statements.
"},{"location":"learn/convert-between-vdom-and-html/","title":"Convert Between VDOM and HTML","text":""},{"location":"learn/convert-between-vdom-and-html/#overview","title":"Overview","text":"

ReactPy's UI is a plain dictionary. Two functions move it across the boundary to and from ordinary HTML text: reactpy_to_string() renders a VDOM dictionary or a component to an HTML string, and string_to_reactpy() parses an HTML string into a VDOM dictionary. Both live at the top of the package, and both are load-bearing inside ReactPy itself \u2014 the executors build the initial document and the PyScript head with them.

You will learn

  • How to turn VDOM into an HTML string, and what that conversion throws away
  • How to parse HTML text into a VDOM dictionary, and the renames it performs on the way in
  • Why className survives parsing but not serialization
  • When to reach for string_to_reactpy() instead of writing html.* calls
"},{"location":"learn/convert-between-vdom-and-html/#the-two-functions","title":"The two functions","text":"Function In Out Raises on bad input reactpy_to_string(root) VdomDict or a called Component str of HTML TypeError on a callable attribute string_to_reactpy(html, *transforms, strict=True, intercept_links=True) str of HTML VdomDict HTMLParseError, ValueError, TypeError

Both are exported from the package root:

from reactpy import reactpy_to_string, string_to_reactpy\n

Neither is a serializer for the wire. The socket protocol has its own shape, described in Protocol Structure; these two functions exist so you can hand ReactPy's output to something that speaks HTML, and hand HTML from somewhere else to ReactPy.

"},{"location":"learn/convert-between-vdom-and-html/#vdom-to-html-reactpy_to_string","title":"VDOM to HTML: reactpy_to_string()","text":"

Give it an element, or a component that has already been called:

app.py Run
todo_list = html.div(\n    html.h1(\"My Todo List\"),\n    html.ul(\n        html.li(\"Build a cool new app\"),\n        html.li(\"Share it with the world!\"),\n    ),\n)\n\nprint(reactpy_to_string(todo_list))\n# <div><h1>My Todo List</h1><ul><li>Build a cool new app</li><li>Share it with the world!</li></ul></div>\n
# TODO\n

There is no formatting option, no indent, no doctype. You get the tags and the attributes and nothing else.

"},{"location":"learn/convert-between-vdom-and-html/#what-it-renders","title":"What it renders","text":"

reactpy_to_string() walks tagName, attributes, and children (utils.py, _add_vdom_to_etree). Everything else in the dictionary is ignored:

  • Event handlers disappear. A {\"onClick\": handler} produces no attribute at all. The value is a Python function; there is nothing sensible to write into HTML text.
  • Inline JavaScript disappears too. inlineJavaScript is not consulted, so reactpy_to_string(html.button({\"onClick\": \"this.innerText = 1\"}, \"x\")) returns <button>x</button> with the script gone.
  • An importSource disappears. A web-module component renders as its bare tag name, which no browser knows what to do with.
  • A component child is rendered. _add_vdom_to_etree calls component_to_vdom() on any child with a .render attribute, and component_to_vdom() recurses, so nested components come out as HTML \u2014 verified two levels down, html.section(mid()) becomes <section><div><p>deep</p></div></section>. The limit is hooks, not depth.

That list is why this function cannot render your average app. It has no Layout, so there is no hook stack: use_state, use_ref, use_effect, use_location all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? Only hook-free components and plain html.* elements convert. This is the same path ReactPy(prepend_body=...) uses, which is why that argument takes an element and not a stateful component.

Passing an uncalled component raises AttributeError

reactpy_to_string(my_component) fails with AttributeError: 'function' object has no attribute 'render' \u2014 the decorator has not run, so there is nothing to render. Call it: reactpy_to_string(my_component()).

"},{"location":"learn/convert-between-vdom-and-html/#attribute-names-are-lowercased","title":"Attribute names are lowercased","text":"

This is the sharpest edge on the page, and it is silent. On the way out, every attribute name goes through .lower() (utils.py, _react_attribute_to_html), with the exception of data-* and aria-*, which are left exactly as you wrote them:

app.py Run
# HTML in, ReactPy out: camelCase comes back.\nprint(string_to_reactpy('<label for=\"email\">Email</label>'))\n# {'attributes': {'htmlFor': 'email'}, 'children': ['Email'], 'tagName': 'label'}\n\n# ReactPy out: every attribute name is lowercased on the way out.\nprint(reactpy_to_string(html.label({\"htmlFor\": \"email\"}, \"Email\")))\n# <label htmlfor=\"email\">Email</label>\n\nprint(reactpy_to_string(html.label({\"for\": \"email\"}, \"Email\")))\n# <label for=\"email\">Email</label>\n\nprint(reactpy_to_string(html.div({\"className\": \"card\"}, \"x\")))\n# <div classname=\"card\">x</div>\n
# TODO\n

htmlFor becomes htmlfor and className becomes classname \u2014 neither of which is a real HTML attribute, so the browser ignores both. The label stops labelling and the class stops styling, with no error anywhere.

Write the HTML name when the string is the destination. {\"for\": \"email\"} and {\"class\": \"card\"} produce correct markup; {\"htmlFor\": ...} and {\"className\": ...} do not. There is no single spelling that satisfies both this and live rendering \u2014 see Common Props for why className is what the browser-side path wants.

The one attribute ReactPy does translate is style, which it flattens from a dict into a key:value; string and kebab-cases on the way:

reactpy_to_string(html.div({\"style\": {\"fontSize\": \"20px\", \"zIndex\": 3}}, \"s\"))\n# <div style=\"font-size:20px;z-index:3\">s</div>\n

Note what did not happen to the 3: no unit is inferred. z-index:3 is fine because it takes no unit, but font-size:30 would be invalid CSS where the browser-side renderer would have applied it as 30px. Always put the unit in the string.

Some boolean attributes obey lxml, not HTML

ReactPy renders the string through lxml, which carries its own list of known boolean attributes. For a name on that list the value is discarded and only presence matters \u2014 so False still emits the attribute:

app.py Run
# lxml knows a fixed list of boolean attributes. For those, the value is\n# dropped and presence is all that survives -- False included.\nprint(reactpy_to_string(html.input({\"type\": \"checkbox\", \"checked\": False})))\n# <input type=\"checkbox\" checked>\n\n# For everything else the value is str()-ed, so False leaks into the page.\nprint(reactpy_to_string(html.div({\"hidden\": False}, \"still hidden\")))\n# <div hidden=\"False\">still hidden</div>\n
# TODO\n

checked renders as <input type=\"checkbox\" checked> even though you said False. Names outside that list get str(value) instead, which leaks the word False into your markup as in <div hidden=\"False\">. Which names fall in which group depends on your lxml build, so test the attribute you care about. The safe form for a server-rendered string is to omit the key entirely rather than set it to False.

"},{"location":"learn/convert-between-vdom-and-html/#html-to-vdom-string_to_reactpy","title":"HTML to VDOM: string_to_reactpy()","text":"app.py Run
model = string_to_reactpy('<div class=\"card\"><h1>My Todo List</h1></div>')\n\nprint(model[\"tagName\"])  # div\nprint(model[\"attributes\"])  # {'className': 'card'}\nprint(model[\"children\"])  # [{'tagName': 'h1', 'children': ['My Todo List']}]\n\n# Nested children are full models too, which is easiest to see with pprint:\n# pprint(model, sort_dicts=False)\n# {'children': [{'children': ['My Todo List'], 'tagName': 'h1'}],\n#  'attributes': {'className': 'card'},\n#  'tagName': 'div'}\n
# TODO\n

Parsing is done by lxml.html, then each node is rebuilt through the normal html.* constructors (utils.py, _etree_to_vdom), which means what comes back is a genuine VdomDict you can render, patch, and send over the socket.

"},{"location":"learn/convert-between-vdom-and-html/#what-parsing-renames-and-rewrites","title":"What parsing renames and rewrites","text":"

The parser does not give you a mirror of the input. It runs a table of renames and a handful of node rewrites (transforms.py, attributes_to_reactjs and RequiredTransforms):

In the HTML In the VDOM Why class=\"card\" {\"className\": \"card\"} React-style prop name for=\"email\" {\"htmlFor\": \"email\"} React-style prop name checked {\"defaultChecked\": ...} Controlled-vs-uncontrolled accept-charset {\"acceptCharset\": ...} React-style prop name http-equiv {\"httpEquiv\": ...} React-style prop name style=\"font-size:20px\" {\"style\": {\"fontSize\": \"20px\"}} Style strings become dicts <input value=\"ab\"> {\"defaultValue\": \"ab\"} A value prop makes an input controlled <textarea>hi</textarea> {\"defaultValue\": \"hi\"}, no children Same reason <option selected> defaultValue on the parent <select> React puts the selection on the parent <a href=\"/x\"> an onClick handler with prevent_default Keeps a client router in charge <div id=\"uniq\"> {\"id\": \"uniq\", \"key\": \"uniq\"} A key is inferred from id, then name

The last two deserve a second look, because they change behaviour rather than spelling.

Every anchor gets a click handler. By default intercept_links=True installs a preventDefault-only onClick on every <a> (transforms.py, intercept_link_clicks). The link looks dead: clicking it does nothing, because ReactPy has no router to hand the navigation to. Pass intercept_links=False to keep real links.

An id becomes a key. infer_key_from_attributes copies id \u2014 or name on <input>, <select>, <textarea> \u2014 into attributes[\"key\"]. That is usually what you want, since a stable id is a stable identity, but it also means two elements parsed with the same id collide: the layout rejects them with ValueError: Duplicate keys [...].

Whitespace you did not ask for

lxml is configured to drop comments, processing instructions, and blank text \u2014 but remove_blank_text only removes whitespace that is purely formatting. The newline and indentation between tags in a pretty-printed document survive as text children:

string_to_reactpy(\"<ul>\\n  <li>a</li>\\n  <li>b</li>\\n</ul>\")[\"children\"]\n# [{'tagName': 'li', 'children': ['a']}, '\\n  ', {'tagName': 'li', 'children': ['b']}, '\\n']\n

Those strings render as visible whitespace. If you parse an indented template literal, expect stray spaces and newlines in the layout \u2014 write the markup flat, or strip the text children in a transform.

"},{"location":"learn/convert-between-vdom-and-html/#errors-and-when-to-turn-strictness-off","title":"Errors, and when to turn strictness off","text":"

string_to_reactpy() raises in four distinct ways:

string_to_reactpy(12)          # TypeError: Expected html to be a string, not int\nstring_to_reactpy(\"no tags here\")  # ValueError: Expected html string to contain HTML tags, but no tags were found.\nstring_to_reactpy(\"<div <p>x</p>\")  # HTMLParseError (a subclass of lxml.LxmlSyntaxError)\nstring_to_reactpy(\"<div <p>x</p>\", strict=False)  # parses; '<p' becomes an attribute name\n

strict=True reaches lxml as recover=False, so a document lxml refuses to parse cleanly becomes an HTMLParseError whose message tells you to turn strictness off. Do that deliberately: recovery invents attribute names out of the malformed text rather than complaining, so you trade a loud failure for a quietly wrong model.

A few things you might expect strict to catch, and it does not:

  • Unclosed or mismatched tags \u2014 <div><span>x</div> parses fine and the span is closed for you.
  • Orphan fragments \u2014 <td>orphan</td> and <li>x</li> parse into bare td/li models, which then render somewhere HTML forbids them.
  • Unknown entities \u2014 &notreal; becomes the literal text \u00acreal;.
"},{"location":"learn/convert-between-vdom-and-html/#wrapping-empty-input-and-multiple-roots","title":"Wrapping, empty input, and multiple roots","text":"

lxml wants a single well-formed root, so it adds one for you:

  • <p>a</p><p>b</p> comes back wrapped in a div.
  • <body>hi</body> comes back as a span, not a body \u2014 lxml.html's document scaffolding leaks through.
  • <meta http-equiv=\"refresh\"> is likewise pulled into an html/head pair.
  • Empty or whitespace-only input returns {\"tagName\": \"\"} \u2014 a fragment, not an error.

Do not write code that indexes children by position on parsed input; the wrapper is lxml's decision, not yours.

"},{"location":"learn/convert-between-vdom-and-html/#transforming-every-node-the-transforms-argument","title":"Transforming every node: the *transforms argument","text":"

string_to_reactpy() takes any number of one-argument callables and applies each to every node after parsing (utils.py, _etree_to_vdom). This is the supported way to post-process the model rather than editing it after the fact:

def add_card_class(vdom):\n    if vdom[\"tagName\"] == \"div\":\n        vdom.setdefault(\"attributes\", {})[\"className\"] = \"card\"\n    return vdom\n\n\nstring_to_reactpy(\"<div>x</div>\", add_card_class)\n# {'tagName': 'div', 'attributes': {'className': 'card'}, 'children': ['x']}\n

Mutate and return the same dictionary; the return value replaces the node, assigned unconditionally (utils.py, _etree_to_vdom). A transform that returns None deletes the node \u2014 for a child it leaves None in children, for the root it makes string_to_reactpy() itself return None. Always return the model.

Transforms run after the built-in ones, so a style value is already a dict by the time yours sees it, and an <a> already has its onClick. Use this hook to strip attributes you do not trust from third-party HTML \u2014 but note that removing an attribute is not the same as making untrusted HTML safe:

Never parse untrusted HTML without filtering

string_to_reactpy() does not sanitize. An <img src=x onerror=\"alert(1)\"> comes back with the payload in inlineJavaScript, not in attributes \u2014 verified: {'attributes': {'src': 'x'}, 'inlineJavaScript': {'onError': 'alert(1)'}, 'tagName': 'img'} \u2014 and that is the more dangerous of the two outcomes: the client turns each inlineJavaScript entry into an event-handler prop whose body is eval(...) (static/index.js, the inline-JavaScript wrapper), so the code executes when the event fires. An <img src=x> fails to load, onError fires, and the payload runs. An <script> comes back as a script element with its content intact. ReactPy's own html.script refuses event handlers and injects an inferred key, but it does not inspect the JavaScript inside, and neither guard applies to what string_to_reactpy() produces. If the string came from a user, run it through a sanitizer first and pass intercept_links=False only once you have decided links are wanted.

"},{"location":"learn/convert-between-vdom-and-html/#using-the-result-in-a-component","title":"Using the result in a component","text":"

A parsed model is an ordinary element, so a component can return it directly:

app.py Run
@component\ndef card():\n    return string_to_reactpy('<div class=\"card\"><b>rendered from a string</b></div>')\n\n\nprint(reactpy_to_string(card()))\n# <div class=\"card\"><b>rendered from a string</b></div>\n
# TODO\n

That is exactly how ReactPy builds its own document head: ReactPy(pyscript_setup=True) parses a block of PyScript markup with string_to_reactpy() and splices it into html_head (executors/asgi/standalone.py).

Do not parse inside the render body

The parse is a full lxml document build, and it runs on the server on every render. Parse once at module level and return the stored model, or memoize it with use_memo. Re-parsing per render is pure waste, and if you put the model in state you also hand yourself the mutation hazard covered in VDOM Mutations.

"},{"location":"learn/convert-between-vdom-and-html/#choosing-a-direction","title":"Choosing a direction","text":"

Reach for these when the HTML exists somewhere other than in your Python code:

  • string_to_reactpy() when markup arrives from a CMS, a markdown renderer, an email body, or a template you do not control. It is also the shortest path to pasting a snippet of HTML from a design mock into a component.
  • reactpy_to_string() when you must hand ReactPy's output to something that is not the ReactPy client: server-rendered <head> content, an <noscript> fallback, an email or PDF generator, a test assertion.

If the markup is yours to write, write html.* calls. The constructors validate at the call site, keep their arguments JSON-serializable, and support handlers \u2014 none of which the string path gives you.

"},{"location":"learn/convert-between-vdom-and-html/#summary","title":"Summary","text":"
  • reactpy_to_string() takes a VdomDict or a called component and returns HTML text; it drops event handlers, inline JavaScript, and import sources, and can only render components that use no hooks.
  • It lowercases every attribute name except data-* and aria-*, so write \"class\" and \"for\" when the string is the destination.
  • string_to_reactpy() parses HTML into a real VdomDict, renaming to React-style prop names, converting style strings to dicts, and moving value/<textarea> content to defaultValue.
  • It intercepts link clicks and infers a key from id unless you tell it not to.
  • strict=False turns a loud HTMLParseError into a guessed model; lxml never complained about unclosed tags anyway.
  • Neither function is the wire format \u2014 that is Protocol Structure.
"},{"location":"learn/convert-between-vdom-and-html/#related","title":"Related","text":"
  • Creating HTML Tags \u2014 building the same models with html and Vdom
  • VDOM Mutations \u2014 what you may and may not do to a model after you get one
  • Creating VDOM Event Handlers \u2014 the eventHandlers slot these functions ignore
  • Common Props \u2014 attribute naming on the live rendering path
  • Protocol Structure \u2014 what actually goes over the socket
"},{"location":"learn/creating-a-react-app/","title":"Creating a Standalone React App","text":""},{"location":"learn/creating-a-react-app/#overview","title":"Overview","text":"

If you want to build a new app or website with React, we recommend starting with a standalone executor.

If your app has constraints not well-served by existing web frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, you can use ReactPy in standalone mode.

"},{"location":"learn/creating-a-react-app/#using-reactpy-for-full-stack","title":"Using ReactPy for full-stack","text":"

ReactPy is a component library that helps you build a full-stack web application. For convenience, ReactPy is also bundled with several different standalone executors.

These standalone executors are the easiest way to get started with ReactPy, as they require no additional setup or configuration.

Note

Standalone ReactPy requires a server

In order to serve the initial HTML page, you will need to run a server. The ASGI examples below use Uvicorn, but you can use any ASGI server.

Executors on this page can either support client-side rendering (CSR) or server-side rendering (SSR).

"},{"location":"learn/creating-a-react-app/#running-via-asgi-ssr","title":"Running via ASGI SSR","text":"

ReactPy can run in server-side standalone mode, where both page loading and component rendering occurs on an ASGI server.

This executor is the most commonly used, as it provides maximum extensibility.

First, install ReactPy and your preferred ASGI webserver.

Terminal

pip install reactpy[asgi] uvicorn[standard]\n

Next, create a new file called main.py containing the ASGI application:

main.py
from reactpy import component, html\nfrom reactpy.executors.asgi import ReactPy\n\n\n@component\ndef hello_world():\n    return html.div(\"Hello World\")\n\n\nmy_app = ReactPy(hello_world)\n

Finally, use your webserver of choice to start ReactPy:

Terminal

uvicorn main:my_app\n
"},{"location":"learn/creating-a-react-app/#running-via-asgi-csr","title":"Running via ASGI CSR","text":"

ReactPy can run in client-side standalone mode, where the initial page is served using the ASGI protocol. This is configuration allows direct execution of Javascript, but requires special considerations since all ReactPy component code is run on the browser via WebAssembly.

First, install ReactPy and your preferred ASGI webserver.

Terminal

pip install reactpy[asgi] uvicorn[standard]\n

Next, create a new file called main.py containing the ASGI application, and a root.py file containing the root component:

main.pyroot.py
from pathlib import Path\n\nfrom reactpy.executors.asgi import ReactPyCsr\n\nmy_app = ReactPyCsr(Path(__file__).parent / \"root.py\", initial=\"Loading...\")\n
from reactpy import component, html\n\n\n@component\ndef root():\n    return html.div(\"Hello World\")\n

Finally, use your webserver of choice to start ReactPy:

Terminal

uvicorn main:my_app\n
"},{"location":"learn/creating-a-react-app/#running-via-wsgi-ssr","title":"Running via WSGI SSR","text":"

Support for WSGI executors is coming in a future version.

"},{"location":"learn/creating-a-react-app/#running-via-wsgi-csr","title":"Running via WSGI CSR","text":"

Support for WSGI executors is coming in a future version.

"},{"location":"learn/creating-a-react-app/#running-as-a-native-app","title":"Running as a native app","text":"

Support for native apps (iO, Android, Windows, etc.) is coming in a future version.

"},{"location":"learn/creating-backends/","title":"Creating Backends","text":""},{"location":"learn/creating-backends/#overview","title":"Overview","text":"

A ReactPy backend is the piece that owns the HTTP routes, serves the client bundle, accepts the websocket, and drives a Layout against it. ReactPy and ReactPyMiddleware are two backends. This page is about writing the third: what the framework actually does for you, which parts you have to reproduce, and where the quiet traps are. It is also the fastest way to understand the request lifecycle, even if you never ship a custom backend.

You will learn

  • The four jobs a backend performs, in the order a browser makes them necessary
  • The three ASGI routes ReactPy needs, and the exact shapes of the messages on each
  • How to run a Layout by hand with serve_layout()
  • Why Connection is yours to construct, and what carrier means
"},{"location":"learn/creating-backends/#what-a-backend-owes-you","title":"What a backend owes you","text":"

Strip away the executors and there are exactly four responsibilities. Everything else \u2014 hot reloading, config validation, HTML assembly \u2014 is convenience on top.

  1. Serve the client bundle over HTTP, so the page can import it.
  2. Serve web modules over HTTP, so the client can import any importSource you emitted.
  3. Return a document containing a mount element and a mountReactPy(...) call.
  4. Accept a websocket and run one Layout against it, pumping messages in both directions.

The protocol is four message types. A rendered update goes down as {\"type\": \"layout-update\", \"path\": ..., \"model\": ...}; an event comes up as {\"type\": \"layout-event\", \"target\": ..., \"data\": [...]}; and when a handler reads a property of that event, the request goes down as {\"type\": \"event-proxy-request\", ...} and the browser's answer comes up as {\"type\": \"event-proxy-response\", ...}. Protocol Structure documents them in full \u2014 this page is about plumbing, not the schema.

The good news about the last pair: serve_layout wires both directions for you. It binds your send as the proxy sender, and Layout.deliver recognises a response and routes it to the proxy backend instead of treating it as an event. A backend which pumps every received frame into deliver and every message out through send supports live event access without knowing it exists. A backend that filters frames before deliver \u2014 the way the ASGI middleware does, matching on \"layout-event\" first \u2014 must route the responses itself or every await on an event stalls until it times out.

"},{"location":"learn/creating-backends/#the-job-in-one-file","title":"The job, in one file","text":"

This is a complete, working backend: no framework, no executor. It was run, and the counter incremented in a real browser.

main.py Run
\"\"\"A ReactPy backend with no framework: routing, static files, and Layout.\"\"\"\n\nimport asyncio\nfrom pathlib import Path\n\nimport orjson\nfrom asgi_tools import ResponseHTML\nfrom servestatic import ServeStaticASGI\n\nimport reactpy\nfrom reactpy import component, html, use_state\nfrom reactpy.config import REACTPY_WEB_MODULES_DIR\nfrom reactpy.core.hooks import ConnectionContext\nfrom reactpy.core.layout import Layout\nfrom reactpy.core.serve import serve_layout\nfrom reactpy.types import Connection, Location\n\nPATH_PREFIX = \"/reactpy/\"\nSTATIC_PATH = f\"{PATH_PREFIX}static/\"\nMODULES_PATH = f\"{PATH_PREFIX}modules/\"\n\nSTATIC_DIR = Path(reactpy.__file__).parent / \"static\"\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"Count: {count}\"),\n        html.button({\"onClick\": handle_count_click}, \"Increment\"),\n    )\n\n\ndef index_html() -> str:\n    mount = (\n        \"mountReactPy({mountElement: document.getElementById('app'),\"\n        \"  pathPrefix:'\" + PATH_PREFIX + \"', componentPath:'counter/'});\"\n    )\n    return (\n        \"<!doctype html><html><head><meta charset='utf-8'><title>Custom backend</title>\"\n        \"<script type='module'>\"\n        f\"import {{ mountReactPy }} from '{STATIC_PATH}index.js';\"\n        + mount\n        + \"</script></head><body><div id='app'>Loading...</div></body></html>\"\n    )\n\n\nclass Backend:\n    def __init__(self) -> None:\n        self.components = {\"counter\": counter}\n        self.static_app = ServeStaticASGI(\n            self.not_found, root=STATIC_DIR, prefix=STATIC_PATH\n        )\n        self.modules_app = ServeStaticASGI(\n            self.not_found, root=REACTPY_WEB_MODULES_DIR.current, prefix=MODULES_PATH\n        )\n\n    async def not_found(self, scope, receive, send) -> None:\n        await ResponseHTML(\"Resource not found on this server.\", status_code=404)(\n            scope, receive, send\n        )\n\n    async def __call__(self, scope, receive, send) -> None:\n        path = scope[\"path\"]\n        if scope[\"type\"] == \"http\" and path.startswith(STATIC_PATH):\n            return await self.static_app(scope, receive, send)\n        if scope[\"type\"] == \"http\" and path.startswith(MODULES_PATH):\n            return await self.modules_app(scope, receive, send)\n        if scope[\"type\"] == \"http\" and path == \"/\":\n            return await ResponseHTML(index_html())(scope, receive, send)\n        if scope[\"type\"] == \"websocket\" and path.startswith(PATH_PREFIX):\n            return await self.serve_component(scope, receive, send)\n        return await self.not_found(scope, receive, send)\n\n    async def serve_component(self, scope, receive, send) -> None:\n        name = scope[\"path\"][len(PATH_PREFIX) :].strip(\"/\")\n        if name not in self.components:\n            raise RuntimeError(f\"Attempting to use an unregistered root {name}.\")\n\n        await send({\"type\": \"websocket.accept\"})\n        queue: asyncio.Queue = asyncio.Queue(1000)\n\n        async def pump() -> None:\n            while True:\n                event = await receive()\n                if event[\"type\"] == \"websocket.disconnect\":\n                    return\n                if event[\"type\"] == \"websocket.connect\":\n                    continue\n                await queue.put(orjson.loads(event[\"text\"]))\n\n        async def recv():\n            return await queue.get()\n\n        async def send_model(message):\n            await send(\n                {\"type\": \"websocket.send\", \"text\": orjson.dumps(message).decode()}\n            )\n\n        task = asyncio.create_task(pump())\n        connection = Connection(\n            scope=scope,\n            location=Location(path=\"/\", query_string=\"\"),\n            carrier=None,\n        )\n        try:\n            await serve_layout(\n                Layout(ConnectionContext(self.components[name](), value=connection)),\n                send_model,\n                recv,\n            )\n        finally:\n            task.cancel()\n\n\napp = Backend()\n
# TODO\n

Run it with anything that speaks ASGI:

uvicorn main:app --port 8000\n

Read it top to bottom and the four jobs map onto four spots: static_app and modules_app are jobs 1 and 2, index_html() is job 3, and serve_component() is job 4. The rest is __call__ dispatching on scope[\"type\"].

"},{"location":"learn/creating-backends/#the-routes","title":"The routes","text":"Route Serves Supplied by {path_prefix}static/ index.js and its chunks reactpy/static/ inside the wheel {path_prefix}modules/ authored JavaScript modules config.REACTPY_WEB_MODULES_DIR {path_prefix}<name>/ (websocket) the layout for <name> your components mapping

The trailing slash on the websocket path is not decoration. The stock executors match ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$, and the client builds that URL by concatenating pathPrefix and componentPath. A custom backend can match anything \u2014 but then componentPath has to agree with it, and pathPrefix must start and end with /. See The URL is built by concatenation for why a missing slash fails loudly in development and silently in production.

"},{"location":"learn/creating-backends/#serving-the-bundle","title":"Serving the bundle","text":"

Two directories, both plain static files:

import reactpy\nfrom pathlib import Path\n\nfrom servestatic import ServeStaticASGI\n\nfrom reactpy.config import REACTPY_WEB_MODULES_DIR\n\nstatic_app = ServeStaticASGI(\n    not_found, root=Path(reactpy.__file__).parent / \"static\", prefix=\"/reactpy/static/\"\n)\nmodules_app = ServeStaticASGI(\n    not_found, root=REACTPY_WEB_MODULES_DIR.current, prefix=\"/reactpy/modules/\"\n)\n

servestatic is what the executors use, and it is a normal ASGI app \u2014 swap in your own static handler if you have one. Two details matter more than the choice of library.

Resolve REACTPY_WEB_MODULES_DIR.current at request time or shortly after, never at import time. It is an Option, and the executors repoint it. Reading it too early hands you a directory that later ceases to be the live one.

The <script type=\"module\"> must be a module. The bundle uses static import for its two sibling chunks, so an ordinary <script> fails before any of your code runs, usually with a bare SyntaxError: Cannot use import statement outside a module in the console.

Serve the bundle from your own path at your peril

The client imports its chunks by a path derived from where index.js was loaded. Serve it under a prefix that does not also serve static/, and the chunk requests 404 while index.js itself loaded fine \u2014 which reads as a partial success and is easy to misdiagnose. Keep static/ a child of your mount prefix, exactly as the executors do.

"},{"location":"learn/creating-backends/#driving-a-layout","title":"Driving a layout","text":"

This is the part that is genuinely yours. Everything else is file serving.

The two primitives are Layout (core/layout.py) and serve_layout (core/serve.py). The latter is about twenty lines: it opens the layout, then runs one loop sending await layout.render() out and one loop calling await layout.deliver(event) on each received message \u2014 with the deliver not awaited to completion, so a slow handler never blocks the next event.

You supply send and recv coroutines:

async def send_model(message):\n    await send({\"type\": \"websocket.send\", \"text\": orjson.dumps(message).decode()})\n\n\nasync def recv():\n    return await queue.get()\n

Four things go wrong here, and all four are worth knowing before you hit them.

"},{"location":"learn/creating-backends/#1-the-first-receive-is-websocketconnect","title":"1. The first receive() is websocket.connect","text":"

await send({\"type\": \"websocket.accept\"}) completes the handshake, but the websocket.connect event is still sitting in the incoming stream. A pump loop that treats every event as a message will orjson.loads() a dict with no text key and die, typically with TypeError or KeyError: 'text', right at connection time. Skip it:

async def pump():\n    while True:\n        event = await receive()\n        if event[\"type\"] == \"websocket.disconnect\":\n            return\n        if event[\"type\"] == \"websocket.connect\":\n            continue\n        await queue.put(orjson.loads(event[\"text\"]))\n

This is the single most common way a hand-written backend fails, because everything looks right until the client connects: the page serves, the bundle loads, and the socket opens and closes immediately. The console shows [ReactPy] Disconnected! followed by an endless reconnect loop.

"},{"location":"learn/creating-backends/#2-recv-must-not-return-none","title":"2. recv() must not return None","text":"

If your recv returns None \u2014 which is what an unguarded await receive() does on disconnect \u2014 serve_layout calls layout.deliver(None) and raises inside a task group, surfacing as an ExceptionGroup that names neither your code nor the cause. Return a valid message or raise; never None.

"},{"location":"learn/creating-backends/#3-responsewebsocket-has-no-send_text-and-receive-needs-rawtrue","title":"3. ResponseWebSocket has no send_text, and receive() needs raw=True","text":"

asgi_tools.ResponseWebSocket is what the built-in executors use \u2014 ReactPyWebsocket subclasses it \u2014 and it works fine as a backend's socket. Two API details differ from raw ASGI, and getting either wrong fails inside an anyio task group with an ExceptionGroup that names neither your code nor the cause:

async with ResponseWebSocket(scope=scope, receive=receive, send=send) as ws:\n\n    async def pump():\n        while True:\n            event = await ws.receive(raw=True)  # the ASGI dict, not the payload\n            if event[\"type\"] == \"websocket.disconnect\":\n                return\n            if event[\"type\"] != \"websocket.receive\":\n                continue\n            await queue.put(orjson.loads(event[\"text\"]))\n\n    async def send_model(message):\n        await ws.send(orjson.dumps(message).decode())  # not send_text(...)\n
  • There is no send_text. On asgi_tools.response.ResponseWebSocket the methods are send(msg) \u2014 which wraps a str as {\"type\": \"websocket.send\", \"text\": ...} and bytes as \"bytes\": ... \u2014 plus send_json(data). Send text frames. The stock send_json emits {\"type\": \"websocket.send\", \"bytes\": json_dumps(data)}, i.e. a binary frame, and that is precisely why the executors do not use it: executors/asgi/middleware.py, ReactPyWebsocket.send_json overrides it to self._send({\"type\": \"websocket.send\", \"text\": orjson.dumps(data).decode()}). The client never sets socket.binaryType (static/index.js), so a binary frame arrives as a Blob, and JSON.parse(blob) throws SyntaxError: ... \"[object Blob]\" is not valid JSON \u2014 verified in headless Chrome. Reusing a stock asgi_tools websocket object instead of overriding send_json therefore produces a socket that connects, accepts, and then parses nothing.
  • receive() returns the payload by default, not the ASGI event. Without raw=True you get a str, and event[\"type\"] then raises TypeError: string indices must be integers. Pass raw=True.
  • Under async with, the handshake is already done \u2014 do not send websocket.accept yourself. That is why the websocket.connect skip above tests for != \"websocket.receive\" rather than naming connect.

Raw send/receive from the ASGI callable, as the main example does, is the interface guaranteed to be present on any host; that is the reason it is the default here.

"},{"location":"learn/creating-backends/#4-one-layout-per-connection-and-never-share-one","title":"4. One layout per connection, and never share one","text":"

A Layout owns the hook state for a single client. Creating it before the request, or caching it by component name, means every browser tab shares one set of use_state values \u2014 one user's click changes another's screen. Build the Layout inside the connection handler, as above, and let it be garbage-collected with the socket.

The mirror-image bug is at the component level: a module-level element rendered by every layout is shared too, and mutation by one client leaks to all of them. That is covered in VDOM Mutations.

"},{"location":"learn/creating-backends/#connection-and-carrier","title":"Connection and carrier","text":"

Wrap the component in ConnectionContext so the connection hooks work:

from reactpy.core.hooks import ConnectionContext\nfrom reactpy.types import Connection, Location\n\nconnection = Connection(\n    scope=scope,\n    location=Location(path=\"/\", query_string=\"\"),\n    carrier=None,\n)\nlayout = Layout(ConnectionContext(my_component(), value=connection))\n

Without that wrapper, use_connection(), use_location(), and use_scope() all raise RuntimeError: No backend established a connection. \u2014 even inside a perfectly healthy socket.

Connection is a three-field dataclass, and as the backend author you decide what goes in it:

  • scope \u2014 pass the websocket scope through verbatim. use_scope() returns it, and users expect headers and query strings to be there.
  • location \u2014 the stock ASGI executors do not use scope[\"path\"]. The browser reports where it is, and the executor parses it out of the websocket query string: ?path=%2Fdocs%2F&qs=a%3D1 becomes Location(path=\"/docs/\", query_string=\"a=1\"). If you hard-code path=\"/\", every use_location() on your app reports the root regardless of the URL in the address bar. Parse it the same way if your app routes on it.
  • carrier \u2014 whatever mediates the connection. Nothing in ReactPy reads it; the executors put their websocket object there so your components can reach it. Put your own in, and document what it is, because User Connection tells people to check its type rather than assume. None is fine if nothing needs it.

Connection is a read-only view by design

There is no deliver, no send, no close on it. Components talk to the client through state, effects, and event handlers \u2014 not by pushing messages. Your backend is the only code allowed to touch the socket, precisely because letting a component write to the wire bypasses the protocol and desynchronizes the client. If you find yourself wanting a push channel, read Communicating Data Between Server and Client first.

"},{"location":"learn/creating-backends/#when-to-write-one","title":"When to write one","text":"

Almost never, and it is worth naming the honest cases:

  • Embedding ReactPy in an ASGI app you cannot wrap. ReactPyMiddleware already covers this \u2014 it takes your existing app as its first argument. Reach for a custom backend only when the middleware's routing gets in your way, since it owns {path_prefix}* and nothing else.
  • A non-ASGI host. ReactPy's core is host-agnostic: Layout and serve_layout need only two coroutines, so a socket server on another loop is a routing problem, not a rewrite. This is the case the design genuinely supports.
  • Owning the document. If you generate HTML yourself and want ReactPy components inside it, you need the mount markup and the import map. You usually want server_side_component_html for that rather than a new backend \u2014 see Serving a component from your own route.
  • Understanding it. Writing the backend once is the clearest way to learn where the boundary between layout and transport is. It is a good exercise even when the result is deleted.

What it does not buy you: performance. serve_layout in your own code is the same loop. And you take on the pieces the executors handle quietly \u2014 config validation, the import-map injection, pyscript_setup, static caching, reconnect options, and the error paths in the four sections above.

"},{"location":"learn/creating-backends/#summary","title":"Summary","text":"
  • A backend serves the bundle, serves web modules, returns a mount document, and runs one Layout per websocket.
  • The message shapes are fixed: layout-update and event-proxy-request down, layout-event and event-proxy-response up.
  • Feed every received frame to layout.deliver and serve_layout routes event-proxy responses for you; filter frames yourself and you must route them yourself.
  • Skip the websocket.connect event in your pump; it is the most common cause of an instantly-dying socket.
  • Speak raw ASGI on the socket; framework websocket wrappers do not have the methods serve_layout needs.
  • Build the Layout per connection, and wrap the component in ConnectionContext or the connection hooks raise.
  • You populate Connection; the stock executors parse location from the query string, not the scope path.
  • Components get a read-only view of the connection by design \u2014 that is the protocol's integrity, not an omission.
"},{"location":"learn/creating-backends/#related","title":"Related","text":"
  • Protocol Structure \u2014 all four message types, target, seq, and the event proxy round trip
  • Client API \u2014 what mountReactPy expects from your URL layout
  • ReactPy and ReactPyMiddleware \u2014 the two built-in backends
  • User Connection, User Scope, User Location \u2014 what your Connection feeds
  • Communicating Data Between Server and Client \u2014 the supported alternatives to pushing
"},{"location":"learn/creating-html-tags/","title":"Creating HTML Tags","text":""},{"location":"learn/creating-html-tags/#overview","title":"Overview","text":"

Every element you write in ReactPy is a function call that returns a dictionary. html.div(...) is not a keyword, a macro, or a parser \u2014 it is an attribute lookup that manufactures a constructor on first use, and a __call__ that assembles the model. Knowing how that works explains why there is no allow-list to fight, why html.del_ means <del>, and where the handful of hard errors come from.

You will learn

  • How html turns an attribute name into a tag name
  • The rule that decides whether your first argument is attributes or a child
  • Which elements reject children, and why html.script is stricter still
  • How to build fragments, SVG, and tag names that are not valid Python identifiers
"},{"location":"learn/creating-html-tags/#html-manufactures-tags-on-demand","title":"html manufactures tags on demand","text":"

html is an instance of HtmlConstructor with a single __getattr__ (_html.py). Ask it for anything and you get a Vdom constructor back, cached by name:

app.py Run
# The tag name is whatever you ask for -- there is no allow-list to consult.\nprint(html.div({\"id\": \"greeting\"}, \"Hello\"))\n# {'tagName': 'div', 'attributes': {'id': 'greeting'}, 'children': ['Hello']}\n\nprint(html.definitely_not_a_real_tag(\"still renders\"))\n# {'tagName': 'definitely-not-a-real-tag', 'children': ['still renders']}\n
# TODO\n

There is no allow-list. ReactPy does not know or care whether <definitely-not-a-real-tag> exists; the string travels to the browser and the browser decides. The names spelled out on HtmlConstructor exist only so your editor can autocomplete them \u2014 the comment in the source says as much. Two practical consequences:

  • A misspelled tag renders as an unknown element with your text inside it, silently. Nothing in ReactPy will tell you that html.hdeader is wrong.
  • Custom elements work with no ceremony: html.my_widget(...) produces {\"tagName\": \"my-widget\"} and the browser hands it to whatever registered that name.
"},{"location":"learn/creating-html-tags/#the-underscore-rules","title":"The underscore rules","text":"

__getattr__ applies exactly two transformations \u2014 value.rstrip(\"_\").replace(\"_\", \"-\") \u2014 which is how you write names Python will not let you spell:

app.py Run
# Underscores become dashes, and a trailing underscore is stripped -- the name\n# is translated, not looked up.\nprint(html.del_(\"removed\")[\"tagName\"])  # del\nprint(html.data_table()[\"tagName\"])  # data-table\nprint(html.my_widget()[\"tagName\"])  # my-widget\n\n# The same constructor comes back every time, so identity checks are cheap.\nprint(html.my_widget is html.my_widget)  # True\n
# TODO\n

Read the rules literally, because they are applied blindly:

You write Tag name Rule html.del_ del trailing _ stripped (a keyword) html.data_table data-table every _ becomes - html.my_widget my-widget same html.h1 h1 no underscore, nothing changes html._private -private leading underscore is not special html.a__b a--b doubles are not collapsed

Casing is passed through untouched, so html.myWidget gives you myWidget. Because translation happens before the cache lookup, html.data_table is html.data_table holds \u2014 but two spellings that translate to the same tag are the same object, and two that do not are different elements.

If a name cannot be written as an attribute at all \u2014 a leading digit, or one you are building from a variable \u2014 construct it directly:

from reactpy import Vdom\n\ntag = Vdom(\"2fa-code\")\ntag(\"123456\")  # {'tagName': '2fa-code', 'children': ['123456']}\n

Vdom(tag_name, /, allow_children=True, custom_constructor=None, import_source=None) is the same class every html.* name is built from, so you get identical behaviour. It is exported from the package root. Reach for it only for generated or unspellable names; anything else should go through html so it reads consistently.

html is not a namespace of plain functions

html.div is a Vdom object, not a function \u2014 html.div(\"x\") works because Vdom.__call__ is the constructor. So html.div.__name__ is the tag name rather than a qualified name, and any name you ask for is fair game: html.allow_children(\"x\") returns a perfectly valid {\"tagName\": \"allow-children\"} element rather than tripping over the Vdom attribute of the same name.

"},{"location":"learn/creating-html-tags/#the-two-call-shapes","title":"The two call shapes","text":"

Vdom.__call__ accepts either children only, or an attribute dictionary followed by children (core/vdom.py, Vdom.__call__):

app.py Run
# Two call shapes: children only, or an attribute dict first.\nprint(html.button(\"Click Me\"))\n# {'tagName': 'button', 'children': ['Click Me']}\n\nprint(html.button({\"id\": \"save\", \"disabled\": True}, \"Save\"))\n# {'tagName': 'button', 'attributes': {'id': 'save', 'disabled': True}, 'children': ['Save']}\n\n# Only an *exact* dict counts as attributes -- a dict subclass is a child.\nprint(html.div(VdomDict(tagName=\"span\")))\n# {'tagName': 'div', 'children': [{'tagName': 'span'}]}\n
# TODO\n

The discriminator is exact-type, not isinstance: type(values[0]) is dict (core/vdom.py, separate_attributes_and_children). Two consequences that bite:

  • A VdomDict \u2014 which is a dict subclass \u2014 in first position is treated as a child, which is what you want when nesting models you built earlier.
  • Any other dict subclass in first position is also a child, so a custom mapping you meant as attributes becomes a rendered object instead. Pass a plain dict literal for attributes.

A dict in any later position is always a child. There is one slot for attributes and it is the first.

"},{"location":"learn/creating-html-tags/#attribute-values-are-forwarded-verbatim","title":"Attribute values are forwarded verbatim","text":"

ReactPy renames nothing. className, class, htmlFor, and for all reach the DOM as whichever string you typed, and unknown names like data-* and aria-* pass straight through. Nothing validates names against a list, so a typo is a silent no-op \u2014 {\"class_name\": \"card\"} renders <div class_name=\"card\">, an attribute nobody reads.

There are no exceptions to this rule on the live path. class_name is not a thing. See Common Props for the full picture, including the one case where numeric style values behave differently between executors.

Attributes must be JSON-serializable, but you may not be told

Everything in the attribute dict is JSON-encoded on its way to the browser. ReactPy checks that with json.dumps only when REACTPY_CHECK_JSON_ATTRS is on \u2014 and that option's parent is REACTPY_DEBUG, so turning debug on turns this on too:

from reactpy import config\n\nconfig.REACTPY_DEBUG.current = True\n

With the check on, html.div({\"data\": object()}) raises TypeError: Object of type object is not JSON serializable at the tag call, pointing at your line. With it off, the model is accepted and the failure surfaces much later, inside the websocket send, as an orjson error in a server log. If a page renders nothing and the browser console is empty, this is why.

"},{"location":"learn/creating-html-tags/#fragments","title":"Fragments","text":"

html.fragment(...) and the shorter html(...) build a child list with no element of its own \u2014 tagName is the empty string, and the client renders it as nothing:

app.py Run
# Fragments carry no tag of their own: tagName is the empty string.\nprint(html.fragment(html.b(\"bold\"), html.i(\"italic\")))\n# {'tagName': '', 'children': [{'tagName': 'b', ...}, {'tagName': 'i', ...}]}\n\n# html(...) is html itself: HtmlConstructor.__call__ is the fragment call.\nprint(html(html.b(\"bold\"))[\"tagName\"])  # ''\nprint(html()[\"tagName\"])  # '' -- an empty fragment\n\n# A fragment may carry a key -- and nothing else.\nprint(html.fragment({\"key\": \"row-1\"}, html.b(\"x\"))[\"attributes\"])  # {'key': 'row-1'}\n\ntry:\n    html.fragment({\"className\": \"wrap\"}, html.b(\"x\"))\nexcept TypeError as error:\n    print(error)  # Fragments cannot have attributes besides 'key'\n
# TODO\n

A fragment accepts key and nothing else: a className, a style, or an event handler all raise TypeError: Fragments cannot have attributes besides 'key'. There is no node to put them on, so ReactPy refuses rather than dropping them quietly.

Use a fragment when a component must return siblings and a wrapper <div> would break the layout or the CSS. When the children are already in a list, pass the list \u2014 you do not need a fragment for that, though you do need keys, covered in Rendering Lists.

"},{"location":"learn/creating-html-tags/#void-elements","title":"Void elements","text":"

html.br(\"x\") is a hard error, not a warning that scrolls past:

app.py Run
# Void elements reject children -- loudly, at the call site.\ntry:\n    html.br(\"oops\")\nexcept TypeError as error:\n    print(error)  # 'br' nodes cannot have children.\n\nprint(html.br())  # {'tagName': 'br'}\n\n# script is stricter still: one string child, no handlers, an inferred key.\nprint(html.script(\"var answered = 42;\"))\n# {'tagName': 'script', 'children': ['var answered = 42;'],\n#  'attributes': {'key': 'var answered = 42;'}}\n\nprint(html.script({\"src\": \"/chart.js\"}))\n# {'tagName': 'script', 'attributes': {'src': '/chart.js', 'key': '/chart.js'}}\n\n\nasync def handle_load(event):\n    pass\n\n\ntry:\n    html.script({\"onLoad\": handle_load}, \"var x = 1;\")\nexcept ValueError as error:\n    print(error)  # 'script' elements do not support event handlers\n
# TODO\n

The rejecting set is NO_CHILDREN_ALLOWED_HTML_BODY (_html.py): area, base, br, col, command, embed, hr, img, input, iframe, keygen, link, meta, param, portal, source, track, wbr. The check happens after the model is built, so the message names the tag rather than your mistake \u2014 read the name in the error.

iframe is on that list, which surprises people who put fallback content inside one. ReactPy will not let you, and the browser-side renderer would not have shown it anyway.

"},{"location":"learn/creating-html-tags/#htmlscript-is-its-own-thing","title":"html.script is its own thing","text":"

html.script uses a custom constructor (_html.py, _script) with three rules and a side effect:

  • At most one child, and it must be a str \u2014 ValueError: 'script' nodes may have, at most, one child. and The child of a 'script' must be a string.
  • No event handlers at all \u2014 ValueError: 'script' elements do not support event handlers. Attach behaviour to a sibling element instead, or use the inline-JavaScript handler form on a normal tag (Creating VDOM Event Handlers).
  • A key is injected from the script's content, or from its src if it has no content. Upstream documents the purpose as allowing the script \"to be run multiple times if its key changes\", browser permitting. Pass your own key to control re-execution yourself.

Scripts are an injection surface

The child string reaches the browser as JavaScript. Never interpolate untrusted text into it \u2014 ReactPy's own docstring on _script says so, and there is no escaping step to save you. Build the string from your own constants, or pass a src you control.

"},{"location":"learn/creating-html-tags/#svg-is-a-second-constructor","title":"SVG is a second constructor","text":"

html.svg is a separate SvgConstructor, so SVG element names live in their own namespace and cannot collide with HTML names. Children come off it:

app.py Run
# SVG lives on a second constructor, so html.svg.circle() does not collide\n# with a hypothetical html.circle().\nprint(\n    html.svg(\n        {\"width\": \"100\", \"height\": \"100\", \"viewBox\": \"0 0 100 100\"},\n        html.svg.circle({\"cx\": \"50\", \"cy\": \"50\", \"r\": \"40\", \"fill\": \"tomato\"}),\n    )\n)\n# {'tagName': 'svg', 'attributes': {...},\n#  'children': [{'tagName': 'circle', 'attributes': {...}}]}\n
# TODO\n

It has its own void set (NO_CHILDREN_ALLOWED_SVG), and that set is broader and less intuitive than the HTML one \u2014 it includes text, tspan, title, desc, path, use, and foreignObject. So the element you most need children inside is exactly the one that rejects them:

html.svg.text(\"hello\")  # TypeError: 'text' nodes cannot have children.\n

Build those nodes with Vdom, which shares all the behaviour minus the void check:

from reactpy import Vdom\n\nVdom(\"text\")(\"hello\")  # {'tagName': 'text', 'children': ['hello']}\n

The bundled client creates every element through document.createElementNS(namespace, ...), picking the namespace from the tag name: \"svg\" gets http://www.w3.org/2000/svg, \"math\" gets the MathML URI, everything else the XHTML one. That logic lives in the preact runtime ReactPy bundles into its client chunks, not in ReactPy's own @reactpy/client source \u2014 so grep there for createElementNS and you will find nothing. Inside the SVG namespace attribute names are forwarded verbatim rather than lower-cased, so viewBox keeps its capital B \u2014 write it that way, since viewbox is a different attribute to the DOM.

"},{"location":"learn/creating-html-tags/#choosing-children-over-attributes","title":"Choosing children over attributes","text":"

html.div({\"children\": [\"a\", \"b\"]}) does not do what it looks like. The dict is attributes, so you get an attribute literally named children and no children at all. Children are always positional arguments after the attribute dict \u2014 that is the only way to supply them:

html.div({\"id\": \"list\"}, html.li(\"one\"), html.li(\"two\"))   # correct\nhtml.ul({\"children\": [html.li(\"one\")]})                     # an attribute named children\n

Children are flattened fully, not one level at a time, for any iterable that is not a string or a mapping \u2014 core/vdom.py, _flatten_children recurses, and verified html.div([[[\"a\"], \"b\"]]) yields children: ['a', 'b']. A list or tuple comprehension therefore works at any nesting depth, and None is skipped by the layout. Two rules from there are worth internalizing now, because both failures are quiet:

  • Elements inside a list need keys, in debug mode, or you get Key not specified for child in list ....
  • A generator in place of a list skips that check entirely, with Did not verify key-path integrity of children in generator .... Pass a list. The deeper hazard \u2014 storing an iterator in state \u2014 is covered in Rendering Lists.
"},{"location":"learn/creating-html-tags/#summary","title":"Summary","text":"
  • html invents a tag for any attribute name; the two rules are trailing _ stripped and every _ turned into -. There is no allow-list and no warning for a typo.
  • The first argument is attributes only if it is exactly a dict; everything else, including VdomDict and dict subclasses, is a child.
  • Attribute names are forwarded verbatim \u2014 use the DOM's spelling. Turn on REACTPY_DEBUG to catch un-serializable attribute values at the call site.
  • html.fragment and html() take only a key.
  • The void elements in NO_CHILDREN_ALLOWED_HTML_BODY reject children with a TypeError; html.script additionally allows one string child, forbids handlers, and infers its own key.
  • html.svg.* uses a broader void set that includes text; build those with Vdom.
  • Vdom(tag_name) is the class behind every html.* name \u2014 use it for tag names you cannot spell as an attribute, and for the SVG nodes html.svg rejects.
"},{"location":"learn/creating-html-tags/#related","title":"Related","text":"
  • Common Props \u2014 attribute naming, style, and the executor differences
  • Creating VDOM Event Handlers \u2014 the eventHandlers half of the model
  • VDOM Mutations \u2014 editing a model after you built it
  • Conditional Rendering and Rendering Lists \u2014 the child rules with their failure modes
  • Convert Between VDOM and HTML \u2014 the same models as text, and what that loses
"},{"location":"learn/creating-vdom-event-handlers/","title":"Creating VDOM Event Handlers","text":""},{"location":"learn/creating-vdom-event-handlers/#overview","title":"Overview","text":"

An event handler in ReactPy is a coroutine you put in an element's attribute dict. ReactPy inspects the value, not the name, decides what it is, and files it under one of three keys in the model \u2014 refusing it outright if it cannot be awaited. Most of the surprises on this page come from that one decision point: what counts as a handler, what counts as JavaScript, and what happens to the options you thought you set.

You will learn

  • The three buckets an attribute value lands in, and the rule that sorts them
  • Why a handler must be async def, and what that rules out
  • Why preventDefault() sometimes reaches the browser and sometimes does not
  • The difference in arguments between a plain callable and an EventHandler
  • How to set debounce and throttle, and what values are rejected
"},{"location":"learn/creating-vdom-event-handlers/#the-three-buckets","title":"The three buckets","text":"

When you call html.button({...}, ...), ReactPy walks the attribute dict once and routes every value by kind \u2014 callable first, then string-under-onXxx, then everything else (core/vdom.py, separate_attributes_handlers_and_inline_javascript):

app.py Run
# ReactPy sorts each value in the attribute dict into one of three buckets\n# by *kind*, not by name (core/vdom.py, separate_attributes_handlers_and_inline_javascript).\nasync def save(event):\n    print(\"saved\", await event.target.value)\n\n\nelement = html.button(\n    {\n        \"id\": \"save\",  # str, not onXxx -> attribute\n        \"onClick\": save,  # callable -> event handler\n        \"onHover\": \"console.log('hover')\",  # str under onXxx -> inline JS\n        \"data-row\": \"3\",  # str, not onXxx -> attribute\n    },\n    \"Save\",\n)\n\nprint(sorted(element))\n# ['attributes', 'children', 'eventHandlers', 'inlineJavaScript', 'tagName']\nprint(sorted(element[\"attributes\"]))  # ['data-row', 'id']\nprint(sorted(element[\"eventHandlers\"]))  # ['onClick']\nprint(sorted(element[\"inlineJavaScript\"]))  # ['onHover']\n
# TODO\n

The rules, exactly:

Value Lands in Note Anything callable eventHandlers under any key name; anything callable which is not a coroutine raises A BaseEventHandler eventHandlers stored as-is, untouched A str under a key matching ^on[A-Z]\\w+ inlineJavaScript eval'd in the page A reactpy.types.InlineJavaScript string inlineJavaScript under any key Everything else attributes forwarded verbatim

Three of those rows have consequences worth spelling out.

callable wins over the key name. {\"notAnHandler\": my_async_function} registers a real listener whose target ends in :notAnHandler. It will never fire, because no DOM event is named that, but it is registered. Conversely {\"onClick\": True} is not a handler \u2014 a bool is not callable, so it becomes an attribute and no listener exists. Neither mistake produces an error. What does raise is a callable ReactPy cannot await: the sort step builds an EventHandler for every callable value, and that is where the coroutine check lives.

Calling instead of passing gives you something that is not a handler. A handler is a coroutine, so {\"onClick\": handle_click()} hands over a coroutine object, which is not callable and so falls through to the attributes: the button renders, looks dead, and the failure surfaces later as TypeError: Type is not JSON serializable: coroutine when the model is encoded for the wire \u2014 plus a RuntimeWarning: coroutine ... was never awaited in the log. Calling something that was never a handler ({\"onClick\": print(\"hi\")}) is the None case, and that one really is silent on both sides. Either way: pass the function, do not call it. The checklist in What to check when a control does nothing covers all three shapes.

A dict is never a handler. The React-style {\"function\": fn, \"preventDefault\": True} becomes an ordinary attribute; no listener is attached. With REACTPY_DEBUG on it fails loudly instead \u2014 TypeError: Object of type function is not JSON serializable \u2014 which is the better outcome. Build handlers with @event or EventHandler, never with a dict.

"},{"location":"learn/creating-vdom-event-handlers/#plain-coroutines-the-shape-you-want-most-of-the-time","title":"Plain coroutines, the shape you want most of the time","text":"

Pass a coroutine and ReactPy wraps it. The browser sends a list of arguments; a plain coroutine receives it unpacked positionally (core/events.py, to_event_handler_function), which for a DOM event means one argument:

async def handle_click(event):\n    print(await event.target.value)\n\n\nhtml.button({\"onClick\": handle_click}, \"Click me\")\n

The parameter name is yours to choose \u2014 the value is positional. What is not optional is async def: EventHandler.__init__ and to_event_handler_function both run the value through check_is_async_handler, and anything that is not awaitable raises there. A coroutine function, a callable object whose __call__ is a coroutine, and a functools.partial or functools.wraps wrapper around either all pass. A plain def and a lambda do not \u2014 the latter has no syntax for await, which is why the old inline-handler idiom is gone.

The value handed over is a reactpy.types.Event: not a dict, but a live proxy onto the browser object, where await event.target.value is the read and the property chain matches JavaScript's. See Responding to Events for the payload surface and Protocol Structure for what crosses the wire.

Declare exactly one parameter

The list is unpacked onto your signature, so a handler declaring two required parameters fails at the moment of the click with TypeError: ... missing 1 required positional argument, logged server-side only \u2014 the browser stays silent and the button just stops working. Extra context has to be bound in Python, and since a lambda is no longer available that means a named handler or a closure:

def delete_handler(item_id):\n    async def handle_click(event):\n        await delete(item_id)\n\n    return handle_click\n\n\nhtml.button({\"onClick\": delete_handler(item.id)}, \"Delete\")\n

functools.partial does the same job, with one catch: the client still hands over the event, so the underlying function must leave exactly one parameter free \u2014 partial(delete, item.id) over async def delete(item_id, event), not over async def delete(item_id). Binding the leading argument also moves the event off the first parameter, which is what the bytecode detector matches, so see Be discovered: bytecode inspection below: a preventDefault() in that handler is not detected, and @event(prevent_default=True) is the fix.

"},{"location":"learn/creating-vdom-event-handlers/#eventhandler-the-pre-built-form-and-its-different-argument","title":"EventHandler: the pre-built form, and its different argument","text":"

EventHandler is what the @event decorator returns, and you can construct it directly. Two things change when you do.

First, ReactPy stores an EventHandler as-is rather than wrapping it, so the same object can serve every element that uses it. It does not follow that they share a target. The target is derived per element: unless the handler carries an explicit target, core/layout.py builds target = f\"{key_path}:{event}\", and @event leaves target as None. Verified with one @event handler on two buttons: the targets came out /0/0:onClick and /0/1:onClick \u2014 one function object, two targets.

Do not try to force the sharing by setting target= yourself. Two elements registered under the same explicit target overwrite each other in the layout's handler table, and the unmount path then tries to del that one key twice \u2014 verified, the second deletion raises KeyError from Layout._unmount_model_states.

Second, and this is the trap: an EventHandler receives the list itself, not the unpacked arguments.

app.py Run
# A plain coroutine gets the browser's argument list UNPACKED.\nasync def plain_handler(event):\n    print(await event.target.value)  # the event itself\n\n\n# An EventHandler gets the LIST ITSELF: an EventArguments, a list subclass.\nasync def list_handler(data):\n    print(await data[0].target.value)  # index first, then read as usual\n\n\nhtml.button({\"onClick\": plain_handler}, \"a\")\nhtml.button({\"onClick\": EventHandler(list_handler, prevent_default=True)}, \"b\")\n\n# Hand the list to a body written for the event and the first read fails:\n# AttributeError: 'EventArguments' object has no attribute 'target'\n# Hand the event to a body written for the list and subscripting it fails:\n# TypeError: 'Event' object is not subscriptable\n
# TODO\n

The failure when you get it backwards differs by direction, and the layout swallows either one into a server-side Failed to execute event handler line \u2014 nothing appears in the browser console, so the UI merely stops responding. Hand the list to a handler written for the event and the first property read raises AttributeError: 'EventArguments' object has no attribute 'target' \u2014 the list is an EventArguments, a list subclass, not the event. Hand the unpacked event to a handler written for the list and you get TypeError: 'Event' object is not subscriptable. Prefer the plain-coroutine form unless you specifically need to hand-build a handler; @event gives you the same options with the friendlier argument shape.

"},{"location":"learn/creating-vdom-event-handlers/#setting-the-five-options","title":"Setting the five options","text":"

Five behaviours run in the browser before an event is sent. They are properties on the handler object, never attributes on the element:

Option Type Effect prevent_default bool calls preventDefault() in the page stop_propagation bool calls stopPropagation() in the page stop_immediate_propagation bool also calls stopImmediatePropagation(), blocking sibling listeners debounce int ms coalesces outgoing events on a trailing edge, so the server sees one event after the user pauses \u2014 applied only to <input>, <select>, and <textarea> throttle int ms rate-limits outgoing events on a leading edge, on any element

There are two ways to set them, and the difference matters.

"},{"location":"learn/creating-vdom-event-handlers/#say-it-event","title":"Say it: @event","text":"
from reactpy import event\n\n\n@event(\n    prevent_default=True,\n    stop_propagation=True,\n    stop_immediate_propagation=True,\n    debounce=250,\n    throttle=100,\n)\nasync def handle_key_down(event_data): ...\n\n\nhtml.input({\"onKeyDown\": handle_key_down})\n

This is explicit and it always works. Reach for it by default.

"},{"location":"learn/creating-vdom-event-handlers/#be-discovered-bytecode-inspection","title":"Be discovered: bytecode inspection","text":"

ReactPy also reads your function's bytecode looking for three specific statements, so you can write them inline instead (core/_event_inspect.py, inspect_event_handler):

app.py Run
# Same behaviour, discovered by reading your bytecode instead of a decorator.\nasync def submit(event):\n    event.preventDefault()\n    event.stopPropagation()\n    event.debounce = 250\n\n\nhandler = html.form({\"onSubmit\": submit}, \"x\")[\"eventHandlers\"][\"onSubmit\"]\nprint(handler.prevent_default)  # True\nprint(handler.stop_propagation)  # True\nprint(handler.debounce)  # 250\n
# TODO\n

This is a source-parsing hack, not a runtime mechanism, and it has limits you should know before relying on it. Detection requires all of the following:

  • The call is written literally in the handler's own body. event.preventDefault() inside a helper the handler calls is invisible to the inspector \u2014 it does not follow calls.
  • The first parameter is the event. The inspector reads co_varnames[0] and matches against it.
  • For debounce, the assigned value must be resolvable at inspect time: an integer literal, a compile-time-folded expression like 100 * 5, a numeric default on a parameter (async def h(event, ms=350)), or a numeric closure cell. It cannot resolve a parameter with no default, a module-level global, or an arbitrary expression \u2014 all three inspect to None. And it stops scanning at the first assignment it can settle \u2014 resolved or not \u2014 so with two assignments to event.debounce the result depends on which one the bytecode reaches first. Verified: if event: event.debounce = 200 / else: event.debounce = len(x) inspects to 200, while the same branches written in the other order inspect to None. Write one assignment and the question does not arise; with more than one, check the value you get.

The second rule produces the sharpest bug, so it deserves its own call-out:

Bound methods are not inspected correctly

For async def on_submit(self, event), the first parameter is self, so the inspector never matches the event variable and reports prevent_default=False. The handler is a coroutine, so it passes the check and still fires \u2014 but your event.preventDefault() call is silently discarded:

app.py Run
# A bound method is a fine handler -- it fires. What is lost is detection of\n# the event.preventDefault() inside it: the bytecode inspector reads the\n# *first* parameter as the event, and for a method that is `self`.\nclass SearchForm:\n    async def on_submit(self, event):\n        event.preventDefault()\n\n\nplain = html.form({\"onSubmit\": SearchForm().on_submit})[\"eventHandlers\"][\"onSubmit\"]\nprint(plain.prevent_default)  # False -- your call was never seen\n\n\n# Say it explicitly with @event and it works.\n@event(prevent_default=True)\nasync def submit(event_data):\n    print(\"submitted\")\n\n\nexplicit = html.form({\"onSubmit\": submit})[\"eventHandlers\"][\"onSubmit\"]\nprint(explicit.prevent_default)  # True\n
# TODO\n

Attach a plain function defined inside the component instead, or pass @event(prevent_default=True) explicitly over the method. A functools.wraps decorator is fine \u2014 the inspector follows __wrapped__ to the function you wrote, first parameter and all. A functools.partial is unwrappable too, but only half-fixed: once the leading argument is bound, the first parameter the inspector reads is yours, not the event's, so the same silent False appears. Same escape hatch: declare the option with @event.

An inline-JavaScript handler cannot be inspected at all

Detection runs on Python bytecode. {\"onClick\": \"this.innerText = 'x'\"} has none, so there is no preventDefault equivalent in the string form. Call it yourself in the JavaScript: \"event.preventDefault(); doThing()\".

"},{"location":"learn/creating-vdom-event-handlers/#inline-javascript","title":"Inline JavaScript","text":"

A str under an onXxx key is stored under inlineJavaScript and eval'd in the page; if it evaluates to a function, that function is called with the event:

html.button({\"onClick\": '(event) => (event.target.innerText = \"Thank you!\")'}, \"Click\")\nhtml.button({\"onClick\": \"this.innerText = 'Thank you!'\"}, \"Click\")\n

This is the way to touch the DOM from ReactPy, and the reason it exists is that the server has no node to hold (Manipulating the DOM with Refs). Keep each snippet to a single call, and never interpolate anything you did not write:

Trusted strings only

The string runs with your application's privileges, in the page. f\"this.innerText = '{user_input}'\" is an injection point. Build strings from constants, or use a Python handler and pass data through state.

Note also that the pattern is ^on[A-Z]\\w+ \u2014 a lowercase {\"onclick\": \"...\"} matches nothing and becomes a plain attribute that never fires.

"},{"location":"learn/creating-vdom-event-handlers/#merging-handlers","title":"Merging handlers","text":"

reactpy.core.events.merge_event_handlers combines several handlers into one that runs them concurrently in a task group. It is public API but nothing in the installed package calls it \u2014 verified by grepping the whole reactpy package, which finds only its own definition \u2014 so you will rarely have a reason to call it. Its failure mode is still worth knowing, because it is a ValueError at build time:

merge_event_handlers([a, b])\n# ValueError: Cannot merge handlers - 'stop_propagation', 'prevent_default',\n# 'debounce', 'throttle', 'target', or 'stop_immediate_propagation' mismatch.\n

Handlers merge only if every option matches. An empty list raises ValueError: No event handlers to merge.

"},{"location":"learn/creating-vdom-event-handlers/#what-to-check-when-a-control-does-nothing","title":"What to check when a control does nothing","text":"

Every failure mode on this page is silent in the browser, so work through them in this order:

  1. Is it declared async def? A def, a lambda, or a state setter raises while the component renders, and the traceback names the function.
  2. Did you pass the function or call it? A handler is a coroutine, so {\"onClick\": handler()} hands over a coroutine object \u2014 not callable, so it falls through to the attributes and dies when the model is encoded: TypeError: Type is not JSON serializable: coroutine from the send path. A handler which returns something instead (None from a helper) ships as an inert attribute and never fires. Either way: pass the function, do not call it.
  3. Is the key spelled the way the DOM spells its event, with a leading on and a capital letter? ReactPy passes the key straight through, so a on_click registers a listener for an event named _click, which nothing ever dispatches. Copy the DOM's camelCase spelling (onClick, onDoubleClick, onKeyDown) rather than guessing.
  4. Does the handler take exactly one parameter? Two required parameters means a server-side TypeError and a dead button.
  5. Is it a bound method? Then preventDefault/stopPropagation/debounce are not detected \u2014 use @event.
  6. Read the server log. Failed to execute event handler is where swallowed handler errors go; the browser console will show nothing.
"},{"location":"learn/creating-vdom-event-handlers/#summary","title":"Summary","text":"
  • Values are sorted by kind, not name: callable \u2192 eventHandlers (and raises there unless it can be awaited), string under onXxx \u2192 inlineJavaScript, everything else \u2192 attributes.
  • None, True, and dict handlers all fail silently as attributes.
  • A handler is a coroutine \u2014 a def or a lambda is refused with a TypeError when the element is built, not when it is clicked.
  • A plain coroutine receives the argument list unpacked (so, the event proxy); an EventHandler receives the list.
  • Set options with @event(prevent_default=..., stop_propagation=..., stop_immediate_propagation=..., debounce=..., throttle=...); the bytecode detector is a convenience with real limits, notably bound methods.
  • Both debounce and throttle rate-limit client\u2192server traffic: debounce coalesces outgoing events on a trailing edge and only on user-input elements, throttle fires on a leading edge on any element. The server's echo of a new value is reconciled by _reactpy_ack_seq, not by debounce. Invalid values are dropped with a console warning, not an error.
  • Handler errors surface in the server log only.
"},{"location":"learn/creating-vdom-event-handlers/#related","title":"Related","text":"
  • Responding to Events \u2014 the tutorial treatment, propagation, and the event object
  • Common Props \u2014 the same surface from the reference side, including ReactPy's own options
  • Protocol Structure \u2014 the wire format, target, and how data is built
  • Creating HTML Tags \u2014 how the model these handlers live in is built
  • Manipulating the DOM with Refs \u2014 the inline-JavaScript escape hatch
"},{"location":"learn/editor-setup/","title":"Editor Setup","text":""},{"location":"learn/editor-setup/#overview","title":"Overview","text":"

A properly configured editor can make code clearer to read and faster to write. It can even help you catch bugs as you write them! If this is your first time setting up an editor or you're looking to tune up your current editor, we have a few recommendations.

You will learn

  • What the most popular editors are
  • How to format your code automatically
"},{"location":"learn/editor-setup/#your-editor","title":"Your editor","text":"

VS Code is one of the most popular editors in use today. It has a large marketplace of extensions and integrates well with popular services like GitHub. Most of the features listed below can be added to VS Code as extensions as well, making it highly configurable!

Other popular text editors used in the React community include:

  • Sublime Text has support for syntax highlighting and autocomplete built in.
  • Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient. It is included as \"vi\" with most UNIX systems and with Apple OS X.
"},{"location":"learn/editor-setup/#recommended-text-editor-features","title":"Recommended text editor features","text":"

Some editors come with these features built in, but others might require adding an extension. Check to see what support your editor of choice provides to be sure!

"},{"location":"learn/editor-setup/#linting","title":"Linting","text":"

Linting is the process of running a program that will analyse code for potential errors. Ruff is Reactive Python's linter of choice due to its speed and configurability. Additionally, we recommend using Pyright as a performant type checker for Python.

Be sure you have Python installed before you proceed!

  • Install Ruff using the VSCode extension or the standalone package.
  • Install Pyright using the VSCode extension or the standalone package.
"},{"location":"learn/editor-setup/#formatting","title":"Formatting","text":"

The last thing you want to do when sharing your code with another contributor is get into an discussion about tabs vs spaces! Fortunately, Ruff will clean up your code by reformatting it to conform to preset, configurable rules. Run Ruff, and all your tabs will be converted to spaces\u2014and your indentation, quotes, etc will also all be changed to conform to the configuration. In the ideal setup, Ruff will run when you save your file, quickly making these edits for you.

You can install the Ruff extension in VSCode by following these steps:

  1. Launch VS Code
  2. Use Quick Open, press Ctrl+P
  3. Paste in ext install charliermarsh.ruff
  4. Press Enter

Formatting on save

Ideally, you should format your code on every save. VS Code has settings for this!

  1. In VS Code, press Ctrl+Shift+P
  2. Type \"workspace settings\"
  3. Hit Enter
  4. In the search bar, type \"Format on save\"
  5. Be sure the \"Format on save\" option is ticked!
"},{"location":"learn/extracting-state-logic-into-a-reducer/","title":"Extracting State Logic into a Reducer","text":""},{"location":"learn/extracting-state-logic-into-a-reducer/#overview","title":"Overview","text":"

A component whose state is updated by many handlers spread across its body gets hard to reason about. A reducer moves every update rule into one pure function, so the handlers say only what the user did. In ReactPy there is one rule to learn on top of React's: the reducer you pass on the first render is the one ReactPy keeps, so a reducer defined inside the component body closes over frozen values.

You will learn

  • What a reducer is, and how dispatching differs from setting
  • Why ReactPy keeps the first reducer, and what that forbids
  • Where a ReactPy reducer actually runs, and what that means for purity
  • When a reducer is worth the extra code
"},{"location":"learn/extracting-state-logic-into-a-reducer/#consolidate-state-logic-with-a-reducer","title":"Consolidate state logic with a reducer","text":"

This component holds a list of tasks and updates it from three handlers:

app.py Run
INITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n    {\"id\": 2, \"text\": \"Camera\", \"done\": False},\n]\n\n\n@component\ndef task_app():\n    next_id, set_next_id = use_state(3)\n    tasks, set_tasks = use_state(INITIAL_TASKS)\n\n    async def handle_add_task(event):\n        set_tasks([*tasks, {\"id\": next_id, \"text\": \"New task\", \"done\": False}])\n        set_next_id(next_id + 1)\n\n    def handle_toggle(task_id):\n        async def handle_click(event):\n            set_tasks(\n                [\n                    (\n                        {**task, \"done\": not task[\"done\"]}\n                        if task[\"id\"] == task_id\n                        else task\n                    )\n                    for task in tasks\n                ]\n            )\n\n        return handle_click\n\n    def handle_delete(task_id):\n        async def handle_click(event):\n            set_tasks([task for task in tasks if task[\"id\"] != task_id])\n\n        return handle_click\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add_task}, \"Add task\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": task[\"id\"]},\n                    html.span(\n                        {\n                            \"style\": {\n                                \"textDecoration\": (\n                                    \"line-through\" if task[\"done\"] else \"none\"\n                                )\n                            }\n                        },\n                        task[\"text\"],\n                    ),\n                    html.button({\"onClick\": handle_toggle(task[\"id\"])}, \"Toggle\"),\n                    html.button({\"onClick\": handle_delete(task[\"id\"])}, \"Delete\"),\n                )\n                for task in tasks\n            ]\n        ),\n        html.p({\"id\": \"summary\"}, f\"{len(tasks)} tasks, next id {next_id}\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Each handler knows how to rebuild the list. That is fine at three handlers and one field. It stops being fine when a fourth handler appears, when a second field has to change alongside tasks, or when you are debugging which of the four produced a wrong list \u2014 and here handle_add_task already has to keep two state variables in step.

Managing state with a reducer inverts it. Instead of telling ReactPy what to do, each handler says what just happened, and one function decides what that means:

app.py Run
INITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n    {\"id\": 2, \"text\": \"Camera\", \"done\": False},\n]\n\n\ndef tasks_reducer(state, action):\n    \"\"\"One place that knows how the task list may change.\"\"\"\n    kind = action[\"type\"]\n    if kind == \"added\":\n        task = {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        return {\n            **state,\n            \"tasks\": [*state[\"tasks\"], task],\n            \"next_id\": state[\"next_id\"] + 1,\n        }\n    if kind == \"toggled\":\n        return {\n            **state,\n            \"tasks\": [\n                (\n                    {**task, \"done\": not task[\"done\"]}\n                    if task[\"id\"] == action[\"id\"]\n                    else task\n                )\n                for task in state[\"tasks\"]\n            ],\n        }\n    if kind == \"deleted\":\n        return {\n            **state,\n            \"tasks\": [task for task in state[\"tasks\"] if task[\"id\"] != action[\"id\"]],\n        }\n    raise ValueError(f\"unknown action type {kind!r}\")\n\n\n@component\ndef task_app():\n    state, dispatch = use_reducer(tasks_reducer, {\"tasks\": INITIAL_TASKS, \"next_id\": 3})\n\n    async def handle_add_task(event):\n        dispatch({\"type\": \"added\"})\n\n    def handle_toggle(task_id):\n        async def handle_click(event):\n            dispatch({\"type\": \"toggled\", \"id\": task_id})\n\n        return handle_click\n\n    def handle_delete(task_id):\n        async def handle_click(event):\n            dispatch({\"type\": \"deleted\", \"id\": task_id})\n\n        return handle_click\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add_task}, \"Add task\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": task[\"id\"]},\n                    html.span(\n                        {\n                            \"style\": {\n                                \"textDecoration\": (\n                                    \"line-through\" if task[\"done\"] else \"none\"\n                                )\n                            }\n                        },\n                        task[\"text\"],\n                    ),\n                    html.button({\"onClick\": handle_toggle(task[\"id\"])}, \"Toggle\"),\n                    html.button({\"onClick\": handle_delete(task[\"id\"])}, \"Delete\"),\n                )\n                for task in state[\"tasks\"]\n            ]\n        ),\n        html.p(\n            {\"id\": \"summary\"},\n            f\"{len(state['tasks'])} tasks, next id {state['next_id']}\",\n        ),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Both examples are verified to behave identically \u2014 add twice, toggle, delete produces the same readouts and the same single flipped row in each. The difference is where the knowledge lives. The handlers now read as a list of user intentions:

  • dispatch({\"type\": \"added\"})
  • dispatch({\"type\": \"toggled\", \"id\": task_id})
  • dispatch({\"type\": \"deleted\", \"id\": task_id})

next_id moved into the same state object as tasks, so added bumps both in one return value. That is the first concrete gain: there is no longer a pair of setters that can be called in the wrong combination.

The reducer ends with raise ValueError(f\"unknown action type {kind!r}\") rather than return state. A fallthrough that silently does nothing is the hardest bug in this pattern to find, because every other symptom looks correct.

Note

use_reducer(reducer, initial_value) takes exactly two arguments. There is no third \"init function\" parameter as in some React versions. If building the initial state is expensive, pass a zero-argument callable as initial_value \u2014 ReactPy calls it once and stores the result, the same way use_state does.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#the-rule-reactpy-adds-the-first-reducer-wins","title":"The rule ReactPy adds: the first reducer wins","text":"

This is the part that differs from React, and it will bite you once. use_reducer builds its dispatcher once and reuses it, so the reducer function from the first render is the one that runs forever \u2014 core/hooks.py, use_reducer.

Swapping the reducer at runtime does nothing at all:

app.py Run
def add_by_one(state, action):\n    return {\"n\": state[\"n\"] + 1, \"by\": \"add_by_one\"}\n\n\ndef add_by_hundred(state, action):\n    return {\"n\": state[\"n\"] + 100, \"by\": \"add_by_hundred\"}\n\n\n@component\ndef counter():\n    version, set_version = use_state(1)\n\n    # The component body picks a different reducer each time version changes,\n    # but use_reducer built its dispatcher on the first render and reuses it.\n    reducer = add_by_one if version == 1 else add_by_hundred\n    state, dispatch = use_reducer(reducer, {\"n\": 0, \"by\": \"-\"})\n\n    async def handle_add(event):\n        dispatch({\"type\": \"add\"})\n\n    async def handle_switch(event):\n        set_version(2)\n\n    return html.div(\n        html.button({\"onClick\": handle_add}, \"Add\"),\n        html.button({\"onClick\": handle_switch}, \"Switch reducer\"),\n        html.p({\"id\": \"readout\"}, f\"n={state['n']} by={state['by']} version={version}\"),\n    )\n\n\n@component\ndef app():\n    return counter()\n
# TODO\n

Press Switch reducer \u2014 the readout shows version=2, so the component body really did pick add_by_hundred \u2014 then press Add: by=add_by_one. Every later action still runs the original function.

The same mechanism is subtler, and much more common, when the reducer is defined inside the component:

app.py Run
INITIAL = {\"count\": 0, \"total\": 0}\n\n\n@component\ndef cart(item_price):\n    # This reducer reads item_price from the enclosing scope. ReactPy keeps the\n    # reducer from the first render, so it keeps the first price too.\n    def reducer(state, action):\n        if action[\"type\"] == \"add\":\n            return {\n                \"count\": state[\"count\"] + 1,\n                \"total\": state[\"total\"] + item_price,\n            }\n        return state\n\n    state, dispatch = use_reducer(reducer, INITIAL)\n\n    async def handle_add(event):\n        dispatch({\"type\": \"add\"})\n\n    return html.div(\n        html.button({\"onClick\": handle_add}, \"Add\"),\n        html.p(\n            {\"id\": \"cart\"},\n            f\"count={state['count']} total={state['total']} price={item_price}\",\n        ),\n    )\n\n\n@component\ndef price_control():\n    price, set_price = use_state(10)\n\n    async def raise_price(event):\n        set_price(50)\n\n    return html.div(\n        html.button({\"onClick\": raise_price}, \"Raise price\"),\n        cart(price),\n        html.p(f\"parent price={price}\"),\n    )\n\n\n@component\ndef app():\n    return price_control()\n
# TODO\n

Press Add once: count=1 total=10. Then Raise price, which takes the displayed price to 50 \u2014 and press Add again. You get total=20, not 60. The line reads price=50 while the arithmetic used 10.

Nothing crashed and nothing was logged. The reducer still holds the closure from the first render, in which item_price was 10. This is the frozen-effect-body problem from Lifecycle of Reactive Effects wearing a different hat, and it is the single most likely way to be quietly wrong with a ReactPy reducer.

The fix is the same instinct as always: keep the reducer pure and self-contained. Define it at module level, and let the action carry every value it needs. Handlers are not frozen \u2014 they are rebuilt each render \u2014 so a handler can read the current prop and hand it over:

app.py Run
INITIAL = {\"count\": 0, \"total\": 0}\n\n\ndef cart_reducer(state, action):\n    \"\"\"Pure and self-contained: every value it needs arrives in the action.\"\"\"\n    if action[\"type\"] == \"add\":\n        return {\n            \"count\": state[\"count\"] + 1,\n            \"total\": state[\"total\"] + action[\"price\"],\n        }\n    return state\n\n\n@component\ndef cart(item_price):\n    # Defined at module level, so there is no enclosing scope to freeze.\n    state, dispatch = use_reducer(cart_reducer, INITIAL)\n\n    async def handle_add(event):\n        # The handler closes over the *current* price, and hands it over.\n        dispatch({\"type\": \"add\", \"price\": item_price})\n\n    return html.div(\n        html.button({\"onClick\": handle_add}, \"Add\"),\n        html.p(\n            {\"id\": \"cart\"},\n            f\"count={state['count']} total={state['total']} price={item_price}\",\n        ),\n    )\n\n\n@component\ndef price_control():\n    price, set_price = use_state(10)\n\n    async def raise_price(event):\n        set_price(50)\n\n    return html.div(\n        html.button({\"onClick\": raise_price}, \"Raise price\"),\n        cart(price),\n        html.p(f\"parent price={price}\"),\n    )\n\n\n@component\ndef app():\n    return price_control()\n
# TODO\n

Same buttons: Add, Raise price, Add now gives count=2 total=60 price=50. cart_reducer sits at module level with no enclosing scope to freeze, and dispatch({\"type\": \"add\", \"price\": item_price}) supplies the live value.

So the ReactPy rule is: a reducer must be a module-level function that reads nothing but its two arguments. That is stricter than React's \"reducers must be pure\", and it costs nothing \u2014 a reducer that closes over component scope is a reducer you cannot unit-test with two plain arguments anyway.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#where-a-reactpy-reducer-runs","title":"Where a ReactPy reducer runs","text":"

React's docs say reducers run during rendering, because React queues actions and calls the reducer at the next render. ReactPy is more direct, and the difference is worth knowing when you reach for a print statement:

handler:enter\nreducer(add)\nhandler:exit\nrender\n

Verified trace of one click. dispatch calls your reducer immediately, inside the handler, then compares the result against the current state with == and schedules a render only if it differs \u2014 core/hooks.py, _create_dispatcher.

Three consequences:

  • A reducer that raises surfaces as a failed event handler, not a reducer error. Verified: dispatching an action whose branch raises ValueError leaves the state untouched and produces zero renders. dispatch ran the reducer inside the handler, so the exception propagates to core/layout.py, Layout._process_event_queue, which catches it and calls logger.exception \u2014 one ERROR: Failed to execute event handler record on the reactpy.core.layout logger, with the reducer's traceback attached. ReactPy itself never reports \"a reducer raised\". The component stays mounted and keeps working for later actions.
  • Multiple dispatches in one handler batch into one render. Verified: three dispatch calls in a single handler produce three reducer calls, applied in order, and one render.
  • Purity is not optional, but for a different reason than in React. Since the result is compared with ==, mutating and returning the same object compares equal and schedules nothing.

That last one is the most common reducer bug in ReactPy, and it is silent:

app.py Run
INITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n]\n\n\ndef mutating_reducer(state, action):\n    \"\"\"Looks fine, edits the list in place, returns it. Nothing will re-render.\"\"\"\n    if action[\"type\"] == \"added\":\n        state[\"tasks\"].append(\n            {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        )\n        return state\n    return state\n\n\n@component\ndef task_app():\n    # A fresh list per component instance: this reducer mutates the list it is\n    # given, so it must not be handed a module-level one.\n    state, dispatch = use_reducer(\n        mutating_reducer, {\"tasks\": [*INITIAL_TASKS], \"next_id\": 2}\n    )\n\n    async def handle_add_task(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.button({\"onClick\": handle_add_task}, \"Add task\"),\n        html.ul(\n            [html.li({\"key\": task[\"id\"]}, task[\"text\"]) for task in state[\"tasks\"]]\n        ),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks rendered\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Press Add task three times. The rendered count stays at 2 tasks rendered and the component body never runs again \u2014 verified at zero renders. The list did grow; the returned state compared equal to the old one, so ReactPy decided nothing had changed. Return new objects ({**state, \"tasks\": [*state[\"tasks\"], task]}), exactly as you would with use_state.

Pitfall

return state after modifying state is a no-op, and so is any branch that falls through to return state. If an action's \"type\" matches no branch \u2014 a typo, a renamed constant \u2014 the dispatch is silently ignored. End the reducer with raise ValueError(...) for unknown types instead of a bare return state, so the mistake surfaces where it is made.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#comparing-use_state-and-use_reducer","title":"Comparing use_state and use_reducer","text":"
  • Code size. use_state needs less code upfront: a reducer and actions is two artefacts, not one. A reducer pays off when many handlers mutate state in similar shapes, or when one transition touches several fields at once \u2014 moving next_id next to tasks above is that case.
  • Readability. With use_state the update logic lives in the handlers, which is where you want it while it is three lines. When handlers grow conditionals, the reducer lets you separate how state changes from what happened.
  • Debugging. With use_state you must find which setter was wrong. With a reducer you can log the two arguments of one function and reconstruct the session \u2014 in ReactPy the log also tells you the order, because dispatch is eager. If each action is right, the bug is in the reducer.
  • Testing. A module-level reducer \u2014 which ReactPy already requires \u2014 is a plain function. tasks_reducer(state, {\"type\": \"deleted\", \"id\": 1}) is an assertion, no rendering involved.
  • Render count. Neither is cheaper. Both compare with == and both skip a render when the value is unchanged; neither bails out of rendering the component, because ReactPy has no memo.

Use a reducer when a component has produced real bugs from the wrong update path, not as a default. Mixing is fine: use_state for a draft input, use_reducer for the form's submitted record, in the same component.

Deep Dive

ReactPy has no immer equivalent that earns its keep here, because {**state, ...} and [*items, item] are already the copy idioms a reducer needs. What is worth doing once a reducer grows past a couple of branches: make the state a frozen @dataclass. Then a mutation raises FrozenInstanceError at the line that did it instead of quietly comparing equal \u2014 verified, and with it the two halves that make this safe: strictly_equal compares dataclass instances field by field, so dispatching an action that changes nothing renders nothing, while one that changes a field renders once. Because the raise happens inside the event handler, though, it is caught like any other handler exception: it appears as a root-logger ERROR, not on screen. Use dataclasses.replace(state, ...), and read the server log. See Lifecycle of Reactive Effects for the full comparison table.

"},{"location":"learn/extracting-state-logic-into-a-reducer/#writing-reducers-well","title":"Writing reducers well","text":"
  • One action per user interaction, not per field. A \"Reset\" button on a five-field form should dispatch {\"type\": \"reset_form\"}, not five set_field actions. If you logged every action, the log should read like a transcript of the session.
  • Actions describe what happened, not what to change. {\"type\": \"toggled\", \"id\": 3} survives a change in how toggling is displayed. {\"type\": \"set_done\", \"value\": True} does not, and is usually shorter written as a setter.
  • Put the data in the action. {\"type\": \"add_item\", \"item\": item, \"price\": price} \u2014 this is also exactly what keeps a ReactPy reducer honest, since the reducer cannot read live values from a closure.
  • Keep transitions in the reducer, not in the handler. A handler that does dispatch(...) and set_something_else(...) has two sources of truth again.
  • Do not dispatch during render. Dispatch is eager, so it updates the pending state while the component body is still running; the render then reflects an update that has already happened. Put it in an effect or a handler.
"},{"location":"learn/extracting-state-logic-into-a-reducer/#recap","title":"Recap","text":"
  • A reducer collects every state transition in one pure function; handlers shrink to a description of what the user did.
  • ReactPy keeps the first reducer it is given. Verified: swapping the reducer argument changes the body, not the behaviour.
  • Therefore define reducers at module level, reading nothing but state and action. A reducer defined in the component body freezes its closure \u2014 verified charging total=20 at a displayed price=50.
  • dispatch runs the reducer immediately, inside the handler, not at the next render. Verified by trace.
  • Mutating the state and returning it compares equal, so nothing renders \u2014 verified: three presses, zero renders, no log line. Return new objects.
  • A raising reducer surfaces as ERROR: Failed to execute event handler from reactpy.core.layout, not as a reducer error; end the reducer with raise ValueError so unknown actions surface.
  • Several dispatches in one handler apply in order and cost one render (verified).
  • use_reducer takes two arguments; pass a callable as initial_value to build it lazily, once.
"},{"location":"learn/extracting-state-logic-into-a-reducer/#related","title":"Related","text":"
  • Choosing the State Structure \u2014 the == comparison and the copy idioms a reducer depends on
  • Queueing a Series of State Updates \u2014 functional updaters, the use_state sibling of dispatch
  • Lifecycle of Reactive Effects \u2014 frozen closures and strictly_equal, the two mechanisms behind the traps on this page
  • Passing Data Deeply with Context \u2014 how to hand dispatch to distant components
  • Use Reducer \u2014 parameter-level reference and troubleshooting
"},{"location":"learn/importing-and-exporting-components/","title":"Importing and Exporting Components","text":""},{"location":"learn/importing-and-exporting-components/#overview","title":"Overview","text":"

The magic of components lies in their reusability: you can create components that are composed of other components. But as you nest more and more components, it often makes sense to start splitting them into different files. This lets you keep your files easy to scan and reuse components in more places.

You will learn

  • What a root component module is
  • How to import a component from another file
  • How ReactPy expresses \"private\" and \"public\" components
  • How to import and export multiple components from one file
  • How to split components into multiple files
"},{"location":"learn/importing-and-exporting-components/#the-root-component-module","title":"The root component module","text":"

In Your First Component, you made a profile component and a gallery component that renders it:

app.pystyles.css
from reactpy import component, html\n\n\n@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n
img {\n    margin: 0 10px 10px 0;\n    height: 90px;\n}\n

These currently live in a single module. Whichever module holds the component you hand to an executor is your root component module \u2014 the ASGI executor in Creating a Standalone React App takes that component directly:

from reactpy.executors.asgi import ReactPy\n\nfrom app import gallery\n\nmy_app = ReactPy(gallery)\n

Which module plays this role depends on your executor. The middleware executor takes dotted paths instead, e.g. [\"my_components.hello_world\"], and the CSR executor takes a path to a file whose root component it mounts.

"},{"location":"learn/importing-and-exporting-components/#exporting-and-importing-a-component","title":"Exporting and importing a component","text":"

What if you want to change the landing screen in the future and put a list of science books there? Or place all the profiles somewhere else? It makes sense to move gallery and profile into their own module. This makes them more modular and reusable in other files. You can move a component in three steps:

  1. Make a new Python file to put the components in.
  2. Define your component functions in that file. Unlike JavaScript, Python has no export keyword \u2014 every name defined at module level is importable.
  3. Import it in the module where you'll use the component.

Here both profile and gallery have moved into a new file called gallery.py. Now app.py imports gallery from it:

app.pygallery.py
from gallery import gallery\n\nfrom reactpy import component\n\n\n@component\ndef app():\n    return gallery()\n
from reactpy import component, html\n\n__all__ = [\"gallery\"]\n\n\n@component\ndef _profile():\n    \"\"\"Used only inside this module, so it stays out of ``__all__``.\"\"\"\n    return html.img({\"src\": \"https://i.imgur.com/QIrZWGIs.jpg\", \"alt\": \"Alan L. Hart\"})\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        _profile(),\n        _profile(),\n        _profile(),\n    )\n

Notice how this example is broken down into two component files now:

  1. gallery.py:
    • Defines the _profile component, which is only used within the same file and is kept private.
    • Makes gallery the public component of the module.
  2. app.py:
    • Imports gallery from gallery.py.
    • Defines the root app component.

Note

ReactPy does not care which module a component is defined in, so unlike ReactJS there is no distinction between \"default\" and \"named\" exports. from gallery import gallery, profile and two separate import statements mean the same thing.

"},{"location":"learn/importing-and-exporting-components/#public-and-private-components","title":"Public and private components","text":"

ReactJS lets you keep a component unexported so that only its own file can use it. Python has no equivalent enforcement, but it has two widely respected conventions that express the same intent.

A leading underscore marks a name as private to its module:

@component\ndef _profile():\n    ...\n

And __all__ lists the names a module publishes. It is what a star import exports, and what linters and type checkers read to decide what is public API:

__all__ = [\"gallery\"]\n

This is why _profile is both underscore-prefixed and absent from __all__ in the example above.

Pitfall

These conventions are advisory, not enforced. from gallery import _profile still works, because Python has no private keyword. Do not reach for underscore-prefixed names from another module, and do not rely on them being unavailable \u2014 treat a broken convention as a bug in the importing code, not as protection.

Deep Dive

"},{"location":"learn/importing-and-exporting-components/#naming-modules-and-components","title":"Naming modules and components","text":"

Because imports name modules rather than files, give both meaningful names. scientists.py defining gallery and _profile reads better than utils.py defining Gallery, and it also keeps you consistent with the rest of the docs, which write components in snake_case.

Components should still be called everywhere they are used, so a component used by two modules must be imported in both of them. There is no global component registry to fall back on.

"},{"location":"learn/importing-and-exporting-components/#exporting-and-importing-multiple-components-from-the-same-file","title":"Exporting and importing multiple components from the same file","text":"

What if you want to show just one profile instead of a gallery? Then app.py needs profile too. Add it to the module's __all__ and import it by name:

app.pygallery.py
from gallery import gallery, profile\n\nfrom reactpy import component, html\n\n\n@component\ndef app():\n    return html.div(profile(), gallery())\n
from reactpy import component, html\n\n__all__ = [\"gallery\", \"profile\"]\n\n\n@component\ndef profile():\n    return html.img({\"src\": \"https://i.imgur.com/QIrZWGIs.jpg\", \"alt\": \"Alan L. Hart\"})\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n

Now gallery.py publishes both components:

  • gallery.py:
    • Lists gallery and profile in __all__.
  • app.py:
    • Imports both gallery and profile from gallery.py.
    • Renders each of them.
"},{"location":"learn/importing-and-exporting-components/#recap","title":"Recap","text":"

You've learned:

  • What a root component module is, and that it depends on your executor
  • How to import a component from another file
  • How ReactPy expresses public and private components with __all__ and a leading underscore
  • How to export multiple components from the same file
"},{"location":"learn/importing-and-exporting-components/#next-steps","title":"Next Steps","text":"

Now that your components can live in separate files, continue to Passing Props to a Component to make them configurable.

"},{"location":"learn/installation/","title":"Installation","text":""},{"location":"learn/installation/#overview","title":"Overview","text":"

React has been designed from the start for gradual adoption. You can use as little or as much React as you need. Whether you want to get a taste of React, add some interactivity to an HTML page, or start a complex React-powered app, this section will help you get started.

You will learn

  • How to start a new React project
  • How to add React to an existing project
  • How to set up your editor
  • How to install React Developer Tools
"},{"location":"learn/installation/#start-a-new-react-project","title":"Start a new React project","text":"

If you want to build an app or a website fully with React, start a new React project.

"},{"location":"learn/installation/#add-react-to-an-existing-project","title":"Add React to an existing project","text":"

If want to try using React in your existing app or a website, add React to an existing project.

"},{"location":"learn/installation/#next-steps","title":"Next steps","text":"

Head to the Quick Start guide for a tour of the most important React concepts you will encounter every day.

"},{"location":"learn/keeping-components-pure/","title":"Keeping Components Pure","text":""},{"location":"learn/keeping-components-pure/#overview","title":"Overview","text":"

Some Python functions are pure. Pure functions only perform a calculation and nothing more. By strictly only writing your components as pure functions, you can avoid an entire class of baffling bugs and unpredictable behavior as your codebase grows. To get these benefits, though, there are a few rules you must follow.

You will learn

  • What purity is and how it helps you avoid bugs
  • How to keep components pure by keeping changes out of the render phase
  • Why ReactPy cannot check purity for you
"},{"location":"learn/keeping-components-pure/#purity-components-as-formulas","title":"Purity: Components as formulas","text":"

In computer science (and especially the world of functional programming), a pure function is a function with the following characteristics:

  • It minds its own business. It does not change any objects or variables that existed before it was called.
  • Same inputs, same output. Given the same inputs, a pure function should always return the same result.

You might already be familiar with one example of pure functions: formulas in math.

Consider this math formula: y = 2 * x.

If x = 2 then y = 4. Always.

If x = 3 then y = 6. Always.

If x = 3, y won't sometimes be 9 or -1 or 2.5 depending on the time of day or the state of the stock market.

If y = 2 * x and x = 3, y will always be 6.

If we made this into a Python function, it would look like this:

def double(number):\n    return 2 * number\n

In the above example, double is a pure function. If you pass it 3, it will return 6. Always.

ReactPy is designed around this concept. ReactPy assumes that every component you write is a pure function. This means that ReactPy components you write must always return the same VDOM given the same inputs:

app.py Run
@component\ndef recipe(drinkers):\n    return html.ol(\n        html.li(f\"Boil {drinkers} cups of water.\"),\n        html.li(f\"Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.\"),\n        html.li(f\"Add {0.5 * drinkers} cups of milk to boil and sugar to taste.\"),\n    )\n\n\n@component\ndef app():\n    return html.section(\n        html.h1(\"Spiced Chai Recipe\"),\n        html.h2(\"For two\"),\n        recipe(2),\n        html.h2(\"For a gathering\"),\n        recipe(4),\n    )\n
# TODO\n

When you pass drinkers=2 to recipe(), it will return VDOM containing 2 cups of water. Always.

If you pass drinkers=4, it will return VDOM containing 4 cups of water. Always.

Just like a math formula.

You could think of your components as recipes: if you follow them and don't introduce new ingredients during the cooking process, you will get the same dish every time. That \"dish\" is the VDOM that the component serves to ReactPy to render.

"},{"location":"learn/keeping-components-pure/#side-effects-unintended-consequences","title":"Side Effects: (un)intended consequences","text":"

ReactPy's rendering process must always be pure. Components should only return their VDOM, and not change any objects or variables that existed before rendering\u2014that would make them impure!

Here is a component that breaks this rule:

app.py Run
guest = 0\n\n\n@component\ndef cup():\n    # Bad: changing a preexisting variable!\n    global guest\n    guest = guest + 1\n    return html.h2(f\"Tea cup for guest #{guest}\")\n\n\n@component\ndef tea_set():\n    return html(cup(), cup(), cup())\n
# TODO\n

This component is reading and writing a guest variable declared outside of it. This means that calling this component multiple times will produce different VDOM! And what's more, if other components read guest, they will produce different VDOM, too, depending on when they were rendered! That's not predictable.

Render this once and you get the numbering you expect:

Tea cup for guest #1\nTea cup for guest #2\nTea cup for guest #3\n

Render it again \u2014 after a hot reload, a reconnect, or simply a second request in the same process \u2014 and the counters have silently moved on:

Tea cup for guest #4\nTea cup for guest #5\nTea cup for guest #6\n

Going back to our formula y = 2 * x, now even if x = 2, we cannot trust that y = 4. Our tests could fail, our users would be baffled, planes would fall out of the sky\u2014you can see how this would lead to confusing bugs!

You can fix this component by passing guest as a prop instead:

app.py Run
@component\ndef cup(guest):\n    return html.h2(f\"Tea cup for guest #{guest}\")\n\n\n@component\ndef tea_set():\n    return html(cup(1), cup(2), cup(3))\n
# TODO\n

Now your component is pure, as the VDOM it returns only depends on the guest prop.

In general, you should not expect your components to be rendered in any particular order. It doesn't matter if you call y = 2 * x before or after y = 5 * x: both formulas will resolve independently of each other. In the same way, each component should only \"think for itself\", and not attempt to coordinate with or depend upon others during rendering. Rendering is like a school exam: each component should calculate VDOM on their own!

ReactPy has no Strict Mode to catch this for you

React ships a \"Strict Mode\" that calls every component function twice in development, which is how the example above gets caught: an impure counter jumps to #2, #4, #6. ReactPy has no equivalent \u2014 there is no StrictMode component and no double-invoke. A single render of an impure component looks completely correct, and the bug only appears later as drifting output or as a failure under a test that renders twice.

Two practical consequences:

  • Add a test that renders the component twice and asserts the two results are equal. That is the closest ReactPy gets to Strict Mode, and it is entirely in your hands.
  • Because there is no double-invoke to hide behind, impurity in ReactPy tends to surface as state that changes between reconnects rather than as an obvious warning. Treat any component that touches module-level state as suspect.
"},{"location":"learn/keeping-components-pure/#local-mutation-your-components-little-secret","title":"Local mutation: Your component's little secret","text":"

In the above example, the problem was that the component changed a preexisting variable while rendering. This is often called a \"mutation\" to make it sound a bit scarier. Pure functions don't mutate variables outside of the function's scope or objects that were created before the call\u2014that makes them impure!

However, it's completely fine to change variables and objects that you've just created while rendering. In this example, you create a [] list, assign it to a cups variable, and then append a dozen cups into it:

app.py Run
@component\ndef cup(guest):\n    return html.h2(f\"Tea cup for guest #{guest}\")\n\n\n@component\ndef tea_gathering():\n    cups = []\n    for index in range(1, 13):\n        cups.append(cup(index, key=index))\n    return html.div(cups)\n
# TODO\n

If the cups variable or the [] list were created outside the tea_gathering() function, this would be a huge problem! You would be changing a preexisting object by appending items to that list.

However, it's fine because you've created them during the same render, inside tea_gathering(). No code outside of tea_gathering() will ever know that this happened. This is called \"local mutation\"\u2014it's like your component's little secret.

"},{"location":"learn/keeping-components-pure/#where-you-can-cause-side-effects","title":"Where you can cause side effects","text":"

While functional programming relies heavily on purity, at some point, somewhere, something has to change. That's kind of the point of programming! These changes\u2014updating the screen, starting an animation, changing the data\u2014are called side effects. They're things that happen \"on the side\", not during rendering.

In ReactPy, side effects usually belong inside event handlers. Event handlers are functions that ReactPy runs when you perform some action\u2014for example, when you click a button. Even though event handlers are defined inside your component, they don't run during rendering! So event handlers don't need to be pure.

app.py Run
@component\ndef tea_counter():\n    sips, set_sips = use_state(0)\n\n    async def take_a_sip(event):\n        # Side effects belong here: this runs on click, not during rendering.\n        set_sips(sips + 1)\n\n    return html.div(\n        html.h1(f\"You have taken {sips} sips.\"),\n        html.button({\"onClick\": take_a_sip}, \"Take a sip\"),\n    )\n
# TODO\n

If you've exhausted all other options and can't find the right event handler for your side effect, you can still attach it to your returned VDOM with a use_effect or use_async_effect call in your component. This tells ReactPy to execute it later, after rendering, when side effects are allowed. However, this approach should be your last resort.

When possible, try to express your logic with rendering alone. You'll be surprised how far this can take you!

Why does ReactPy care about purity?

Writing pure functions takes some habit and discipline. But it also unlocks real opportunities:

  • Your components can run on the server, and be rendered to a string. Components already execute server-side in ReactPy; purity is what makes it safe for one component to serve many requests. The same property lets reactpy_to_string turn a component into HTML \u2014 with one limit worth knowing: it calls component.render() with no Layout behind it, so the component may use no hooks at all. Hand it html.* elements or a hook-free component; anything with use_state raises RuntimeError: No life cycle hook is active. Are you rendering in a layout?. It is a string helper for fragments and tests, not a way to server-render an app. A component that mutated module state would leak one request's output into another's.
  • Rendering can be restarted. ReactPy may need to discard an in-progress render, and purity makes it safe to stop calculating at any time.
  • Reasoning and testing become mechanical. A pure component is an ordinary Python function: call it inside a reactpy.core.layout.Layout, render it twice, and compare the results \u2014 no browser required.

Note that one React benefit is not available to you yet: React skips re-rendering components whose inputs have not changed, which is safe precisely because pure functions are safe to cache. ReactPy has use_memo for values, but no memo for components, so a child re-runs whenever its parent renders. Purity is still required; it simply isn't being used for caching today.

"},{"location":"learn/keeping-components-pure/#recap","title":"Recap","text":"
  • A component must be pure, meaning:

    • It minds its own business. It should not change any objects or variables that existed before rendering.
    • Same inputs, same output. Given the same inputs, a component should always return the same VDOM.
  • Rendering can happen at any time, so components should not depend on each others' rendering sequence.

  • You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, \"set\" state instead of mutating preexisting objects.
  • Strive to express your component's logic in the VDOM you return. When you need to \"change things\", you'll usually want to do it in an event handler. As a last resort, you can use_effect.
  • ReactPy will not double-render your components to find impurity for you; write the render-twice test yourself.
  • Writing pure functions takes a bit of practice, but it unlocks the power of the ReactPy paradigm.
"},{"location":"learn/lifecycle-of-reactive-effects/","title":"Lifecycle of Reactive Effects","text":""},{"location":"learn/lifecycle-of-reactive-effects/#overview","title":"Overview","text":"

An Effect is a piece of synchronization code attached to a render. It runs, it may clean up after itself, and it runs again if its inputs change. Understanding that lifecycle \u2014 not a memorized list of rules \u2014 is what makes Effects predictable in ReactPy.

You will learn

  • How an Effect setup and clean-up pair forms one synchronization
  • Which values an Effect \"reacts\" to, and what happens to the ones it doesn't
  • Why a dependency list, not the Effect's own text, decides re-synchronization
  • How Effects are ordered, and what is not guaranteed
  • Why a broken Effect reports itself nowhere the user can see
"},{"location":"learn/lifecycle-of-reactive-effects/#the-lifecycle-of-an-effect","title":"The lifecycle of an Effect","text":"

Think of an Effect as a synchronization process between a value your component holds and something outside ReactPy. Each Effect has a setup and, sometimes, a clean-up:

app.py Run
@component\ndef room_view(room):\n    def effect():\n        # Setup: ReactPy runs this when the Effect is created.\n        close = server.connect(room)\n\n        # Clean-up: ReactPy runs this before the *next* run of this same\n        # Effect, and again when the component unmounts. Together they make\n        # one synchronization: connect this room, then leave it.\n        def cleanup():\n            close()\n\n        return cleanup\n\n    use_effect(effect, dependencies=[room])\n\n    return html.p(f\"{room} -- open connections: {server.open_count}\")\n\n\n@component\ndef app():\n    room, set_room = use_state(\"lobby\")\n\n    async def handle_room_click(event):\n        set_room(\"dev\" if room == \"lobby\" else \"lobby\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_room_click},\n            f\"Leave {room}\",\n        ),\n        html.p(\"(server log is printed on the server console)\"),\n        room_view(room),\n    )\n
# TODO\n

Verified against the server's own log: mount produces connect lobby; pressing Leave lobby produces close lobby then connect dev; and the last connection closes on unmount. The open-connection count never climbs above 1.

Read the pair as a single unit \u2014 \"be connected to this room\". Setup and clean-up are not two independent callbacks: ReactPy runs the clean-up of a run right before that run's successor sets up, using the same captured state. So switching rooms means leave the old room, join the new one, in that order, and the process is complete no matter how many times the inputs change.

"},{"location":"learn/lifecycle-of-reactive-effects/#why-synchronization-may-need-to-happen-more-than-once","title":"Why synchronization may need to happen more than once","text":"

The value being synchronized can change while the component is on screen: room above, a search query, an auth token. Every such change means the previous synchronization is describing the past, so ReactPy tears it down and starts a new one.

That is why the clean-up is not optional boilerplate. In a browser, forgetting it leaks one subscription until the tab closes. On a ReactPy server it leaks until the process exits \u2014 and the leaked callback still holds your component, still fires, and keeps a connection or file handle alive for a user who left hours ago.

"},{"location":"learn/lifecycle-of-reactive-effects/#how-reactpy-re-synchronizes-an-effect","title":"How ReactPy re-synchronizes an Effect","text":"

When a dependency changes, ReactPy runs this sequence:

  1. The component renders and registers the Effect again.
  2. A new Effect task starts, and its first act is to run the previous run's clean-up \u2014 core/hooks.py, use_effect calls run_effect_cleanup before invoking your function.
  3. Your setup runs and returns the next clean-up.

Two consequences worth internalizing:

  • The clean-up runs on the new task, not the old one. It sees the previous run's captured values, which is exactly what a teardown needs.
  • The old task is not cancelled. A sync Effect's task parks on stop.wait() and is only reaped when the component unmounts. Measured: three dependency changes leave 4 live Effect tasks where there was 1, and all 4 disappear at unmount. The clean-ups still ran in the right order; the parked tasks are simply waiting for a signal that will come at unmount. For a long-lived component re-synchronizing constantly, that is memory you are paying for.
"},{"location":"learn/lifecycle-of-reactive-effects/#thinking-from-the-effects-perspective","title":"Thinking from the Effect's perspective","text":"

When you read an Effect, read it as the outside world would. The code above says: \"while this component is on screen and room is X, stay connected to X.\" Everything in that sentence is checkable:

Sentence part Where it comes from \"while this component is on screen\" the component's mount/unmount \"and room is X\" the dependency list \"stay connected to X\" setup (leaving) the returned clean-up

If part of that sentence is a lie, the Effect is wrong \u2014 not the dependency list, not the linting setup. dependencies is how you spell \"and room is X\"; it is not a performance knob.

"},{"location":"learn/lifecycle-of-reactive-effects/#each-effect-represents-a-separate-synchronization-process","title":"Each Effect represents a separate synchronization process","text":"

Two Effects in one component are two processes, not one with two steps. Keep them apart when their synchronization targets differ, and let each own its own clean-up:

use_effect(connect_to_room, dependencies=[room])\nuse_effect(subscribe_to_feed, dependencies=[user_id])\n

Do not merge them because they live in the same component: merging means a room change tears down the feed subscription too. Two Effects in the same component run in declaration order, so ordering within a component is available to you when one genuinely depends on the other's setup having happened.

"},{"location":"learn/lifecycle-of-reactive-effects/#effects-react-to-reactive-values","title":"Effects \"react\" to reactive values","text":"

Every value in the component body \u2014 props, state, anything computed from them \u2014 belongs to one render. An Effect created by that render reads that render's values and never sees a later one. The dependency list is how you say which values should trigger a new Effect.

Here an Effect reports an error flag but leaves it out of the dependency list:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    show_error, set_show_error = use_state(True)\n    reports, set_reports = use_state([])\n\n    def effect():\n        # Reads `show_error`, but dependencies=[count] never mentions it. So\n        # flipping show_error does not re-synchronize, and this keeps\n        # reporting whatever show_error was when the Effect was created.\n        line = f\"count={count} reported_error={show_error}\"\n        log.append(line)\n        set_reports(lambda previous: [*previous, line])\n\n    use_effect(effect, dependencies=[count])\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_show_error_click(event):\n        set_show_error(not show_error)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button(\n            {\"onClick\": handle_show_error_click},\n            f\"Toggle error (currently {show_error})\",\n        ),\n        html.p(\"Effect reports:\"),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(reports)]),\n    )\n
# TODO\n

Verified: pressing Toggle error re-renders the component and runs the Effect zero times \u2014 the list says dependencies=[count], so nothing happens. Press Add 1 and the Effect runs, and this time it reports the new value of the flag, because a brand-new Effect was created by that render.

That is the precise meaning of \"stale\": the Effect reports values from the render that created it. It does not freeze at mount, and it does not read the latest \u2014 it reads whichever render it was born in. Adding the missing value to the list is the fix.

A local function as a dependency never counts as changed

This one bites hard, and it contradicts the docstrings. ReactPy compares dependencies with core/hooks.py, strictly_equal \u2014 not by id. For two locally defined functions it compares their code: same co_* attributes means same dependency. So a def re-created on every render is never detected as changed:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    label, set_label = use_state(\"count\")\n    runs, set_runs = use_state([])\n\n    def describe():\n        # A fresh function object on every render -- but ReactPy compares two\n        # such local functions by their *code*, not by identity\n        # (core/hooks.py, strictly_equal). So `describe` never counts as\n        # changed, and the Effect below keeps the closure it captured first.\n        return f\"{label}={count}\"\n\n    def effect():\n        line = f\"effect ran, describe() -> {describe()}\"\n        log.append(line)\n        set_runs(lambda previous: [*previous, line])\n\n    use_effect(effect, dependencies=[describe])\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_label_click(event):\n        set_label(label + \"!\")\n\n    return html.div(\n        html.h1(f\"{label}: {count}\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button({\"onClick\": handle_label_click}, \"Rename\"),\n        html.p(\"Effect runs:\"),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(runs)]),\n    )\n
# TODO\n

Verified: press Add 1 twice and Rename once \u2014 the heading shows count!!: 2, yet the Effect has run exactly once and describe() still returns 'count=0'. The Effect is permanently frozen on its first closure, and nothing warns you.

The same rule bites the other way: a fresh [1, 2] or {\"a\": 1} that compares equal is also \"unchanged\", so rebuilding an options dict on every render will not re-trigger an Effect that lists it. Depend on the scalar values you actually use, not on containers or local functions.

"},{"location":"learn/lifecycle-of-reactive-effects/#what-an-effect-with-empty-dependencies-means","title":"What an Effect with empty dependencies means","text":"

dependencies=[] does not mean \"run once, like a componentDidMount\". It means create this Effect and never replace it \u2014 including its body:

def effect():\n    print(f\"body sees label={label}\")\n\nuse_effect(effect, dependencies=[])\n

Verified: change label three times and the component re-renders three times; the Effect never runs again, and when something else finally forces it, it prints the first render's label. An [] Effect is sealed at mount. Reach for it only when the Effect genuinely reads nothing but stable values (module constants, a setter, a ref).

The four settings, restated with their lifecycle meaning:

Value Meaning dependencies=[a, b] Re-synchronize when a or b changes. dependencies=[] Synchronize once, at mount; the body is never replaced. dependencies=None Re-synchronize after every render. omitted Infer the list from the closure's free variables \u2014 then compare with ==, so a rebuilt-but-equal value will not re-trigger."},{"location":"learn/lifecycle-of-reactive-effects/#all-variables-declared-in-the-component-body-are-reactive","title":"All variables declared in the component body are reactive","text":"

Props, state, and anything derived from them are per-render values, so they belong in the dependency list whenever the Effect reads them. Module-level constants and imported singletons do not: they are the same object for every render and every connection.

Careful with the middle case. A dict built at module level looks like a constant and is not safe to mutate \u2014 every connection shares it (VDOM Mutations). And a mutable default argument is shared too. Neither is a dependency, because neither changes; both are bugs if you write to them.

Hooks are unusable inside an effect body

def effect():\n    theme = use_context(theme_context)   # RuntimeError\n

Effects run in their own task, after rendering, with no life cycle hook on the stack. use_context, use_state, and every other hook raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 and that traceback reaches only the server log. Read what you need during render and let the closure carry it:

theme = use_context(theme_context)\n\ndef effect():\n    apply(theme)\n\nuse_effect(effect, dependencies=[theme])\n
"},{"location":"learn/lifecycle-of-reactive-effects/#what-to-do-when-you-dont-want-to-re-synchronize","title":"What to do when you don't want to re-synchronize","text":"

Three legitimate reasons, three different answers.

The value is genuinely constant. Import it, define it at module level, or hoist it out of the component. Then it is not a dependency because it cannot change:

POLL_INTERVAL = 5\n\n\n@component\ndef poller(topic):\n    async def effect():\n        ...\n\n    use_async_effect(effect, dependencies=[topic])\n

You want the latest value without restarting the process. Keep it in a ref and read .current \u2014 the box persists across renders, so a long-lived Effect always reads the newest assignment (Synchronizing with Effects).

You want to switch the process off. Do not delete the hook call \u2014 that changes the hook order and corrupts every hook after it. Pass a flag and branch inside the Effect:

app.py Run
@component\ndef feed(topic, enabled):\n    def effect():\n        if not enabled:\n            # Nothing to set up, and nothing to clean up. The hook call below\n            # still happens, so the hook order never changes.\n            return None\n\n        print(f\"subscribe {topic}\")\n\n        def cleanup():\n            print(f\"unsubscribe {topic}\")\n\n        return cleanup\n\n    use_effect(effect, dependencies=[topic, enabled])\n\n    return html.p(f\"{topic}\")\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n    enabled, set_enabled = use_state(True)\n\n    async def handle_enabled_click(event):\n        set_enabled(not enabled)\n\n    async def handle_topic_click(event):\n        set_topic(\"sports\" if topic == \"news\" else \"news\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_enabled_click},\n            \"Stop\" if enabled else \"Start\",\n        ),\n        html.button(\n            {\"onClick\": handle_topic_click},\n            \"Switch topic\",\n        ),\n        feed(topic, enabled),\n    )\n
# TODO\n

Verified: Stop runs unsubscribe news; while stopped, switching topics logs nothing (correct \u2014 there is nothing to synchronize); and Start then runs subscribe sports for the new topic. The hook call always happens; only the setup is conditional. Note the dependency list includes enabled \u2014 leaving it out would mean toggling did nothing at all.

Do not omit a dependency to dodge a loop

An Effect that re-triggers itself is telling you something. Measured: an Effect that sets a piece of state it also lists as a dependency rendered 86 times before the probe stopped. Deleting the dependency silences the symptom and leaves you with an Effect that reports the past. Fix the loop \u2014 usually by moving the value into the dependency list and making the setup idempotent, or by deriving the value during render instead (You Might Not Need an Effect).

"},{"location":"learn/lifecycle-of-reactive-effects/#reactpy-will-not-verify-your-dependencies","title":"ReactPy will not verify your dependencies","text":"

React's side of this is eslint-plugin-react-hooks, which reads the Effect and its dependency list and warns when they disagree. ReactPy's equivalent is reactpy-flake8 \u2014 a real plugin, pinned in this repo's pyproject.toml \u2014 but it is far narrower than people expect. Every row below was produced by running the installed reactpy_flake8.run.run_checks over the snippet in question.

The rules-of-hooks half, always on:

What you wrote Reported? a hook called inside an if, for, while, try, match, or a conditional expression yes \u2014 REACTPY102 a hook used after an early return yes \u2014 REACTPY103 a hook used outside any component or hook yes \u2014 REACTPY101 a hook defined as a closure inside another function yes \u2014 REACTPY100

The dependency half, which needs exhaustive_hook_deps=True and is otherwise silent:

What you wrote Reported? an inline lambda Effect, use_memo, or use_callback missing a variable that is assigned at the top of the component body yes \u2014 REACTPY202 the same, but the missing variable is a component parameter no \u2014 parameters are not in the set of names it compares against a named def Effect missing a variable no a @use_effect-decorated function missing a variable no a dependency written as obj.attr rather than a bare name yes \u2014 REACTPY200 a dependency list that is a variable or call rather than a literal list, tuple, or None yes \u2014 REACTPY201

So the linter catches every call-site violation, and catches a missing dependency only for an inline lambda over a body-assigned variable \u2014 one shape among the several people actually write. The stale-value class of bug is largely on you.

Two habits replace the missing dependency check:

  1. Read the Effect and list the names. Every free variable in the body should appear in dependencies, or be provably module-level. This is mechanical \u2014 do it once per Effect at write time.
  2. Test the re-synchronization, not just the mount. Render, change the input, assert the outside world changed:

    async def test_switching_room_reconnects():\n    async with Layout(app()) as layout:\n        await layout.render()\n        ...  # deliver the click that changes the room\n        assert server.log == [\"connect lobby\", \"close lobby\", \"connect dev\"]\n

    A reactpy.core.layout.Layout in pytest is enough \u2014 no browser, and it catches exactly the bugs the linter cannot see.

"},{"location":"learn/lifecycle-of-reactive-effects/#the-order-effects-run-in","title":"The order Effects run in","text":"

Verified output for a parent with two children, each declaring two Effects:

beta: first effect\nbeta: second effect\nalpha: first effect\nalpha: second effect\npanel: my own effect\n
app.py Run
@component\ndef leaf(name):\n    @use_effect\n    def first():\n        print(f\"{name}: first effect\")\n\n    @use_effect\n    def second():\n        print(f\"{name}: second effect\")\n\n    return html.p(name)\n\n\n@component\ndef panel():\n    @use_effect\n    def parent_effect():\n        print(\"panel: my own effect\")\n\n    # Two children: the SECOND one's effects run first.\n    return html.div(leaf(\"alpha\"), leaf(\"beta\"))\n\n\n@component\ndef app():\n    return html.div(\n        html.h1(\"Watch the server console\"),\n        panel(),\n    )\n
# TODO\n

Three rules, and nothing else:

  • Children before parent. A parent's own Effects run after everything below it has set up.
  • Siblings in reverse source order. beta before alpha. This surprises everyone and is worth knowing if two siblings write to the same external resource.
  • Within one component, declaration order. This one you can rely on.

Do not build anything on the first two. They are behaviour, not contract \u2014 the reverse-sibling rule in particular is an artifact of how effects are collected, and code that depends on a particular cross-component order is code that will break. Within a component, order Effects deliberately; across components, make each Effect self-contained.

"},{"location":"learn/lifecycle-of-reactive-effects/#async-effects-cancellation-is-not-instant","title":"Async Effects: cancellation is not instant","text":"

use_async_effect wraps your coroutine in a task. On re-synchronization the old task is cancelled, which means it stops at its next await \u2014 not before, and not necessarily in time.

Two things follow, both measured.

Two runs can be alive at once. Switch topics three times quickly, before any 0.4 s poll resolves, and the log is:

start polling news\ncancelled news\nstart polling sports\nstart polling news          # the older run is still awaiting\ncancelled news\nstart polling sports\nfinished polling sports\nfinished polling sports\n

Verified, and identical across three repeats: two runs are simultaneously awaiting. Cancellation happens when the successor task starts, so a run that has already passed its awaits cannot be recalled \u2014 and notice the two finished lines belong to the same topic.

A superseded run can still write state. With a 0.4 s source and four rapid switches (a \u2192 b \u2192 c \u2192 d), the runs that actually applied their result were ['DATA FOR b', 'DATA FOR d'] \u2014 a and c were cancelled, b and d completed and called their setters. Reproducible across runs.

So cancellation is a bias toward the newest value, not a guarantee. If a stale write would be damaging, do not rely on being cancelled \u2014 tag the data with what produced it and check on write:

loaded, set_loaded = use_state({\"name\": None, \"data\": {}})\n\nasync def effect():\n    data = await fetch(name)\n    if name == expected_name.current:   # still the value we care about?\n        set_loaded({\"name\": name, \"data\": data})\n

Put teardown that must run in the cancellation branch, and re-raise:

async def effect():\n    handle = open_connection()\n    try:\n        while True:\n            await handle.poll()\n    except asyncio.CancelledError:\n        handle.close()\n        raise\n

Swallowing CancelledError leaves the task in a state asyncio did not expect.

A sync Effect blocks the whole connection

use_effect runs on the connection's event loop. Measured: an Effect calling time.sleep(0.4) made the next click take 0.40 s to be served, and every other handler on that connection waited behind it. Only the browser's own local typing stays responsive. Long or blocking work belongs in use_async_effect, with blocking calls wrapped in asyncio.to_thread (Communicating Data Between Server and Client).

"},{"location":"learn/lifecycle-of-reactive-effects/#when-an-effect-fails-nothing-reports-it","title":"When an Effect fails, nothing reports it","text":"

This is the section that saves the most hours. An Effect body that raises is invisible in three places at once:

  • the page looks completely fine,
  • the browser console is clean,
  • and \u2014 the part that misleads people \u2014 the server log stays silent too.
app.py Run
@component\ndef dashboard():\n    refreshes, set_refreshes = use_state(0)\n\n    def effect():\n        # Suppose this reads a metric from a database and the query fails.\n        #\n        # The page does not change, the browser console stays clean, and no\n        # error reaches the model. The ONLY trace is a traceback on the\n        # server -- and, as written here, it does not even appear until the\n        # component unmounts. Test your Effects; the UI will not report them.\n        msg = \"metric query failed\"\n        raise RuntimeError(msg)\n\n    use_effect(effect, dependencies=[refreshes])\n\n    async def handle_refreshes_click(event):\n        set_refreshes(refreshes + 1)\n\n    return html.div(\n        html.h1(\"Dashboard looks fine\"),\n        html.button({\"onClick\": handle_refreshes_click}, \"Refresh\"),\n    )\n
# TODO\n

Measured with a log handler attached to the reactpy logger: after mount, after three dependency changes, and after half a second idle, zero log records. The single ERROR Error in effect record appears only when the component unmounts \u2014 because core/_life_cycle_hook.py, affect_component_will_unmount is where the Effect tasks are finally awaited and their exceptions gathered (core/hooks.py parks each task on stop.wait(), so nobody looks at its result before then).

Consequences to design around:

  • Never diagnose an Effect from the page. Read the server log, and read it after navigating away \u2014 that is when failures surface.
  • Assert the external world in tests. A render assertion passes happily while your Effect is throwing; assert that the connection happened, the row was written, the file exists.
  • Catch inside the Effect and set state when a failure should be visible to the user. ReactPy will not do it for you:

    def effect():\n    try:\n        set_metrics(fetch_metrics())\n    except Exception as error:\n        set_error(str(error))   # now the user can see it\n

Contrast with the failure modes that are reported, so you can tell which one you have:

Symptom Cause Blank region, error in the model (debug on) or a server traceback the component body raised \u2014 e.g. an async def passed to use_effect ValueError: Cell is empty in the model an Effect registered above the state it reads Page looks fine, log silent until unmount the Effect body raised Page looks fine, log clean forever, Effect never runs wrong dependencies"},{"location":"learn/lifecycle-of-reactive-effects/#recap","title":"Recap","text":"
  • An Effect is a synchronization process: setup, clean-up, and a dependency list that says when to redo it.
  • The clean-up of a run executes on its successor's task, before that successor's setup \u2014 so a re-synchronization is leave-then-join, in order.
  • An Effect reads the values of the render that created it. Not the latest, not necessarily the first.
  • The dependency list, not the Effect's own text, decides re-synchronization. dependencies=[] seals the body at mount.
  • Dependencies compare with == via strictly_equal, so local functions and rebuilt-but-equal containers never count as changed \u2014 and a frozen Effect is silent.
  • ReactPy will not check your dependency list: reactpy-flake8 reports hooks in branches, and missing dependencies only for inline lambdas. Write the list yourself and test the re-synchronization.
  • Children's Effects run before the parent's, siblings in reverse, and within a component in declaration order. Only the last is safe to depend on.
  • Async cancellation lands at the next await: runs can overlap and a superseded run can still write. Guard the write; don't trust the cancel.
  • A raising Effect shows nothing in the page, nothing in the browser, and nothing in the log until unmount. Verify Effects by asserting their effect on the world.
"},{"location":"learn/lifecycle-of-reactive-effects/#related","title":"Related","text":"
  • Synchronizing with Effects \u2014 the four-way dependency table, clean-ups, and the common patterns
  • You Might Not Need an Effect \u2014 the cases that belong in the render
  • Separating Events from Effects \u2014 reading fresh values without restarting a process
  • Removing Effect Dependencies \u2014 the legitimate ways to drop a dependency
  • Use Effect and Use Async Effect \u2014 parameter-level reference
"},{"location":"learn/manipulating-the-dom-with-refs/","title":"Manipulating the DOM with Refs","text":""},{"location":"learn/manipulating-the-dom-with-refs/#overview","title":"Overview","text":"

Sometimes you need to do something a rendering model cannot express: focus an input, scroll an element into view, call a browser API ReactPy does not expose. In React you reach for a ref to hold the DOM node. ReactPy runs on the server and sends description-of-UI over a socket, so it has no DOM node to hand you \u2014 and the consequences shape everything on this page.

You will learn

  • Why there is no <div ref={myRef}> equivalent in ReactPy
  • What happens if you pass a \"ref\" attribute anyway
  • How to focus, scroll, and restyle declaratively, and when you can't
  • Why you must not mutate the DOM ReactPy renders
"},{"location":"learn/manipulating-the-dom-with-refs/#there-is-no-ref-to-the-node","title":"There is no ref to the node","text":"

In React, you declare a ref, pass it to a tag, and React fills it in:

# This is React, not ReactPy. It does not work.\n<div ref={myRef}>\n

myRef.current then holds a real DOM node you can call focus() or scrollIntoView() on. That works because React runs in the browser, next to the DOM.

ReactPy runs in your Python process. It builds a VDOM dictionary, serialises it to JSON, and sends it over a websocket; the browser-side client turns that JSON into DOM nodes. By the time a node exists, the code that described it has finished running, and there is no channel for the browser to send a node back. The \"node\" is on the other side of a network boundary.

"},{"location":"learn/manipulating-the-dom-with-refs/#what-ref-actually-does","title":"What \"ref\" actually does","text":"

It is tempting to copy the React shape and put \"ref\" in the attribute dict. Don't \u2014 both forms fail, and one of them fails quietly.

Passing a dict that looks like a ref is silently dropped:

html.div({\"ref\": {\"current\": None}}, \"...\")\n

The key travels to the browser as an ordinary attribute. Even if the client writes a node into that object, it writes into its own throwaway copy decoded from the JSON \u2014 nothing can travel back up the socket into your Python dict. Measured: the element renders normally, as_dict[\"current\"] stays None forever, and there is no error and no warning. A \"focus the input\" button built this way simply never focuses anything.

Passing a string is worse, because it throws in the browser:

html.div({\"ref\": \"my_ref\"}, \"...\")\n

preact tries to assign to the string and the page dies with an uncaught:

TypeError: Cannot create property 'current' on string 'my_ref'\n

The server logs nothing. Check the browser console.

"},{"location":"learn/manipulating-the-dom-with-refs/#focusing-an-input","title":"Focusing an input","text":"

If the input should be focused when it first appears, that is declarative \u2014 use the browser's own attribute and let ReactPy render it:

app.py Run
@component\ndef search_form():\n    return html.form(\n        html.label({\"htmlFor\": \"search\"}, \"Search\"),\n        html.input({\"id\": \"search\", \"type\": \"search\", \"autoFocus\": True}),\n    )\n
# TODO\n

No ref, no effect, no JavaScript. The attribute dict is the DOM.

To focus later, in response to a click, there is no server-side way to reach a node \u2014 but ReactPy has an escape hatch for exactly this. When the value of an on* key is a string, it runs as JavaScript in the browser (see Responding to Events). Give the element an \"id\" and reach it by id:

app.py Run
@component\ndef search_form():\n    return html.form(\n        html.input({\"id\": \"search\", \"type\": \"search\"}),\n        # A string in a handler slot runs in the browser (see Responding to\n        # Events), which is the only way to reach an element by id.\n        html.button(\n            {\n                \"onClick\": \"document.getElementById('search').focus()\",\n            },\n            \"Focus the input\",\n        ),\n    )\n
# TODO\n

This is the honest equivalent of myRef.current.focus(). The trade-off is that the code doing the focusing now lives in a string, on the server, unchecked by your linter and invisible to your type checker. Keep it to one-line calls.

Re-rendering does not re-focus

A common wish is \"re-fire autoFocus when the user clicks again\". Changing the element's \"key\" really does remount it \u2014 ReactPy destroys and recreates the node, and a child's state resets \u2014 but autoFocus does not re-trigger on that remount, because the browser only applies autofocus on initial page load. Measured: after re-keying, focus stayed on the button that was clicked. Use the inline-JS form above instead.

"},{"location":"learn/manipulating-the-dom-with-refs/#scrolling-to-an-element","title":"Scrolling to an element","text":"

The React version of this example keeps three refs, one per image, and calls scrollIntoView() on each. In ReactPy you give each item an id and let the browser find it:

app.pydata.pystyles.css Run
@component\ndef cat_friends():\n    return html(\n        html.div(\n            {\"id\": \"carousel\", \"className\": \"carousel\"},\n            [\n                html.li(\n                    {\"key\": person[\"id\"], \"id\": f\"friend-{person['id']}\"},\n                    html.img({\"src\": get_image_url(person), \"alt\": person[\"name\"]}),\n                )\n                for person in friends\n            ],\n        ),\n        html.div(\n            {\"className\": \"buttons\"},\n            [\n                html.button(\n                    {\n                        \"key\": person[\"id\"],\n                        \"onClick\": f\"document.getElementById('friend-{person['id']}')\"\n                        \".scrollIntoView({behavior: 'smooth', inline: 'center'})\",\n                    },\n                    person[\"name\"].split()[0],\n                )\n                for person in friends\n            ],\n        ),\n    )\n
friends = [\n    {\n        \"id\": 0,\n        \"name\": \"Creola Katherine Johnson\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,\n        \"name\": \"Mohammad Abdus Salam\",\n        \"image_id\": \"bE7W1ji\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}.jpg\"\n
.carousel {\n    display: flex;\n    gap: 10px;\n    overflow-x: auto;\n    scroll-snap-type: x mandatory;\n    padding: 10px 0;\n}\n\n.carousel li {\n    list-style: none;\n    flex: 0 0 auto;\n    scroll-snap-align: center;\n}\n\n.carousel img {\n    width: 130px;\n    height: 130px;\n    border-radius: 100%;\n    object-fit: cover;\n    display: block;\n}\n\n.buttons {\n    display: flex;\n    gap: 10px;\n    margin-top: 10px;\n}\n
# TODO\n

Note the f-string building the JavaScript: the id is interpolated server-side, so each button gets its own literal scrollIntoView call. That is also the risk \u2014 the interpolation is not escaped. Interpolating anything a user controls into that string is an injection hole; interpolate your own integer keys, like person[\"id\"] above, and nothing else.

"},{"location":"learn/manipulating-the-dom-with-refs/#showing-and-hiding","title":"Showing and hiding","text":"

Toggling visibility is the case where React's ref example and ReactPy agree on the goal but differ completely in method. There is no element.style.display = 'none'; you render the difference:

app.py Run
@component\ndef todo_list():\n    shown, set_shown = use_state(True)\n\n    async def handle_shown_click(event):\n        set_shown(not shown)\n\n    return html(\n        html.div(\n            {\"id\": \"todo\", \"hidden\": not shown},\n            html.p(\"Learn ReactPy\"),\n            html.p(\"Build something\"),\n        ),\n        html.button(\n            {\"onClick\": handle_shown_click},\n            \"Hide\" if shown else \"Show\",\n        ),\n    )\n
# TODO\n

Pressing the button flips shown, the \"hidden\" attribute changes, and the client updates the element. Any style you would have set by hand goes in the attribute dict the same way \u2014 \"style\" accepts a dict of camelCased CSS properties:

html.div(\n    {\"style\": {\"color\": \"red\", \"fontSize\": \"20px\", \"fontWeight\": \"700\"}},\n    \"styled text\",\n)\n

Changing a value in that dict from state re-renders the element with the new style, which is what you almost always want instead of touching element.style from the browser.

"},{"location":"learn/manipulating-the-dom-with-refs/#two-component-only-features-you-cannot-port","title":"Two component-only features you cannot port","text":"

Two sections of React's version of this page have no ReactPy equivalent at all, and it is worth knowing they are absent rather than hunting for them:

  • useImperativeHandle / forwardRef. React lets a component expose a hand-picked slice of its DOM to a parent. There is no forward_ref and no use_imperative_handle in ReactPy \u2014 a component cannot hand a parent a node it does not have. If a parent must drive a child, pass a prop and let the child render it.
  • flushSync. React lets you force a synchronous repaint so that a subsequent DOM read sees the new values. ReactPy renders by sending a message, so there is nothing to flush; the client applies updates as they arrive.
"},{"location":"learn/manipulating-the-dom-with-refs/#best-practices","title":"Best practices","text":"

Refs are an escape hatch, and here the hatch is even narrower: it is a string of JavaScript.

  • Prefer declarative. If the change can be expressed as an attribute, a class, a \"style\" dict, a \"key\", or a conditional render, write that. It survives re-renders, and it is visible to your tools.
  • Keep inline JavaScript to one call. \"document.getElementById('x').focus()\" is fine. A multi-statement script embedded in an attribute dict is unmaintainable and unlintable.
  • Never interpolate user input into an inline-JS string. Build the JS from your own constants and ids.
  • Do not mutate DOM that ReactPy renders. This is the same hazard as in React, and it is easier to hit because nothing warns you. If an inline-JS handler calls remove() on an element ReactPy believes it owns, the next render that tries to update that element is working against a node that is gone. Toggle a state value and let ReactPy remove it.
  • You may touch nodes ReactPy never manages. An element ReactPy renders empty and never writes children to is yours to fill from inline JavaScript. It has no reason to update that subtree, so nothing conflicts.
"},{"location":"learn/manipulating-the-dom-with-refs/#summary","title":"Summary","text":"
  • ReactPy is server-side: there is no DOM node to store in a ref, and {\"ref\": ...} either vanishes (dict) or throws in the browser (string).
  • Express focus, visibility, and styling declaratively first: \"autoFocus\", \"hidden\", \"style\", conditional rendering.
  • For the imperative remainder \u2014 focus on click, scroll into view \u2014 give elements ids and use the inline-JavaScript handler form, keeping each snippet to a single call and interpolating only trusted values.
  • forwardRef, useImperativeHandle, and flushSync do not exist in ReactPy.
  • Never mutate nodes ReactPy owns; remove them by changing state instead.
"},{"location":"learn/manually-register-a-client/","title":"Manually Register a Client","text":""},{"location":"learn/manually-register-a-client/#overview","title":"Overview","text":"

\"Registering a client\" in ReactPy means telling the browser three things: which component to mount, where the socket lives, and which JavaScript modules it may import. The executors do all three for you. This page is for the cases where they cannot \u2014 a page you render yourself, several components on one document, or a component written in JavaScript that the server has to describe but cannot run.

You will learn

  • What mountReactPy needs, and how to write the call by hand
  • How to mount several components in one document, and what breaks if you mount twice into the same node
  • How an author-supplied import map suppresses ReactPy's own
  • How to register a JavaScript component so ReactPy can render it
"},{"location":"learn/manually-register-a-client/#the-three-registrations","title":"The three registrations","text":"What Where it lives Who normally writes it The root component to mount componentPath in mountReactPy, and the backend's component registry the executor's HTML template The socket address pathPrefix + componentPath, concatenated the executor's HTML template Bare-specifier resolution (\"react\", \"react-dom\") <script type=\"importmap\"> injected only if no <script type=\"importmap\" id=\"reactpy-importmap\"> exists

The full reference for the JavaScript side, including every option and its quirks, is Client API. This page covers the parts the executors normally hide.

"},{"location":"learn/manually-register-a-client/#mounting-by-hand","title":"Mounting by hand","text":"

The minimal call needs only an element and a path:

<div id=\"app\"></div>\n<script type=\"module\">\n    import { mountReactPy } from \"/reactpy/static/index.js\";\n\n    mountReactPy({\n        mountElement: document.getElementById(\"app\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"main.counter/\",\n    });\n</script>\n

Three details are load-bearing, all verified in a real browser:

  • pathPrefix must start and end with /. The client builds the socket URL by string concatenation, so \"reactpy/\" on a host with a port throws TypeError: Failed to construct 'URL': Invalid URL \u2014 and on a host without a port silently connects to a hostname like example.comreactpy. The failure mode changes with deployment, which is what makes it nasty.
  • componentPath carries a trailing slash and must name a component the server knows. \"main.counter/\", not \"main.counter\".
  • The import must be <script type=\"module\">. The bundle statically imports two sibling chunks; a classic script fails with SyntaxError: Cannot use import statement outside a module before anything ReactPy-related runs.

The component itself lives in a Python module the backend can import. Under ReactPyMiddleware, componentPath is the dotted path you listed in root_components plus a slash \u2014 the middleware matches ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$. Under ReactPy there is one root and componentPath must be omitted: the client builds the URL as pathPrefix + componentPath (static/index.js) and ReactPy.match_dispatch_path compares the result against dispatcher_path exactly, so a stray \"/\" produces /reactpy//, matches nothing, and the socket dies while the server logs a NotImplementedError \u2014 one whose %s placeholders were never filled in, so the line reads (\"ReactPy app received unsupported request of type '%s' at path '%s'\", 'websocket', '/reactpy//'). Grep for unsupported request of type, not for the literal you expect. See The executors route differently.

Nothing stops you from mounting twice into the same element

mountReactPy returns nothing and keeps no registry, so a second call into the same node is not rejected. Verified in Chrome with two mounts pointing at one <div>: the second component's markup wins the visible tree, and the first keeps a fully live websocket \u2014 its socket stays OPEN, its server-side layout stays alive, and events it registered can still reach the server. You get one visible component and one invisible one consuming a connection and a layout each.

The client does not unmount the first subtree; it renders over it. Nothing you can do from JavaScript will stop it \u2014 there is no unmountReactPy. The only clean teardown is navigation or reload. If you are building a client-side router that swaps components, mount into a fresh element each time and remove the old one, accepting that the orphaned layout lives until the page does.

"},{"location":"learn/manually-register-a-client/#several-components-in-one-document","title":"Several components in one document","text":"

Each mountReactPy call opens its own websocket and gets its own Layout, which means separate hook state and no shared memory between them. Verified with two counters on one page: incrementing one left the other at zero, and the console logged two independent [ReactPy] Connected! lines.

<div id=\"header\"></div>\n<div id=\"cart\"></div>\n<script type=\"module\">\n    import { mountReactPy } from \"/reactpy/static/index.js\";\n\n    mountReactPy({\n        mountElement: document.getElementById(\"header\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"shop.header/\",\n    });\n    mountReactPy({\n        mountElement: document.getElementById(\"cart\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"shop.cart/\",\n    });\n</script>\n

Under ReactPyMiddleware, register both:

from reactpy.executors.asgi import ReactPyMiddleware\n\napp = ReactPyMiddleware(\n    starlette_app,\n    root_components=[\"shop.header\", \"shop.cart\"],\n)\n

Separate roots do not share state

Two components on one page are two layouts. They cannot use_state their way to agreement, and there is no event bus between them. If they must agree, either make them one component tree, or pass the shared value in as props from a single root \u2014 see Sharing State Between Components and Passing Data Deeply with Context.

You can also mount later, from a click handler or after a fetch \u2014 the call is ordinary JavaScript and nothing about it is privileged.

"},{"location":"learn/manually-register-a-client/#import-maps","title":"Import maps","text":"

An import map is only needed when your model contains an importSource, which is what JavaScript components produce. Bare specifiers like \"react\" and \"react-dom\" are resolved by the map; without one, the client falls back to its bundled preact and says so on console.debug, at a level most consoles filter out.

The executors inject a map only when one with a specific id is absent:

if (!document.querySelector(\"#reactpy-importmap\")) { /* inject ReactPy's map */ }\n

The trigger is the id, not the content. Write your own map and give it that id, or ReactPy adds a second one alongside yours and the browser reports a conflict on the \"react\" rule. See The import map for the three-case table.

ReactPy ships a helper that produces exactly that element, so you can decide the framework per page:

from reactpy.executors.asgi import ReactPy\nfrom reactpy.reactjs import import_reactjs\n\napp = ReactPy(\n    counter,\n    html_head=html.head(import_reactjs(\"react\", version=\"19\")),\n)\n

import_reactjs(framework=None, version=None, use_local=False) returns a html.script already carrying {\"type\": \"importmap\", \"id\": \"reactpy-importmap\"}, so putting it in html_head suppresses the automatic injection. \"preact\" (the default) maps the four react* specifiers onto esm.sh/v135/preact@10/compat; \"react\" uses real React at the version you pass (\"19\" if you omit it); use_local=True maps them to ReactPy's own bundled chunks, which is what the default injection does anyway.

use_local rejects companions

import_reactjs(use_local=True, framework=\"react\") raises ValueError: use_local cannot be used with framework or version. Choose one source or the other.

"},{"location":"learn/manually-register-a-client/#registering-a-javascript-component","title":"Registering a JavaScript component","text":"

Python describes; the browser runs. A JavaScript component is any ES module that exports the names you ask for. It can also export a bind function that tells the client how to create, render, and unmount nodes in a container the client owns \u2014 and that is what you want, because the alternative is silent:

// vdom.tsx, loadImportSource\nlet { bind } = module;\nif (typeof bind !== \"function\") {\n    bind = await infer_bind_from_environment();\n}\n

bind is therefore optional. Leave it out and the client infers one \u2014 first trying real React through the import map, then falling back to its bundled preact \u2014 and logs ReactPy will render JavaScript components using internal bindings for 'react' at console.debug, with no [ReactPy] prefix. Your module still mounts and appears to work, so a missing or misnamed bind is easy to miss. Ship it explicitly:

badge.jsmain.py Run
// The bind() export tells the client how to put nodes on screen. It receives\n// the container element the client owns and a config object carrying\n// sendMessage/onMessage, and must hand back create/render/unmount.\nexport function bind(node, config) {\n  return {\n    create: (component, props, children) => {\n      const el = document.createElement(\"span\");\n      el.className = props.tone || \"default\";\n      el.textContent = props.label + \": \" + (children || []).join(\"\");\n      el.addEventListener(\"click\", () => {\n        if (props.onPick) {\n          props.onPick({ label: props.label });\n        }\n      });\n      node.appendChild(el);\n      return el;\n    },\n    render: (el) => {\n      node.innerHTML = \"\";\n      node.appendChild(el);\n    },\n    unmount: () => {\n      node.innerHTML = \"\";\n    },\n  };\n}\n\n// One export per component you intend to register by name.\nexport function Badge(props, children) {\n  return { props, children };\n}\n
HERE = Path(__file__).parent\n\nBadge = component_from_file(\n    HERE / \"badge.js\",\n    \"Badge\",\n    name=\"badge\",\n    fallback=html.p(\"loading badge...\"),\n)\n\n\n@component\ndef product_card(title, tone):\n    return html.div(\n        html.h2(title),\n        Badge({\"label\": \"in stock\", \"tone\": tone}, \"ready to ship\"),\n    )\n
# TODO\n

The registration call gives you a normal constructor:

from pathlib import Path\n\nfrom reactpy.reactjs import component_from_file\n\nBadge = component_from_file(Path(\"badge.js\"), \"Badge\", name=\"badge\")\n\nBadge({\"label\": \"alpha\", \"tone\": \"warn\"}, \"hello\")\n

which produces the element the client executes:

{\n    \"tagName\": \"Badge\",\n    \"attributes\": {\"label\": \"alpha\", \"tone\": \"warn\"},\n    \"children\": [\"hello\"],\n    \"importSource\": {\n        \"source\": \"badge.js\",\n        \"sourceType\": \"NAME\",\n        \"fallback\": null,\n        \"unmountBeforeUpdate\": false\n    }\n}\n

importSource is the whole mechanism: sourceType is \"NAME\" or \"URL\", and the client resolves them differently \u2014 \"NAME\" becomes await import(\"/<prefix>modules/\" + source) and \"URL\" becomes await import(source) directly.

Verified in Chrome: the badge rendered as <span class=\"warn\">alpha: hello</span>, with the <span> coming from JavaScript and the wrapper from Python.

"},{"location":"learn/manually-register-a-client/#the-four-registration-functions","title":"The four registration functions","text":"

All in reactpy.reactjs, all returning a VdomConstructor (or a list, if you pass a list of names):

Function Source Typical use component_from_url(url, import_names, ...) a URL the browser can reach a CDN build, a dev server component_from_npm(package, import_names, version=\"latest\", cdn=\"https://esm.sh/v135\", bundle=...) an NPM package, rewritten to a CDN URL third-party React components component_from_file(file, import_names, name=\"\", symlink=False, ...) a file on disk, copied into the web-modules dir a component you ship in your package component_from_string(content, import_names, name=\"\", ...) a string of JavaScript tiny inline modules, tests

reactpy.web.module_from_url / module_from_file / module_from_string / export do the same thing and are all deprecated \u2014 they warn(DeprecationWarning) telling you to use the component_from_* form.

"},{"location":"learn/manually-register-a-client/#serving-the-module","title":"Serving the module","text":"

A \"NAME\" module is copied into config.REACTPY_WEB_MODULES_DIR and fetched from {path_prefix}modules/ \u2014 which is why a custom backend must serve that directory, and why the built-in ones do:

from reactpy.config import REACTPY_WEB_MODULES_DIR\n\nmodules_app = ServeStaticASGI(not_found, root=REACTPY_WEB_MODULES_DIR.current, prefix=\"/reactpy/modules/\")\n

The default web-modules directory is a temporary directory

REACTPY_WEB_MODULES_DIR defaults to a fresh TemporaryDirectory created at import (config.py). It is per process: read it in one process and you get a path that does not exist in the next. Two consequences:

  • Under a multi-worker server, workers get different directories and a module registered by one is 404 for another. Pin it \u2014 pass web_modules_dir=Path(...) to the executor or set the REACTPY_WEB_MODULES_DIR environment variable to one shared path.
  • The upstream config docstring says the directory must be treated as a black box. Do not read from it yourself; register modules and let the client fetch them.

A \"URL\" module needs no serving at all \u2014 the browser goes to the URL. That makes CDN registration the simpler path when you have a network, and a self-hosted directory the right one for offline or air-gapped use.

"},{"location":"learn/manually-register-a-client/#options-worth-knowing","title":"Options worth knowing","text":"
  • fallback \u2014 what to show while the module loads. Pass an element (html.p(\"loading...\")). Without it there is nothing, and a slow module reads as a broken page.
  • allow_children \u2014 set False for a component that takes props only. Passing a child then raises TypeError: 'Badge' nodes cannot have children. at the call site rather than silently dropping it.
  • unmount_before_update \u2014 unmount and rebuild on every update. Only for packages that fail to re-render on prop change; it costs a full DOM rebuild each time.
  • resolve_imports \u2014 parse the module to learn its export names, so a typo fails in Python at registration instead of in the browser at render. Off by default for component_from_file/component_from_string; it walks imports to resolve_imports_depth (default 5).

A wrong export name fails at different times depending on a flag

With resolve_imports=True, component_from_file(\"badge.js\", \"Nope\") raises immediately:

ValueError: 'badge.js' does not contain 'Nope'\n

With it off \u2014 the default \u2014 you get no Python error, and instead the browser logs, verbatim including the typo:

Module from source {\"source\": \"badge.js\", \"sourceType\": \"NAME\"} does not export Nope\n

and nothing renders. Turn the flag on during development; it turns a console hunt into a stack trace.

A bad bind is a console error, not an exception

If your module's bind does not return an object with create, render, and unmount, the client logs <source> returned an impropper binding \u2014 misspelling in the upstream string \u2014 and renders nothing. Python stays happy. The message goes to console.error, so it is easy to miss when the component simply is not there.

"},{"location":"learn/manually-register-a-client/#summary","title":"Summary","text":"
  • Registering a client means the component path, the socket prefix, and an import map \u2014 the executors write all three for you.
  • pathPrefix needs both slashes, componentPath needs its trailing slash, and the import must be a module script.
  • Mount each component into its own element. Mounting twice into one node leaves a live, invisible websocket you cannot close.
  • Multiple mounts on a page are multiple layouts with no shared state; pass shared values as props instead.
  • ReactPy injects an import map only if no element with id=\"reactpy-importmap\" exists \u2014 the id, not the content, is the trigger. import_reactjs() produces that element.
  • JavaScript components are ordinary ES modules with a bind export, registered with component_from_url/_npm/_file/_string, and served from {prefix}modules/ unless they are URL-sourced.
  • The default web-modules directory is a per-process temporary directory; pin it under any multi-worker deployment.
"},{"location":"learn/manually-register-a-client/#related","title":"Related","text":"
  • Client API \u2014 the full mountReactPy reference and failure-mode table
  • Creating Backends \u2014 serving static/ and modules/ yourself
  • Protocol Structure \u2014 what travels over the socket the mount opens
  • Common Props \u2014 the importSource model shape
  • ReactPyMiddleware \u2014 mounting components into pages you already own
"},{"location":"learn/passing-data-deeply-with-context/","title":"Passing Data Deeply with Context","text":""},{"location":"learn/passing-data-deeply-with-context/#overview","title":"Overview","text":"

Passing data through components that only hand it on is tedious and easy to get wrong. Context lets a component read a value that an ancestor provides, skipping the middle of the tree. In ReactPy it is purely a plumbing tool \u2014 it carries values down and nothing else \u2014 and there are three behaviours worth knowing before you rely on it.

You will learn

  • How to recognise prop drilling worth replacing
  • The three steps: create, use, provide
  • Why a component cannot read the context it provides
  • Why removing a provider throws away the subtree's state
"},{"location":"learn/passing-data-deeply-with-context/#the-problem-with-passing-props","title":"The problem with passing props","text":"

Here is a list of places, and each row is coloured by the current theme. Only the deepest component cares about the theme, but the prop has to travel through everyone:

app.py Run
PLACES = [\"Almaty\", \"Astana\", \"Shymkent\", \"Karaganda\", \"Turkestan\"]\n\n\n@component\ndef place_label(place, theme):\n    color = \"coral\" if theme == \"dark\" else \"black\"\n    return html.li({\"style\": {\"color\": color}}, place)\n\n\n@component\ndef place_list(places, theme):\n    return html.ul([place_label(place, theme) for place in places])\n\n\n@component\ndef list_of_places(places, theme):\n    # Renders its children inside the list box.\n    return html.div(\n        {\"style\": {\"border\": \"1px solid #ccc\", \"padding\": \"8px\"}},\n        place_list(places, theme),\n    )\n\n\n@component\ndef section(places, theme):\n    return html.section(html.h3(\"Places\"), list_of_places(places, theme))\n\n\n@component\ndef app():\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": toggle}, f\"Switch to {'light' if theme == 'dark' else 'dark'}\"\n        ),\n        html.p({\"id\": \"theme\"}, f\"theme={theme}\"),\n        html.div({\"id\": \"places\"}, section(PLACES, theme)),\n    )\n
# TODO\n

app holds the theme. section receives it, list_of_places receives it, place_list receives it, and only place_label reads it. Three components mention a value they never use, and adding a row to list_of_places means threading theme through one more signature.

That is the shape worth replacing: a prop that a component only forwards. If a component would use a prop itself, pass it as a prop \u2014 that is easier to trace.

"},{"location":"learn/passing-data-deeply-with-context/#context-an-alternative-to-passing-props","title":"Context: an alternative to passing props","text":"

Three steps, and the order matters: create the context, read it where you need it, and provide it somewhere above.

"},{"location":"learn/passing-data-deeply-with-context/#step-1-create-the-context","title":"Step 1: Create the context","text":"

A context is an object you create once, usually at module level:

from reactpy import create_context\n\nThemeContext = create_context(\"light\")\n

The argument is the default \u2014 what use_context returns when no provider sits above the reader. Pick something that renders sensibly on its own, because a component will occasionally render without a wrapper.

The object is matched by identity, so create it once and import it everywhere. Two create_context calls are two unrelated contexts even with identical defaults.

"},{"location":"learn/passing-data-deeply-with-context/#step-2-use-the-context","title":"Step 2: Use the context","text":"

In the component that needs the value:

from reactpy import use_context\n\ntheme = use_context(ThemeContext)\n

No parameter, no forwarding. The components in between never learn the value exists:

app.py Run
ThemeContext = create_context(\"light\")\n\n\n@component\ndef place_label(place):\n    # Reads the theme directly: no prop had to travel here.\n    theme = use_context(ThemeContext)\n    color = \"coral\" if theme == \"dark\" else \"black\"\n    return html.li({\"style\": {\"color\": color}}, place)\n\n\n@component\ndef place_list(places):\n    # No theme parameter, and none to forward.\n    return html.ul([place_label(place) for place in places])\n\n\n@component\ndef list_of_places(places):\n    return html.div(\n        {\"style\": {\"border\": \"1px solid #ccc\", \"padding\": \"8px\"}},\n        place_list(places),\n    )\n\n\n@component\ndef section(places):\n    return html.section(html.h3(\"Places\"), list_of_places(places))\n\n\n@component\ndef app():\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": toggle}, f\"Switch to {'light' if theme == 'dark' else 'dark'}\"\n        ),\n        html.p({\"id\": \"theme\"}, f\"theme={theme}\"),\n        # The provider is a component: it renders its children with no wrapper.\n        ThemeContext(html.div({\"id\": \"places\"}, section(PLACES)), value=theme),\n    )\n
# TODO\n

place_label calls use_context, and place_list, list_of_places, and section no longer mention theme at all. Verified: this version and the prop-drilling version above render the same five rows with the same colours, and both respond to the toggle identically.

Without any provider at all, every reader gets the default:

app.py Run
ThemeContext = create_context(\"light\")\n\n\n@component\ndef label(text):\n    theme = use_context(ThemeContext)\n    return html.p({\"id\": f\"label-{text}\"}, f\"{text}: theme={theme}\")\n\n\n@component\ndef app():\n    # Nothing wraps these in ThemeContext, so use_context falls back to the\n    # value passed to create_context.\n    return html.div(html.h3(\"No provider\"), label(\"a\"), label(\"b\"))\n
# TODO\n

Both labels read theme=light \u2014 the value from create_context, not None. This is what makes it safe to reuse a component in a page that never sets the theme up.

"},{"location":"learn/passing-data-deeply-with-context/#step-3-provide-the-context","title":"Step 3: Provide the context","text":"

Wrap the subtree you want to see the value:

ThemeContext(children, value=theme)\n

The context object is a component. Calling it produces a provider element whose children render in place, adding no wrapper of its own \u2014 verified: the element with id=\"places\" contains the whole tree with nothing extra between it and the list.

A provider only covers its own children. A sibling rendered after it sees the default, and a provider nested inside another one shadows it for its own subtree:

app.py Run
@component\ndef deep_label(tag):\n    # Three levels down, still no prop in sight.\n    return html.p({\"id\": tag}, f\"{tag} sees: {use_context(ThemeContext)}\")\n\n\n@component\ndef middle():\n    return html.div(html.h4(\"middle\"), deep_label(\"deep\"))\n\n\n@component\ndef wrapper():\n    # An intermediate component that neither receives nor forwards a theme.\n    return html.div(html.h4(\"wrapper\"), middle())\n\n\n@component\ndef inner_scope():\n    return html.div(\n        deep_label(\"inner\"),\n        # A provider nested inside another one shadows it for its subtree.\n        ThemeContext(deep_label(\"deepest\"), value=\"innermost\"),\n    )\n\n\n@component\ndef app():\n    return html.div(\n        # Same context, several providers, several scopes.\n        deep_label(\"outside\"),\n        ThemeContext(wrapper(), value=\"outer\"),\n        ThemeContext(inner_scope(), value=\"middle-provider\"),\n        deep_label(\"after\"),\n    )\n
# TODO\n

Verified readouts: outside and after see light (the default), deep \u2014 three levels below outer \u2014 sees outer, inner sees middle-provider, and deepest, inside the nested provider, sees innermost. The nearest provider wins; the search walks up and stops.

Pitfall

use_context belongs in the component that uses the value, not in the parent that places it. Write ThemeContext(deep_label(), value=\"x\"), not ThemeContext(html.p(f\"{use_context(ThemeContext)}\"), value=\"x\"). The second one calls the hook while the parent renders, so it reads the parent's context \u2014 the provider has not been entered yet. This is easy to hit by accident: a label formatted inline above a provider reported the default light while its own provider supplied middle-provider.

"},{"location":"learn/passing-data-deeply-with-context/#using-and-providing-context-from-the-same-component","title":"Using and providing context from the same component","text":"

A component cannot read the context it provides. The provider applies to its children, and a component is not its own child:

app.py Run
@component\ndef label(text):\n    return html.p({\"id\": f\"label-{text}\"}, f\"{text} sees: {use_context(ThemeContext)}\")\n\n\n@component\ndef section():\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button({\"onClick\": toggle}, \"toggle\"),\n        # Reads the context the way any *consumer* does...\n        html.p({\"id\": \"self\"}, f\"section itself sees: {use_context(ThemeContext)}\"),\n        # ...and provides it for everything below, which is not itself.\n        ThemeContext(label(\"child\"), value=theme),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Self-provider\"), section(), label(\"outside\"))\n
# TODO\n

The child reads child sees: dark while the component that rendered the provider reads section itself sees: light \u2014 the default, before and after the toggle. If the component needs the value too, read the state it comes from: theme is right there in section, so use it directly.

"},{"location":"learn/passing-data-deeply-with-context/#context-passes-through-intermediate-components","title":"Context passes through intermediate components","text":"

The value reaches any depth as long as the provider is a genuine ancestor. wrapper and middle in the nesting example above receive no props at all, and deep still resolves the theme.

This is also what makes context the right tool for a component you do not control \u2014 a third-party list renderer, a slot you pass children into. Anything rendered inside the provider's subtree can read it, however deep.

Note

A component's state, however, belongs to its position in the tree. Two sibling subtrees that both mount draft() get two independent pieces of state (verified: x#1 and x#2 under two providers of the same context). Context shares what flows down from the provider; it does not merge instances below it.

"},{"location":"learn/passing-data-deeply-with-context/#removing-a-provider-resets-the-subtree-below-it","title":"Removing a provider resets the subtree below it","text":"

A provider is a component, so adding or removing one changes the shape of the tree \u2014 and ReactPy identifies a component by where it sits. The subtree below is unmounted and rebuilt, with its state. Press change value and the draft you typed survives; press toggle provider and it is gone:

app.py Run
@component\ndef draft():\n    # State owned by the leaf. It is lost if this component is remounted.\n    text, set_text = use_state(\"\")\n    body = use_ref(0)\n    body.current += 1\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"placeholder\": \"type something\",\n            }\n        ),\n        html.p(\n            {\"id\": \"draft\"},\n            f\"draft={text!r} renders={body.current} theme={use_context(ThemeContext)}\",\n        ),\n    )\n\n\n@component\ndef panel():\n    wrap, set_wrap = use_state(True)\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle_wrap(event):\n        # Adding or removing the provider changes the shape of the tree, so the\n        # subtree below is unmounted and rebuilt: the draft text is lost.\n        set_wrap(not wrap)\n\n    async def toggle_theme(event):\n        # Changing only the value keeps the same tree: the draft survives.\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button({\"onClick\": toggle_wrap}, \"toggle provider\"),\n        html.button({\"onClick\": toggle_theme}, \"change value\"),\n        (ThemeContext(draft(), value=theme) if wrap else draft()),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Provider identity\"), panel())\n
# TODO\n

Verified sequence. Type hello: the readout shows draft='hello'. Press change value: draft='hello' ... theme=light, and the component's own render counter keeps counting, which is how you can tell it is the same instance \u2014 a changed value does not disturb the tree. Press toggle provider: the counter resets to renders=1 and draft=''. The leaf was unmounted and a fresh one mounted, so its state is not stale, it is new.

Flipping the conditional back does not restore it either \u2014 that is a third instance.

The rule: keep the provider in the tree and vary only its value. Toggling a theme off should be Theme(child, value=\"light\"), not Theme(child, value=...) if dark else child.

Do not reach for key to fix this \u2014 it does not. Verified across four variants: a conditional provider loses the state, and adding a stable key to the child beneath it loses the state too. What keeps the state is having the provider element present in both branches, so the tree shape never changes.

The version that works moves the condition inside value, expressing \"not provided\" as the default rather than as an absent wrapper:

app.py Run
@component\ndef draft():\n    # State owned by the leaf. It survives as long as this component keeps the\n    # same position in the tree.\n    text, set_text = use_state(\"\")\n    body = use_ref(0)\n    body.current += 1\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n                \"placeholder\": \"type something\",\n            }\n        ),\n        html.p(\n            {\"id\": \"draft\"},\n            f\"draft={text!r} renders={body.current} theme={use_context(ThemeContext)}\",\n        ),\n    )\n\n\n@component\ndef panel():\n    provide, set_provide = use_state(True)\n    theme, set_theme = use_state(\"dark\")\n\n    async def toggle_wrap(event):\n        set_provide(not provide)\n\n    async def toggle_theme(event):\n        set_theme(\"light\" if theme == \"dark\" else \"dark\")\n\n    return html.div(\n        html.button({\"onClick\": toggle_wrap}, \"toggle provider\"),\n        html.button({\"onClick\": toggle_theme}, \"change value\"),\n        # The provider is always in the tree, so nothing below it is unmounted.\n        # \"Not provided\" is expressed as the default value, not as an absent\n        # wrapper.\n        ThemeContext(draft(), value=theme if provide else \"light\"),\n    )\n\n\n@component\ndef app():\n    return html.div(html.h3(\"Provider always present\"), panel())\n
# TODO\n

Type hello, press toggle provider: draft='hello' ... theme=light, and the render counter keeps counting. Same buttons, same user-visible behaviour as the broken version, state intact.

This is a special case of a rule that applies to all components, not just providers \u2014 see Preserving and Resetting State. Context makes it easier to hit by accident, because \"wrap this in a provider\" is a natural thing to put behind an if.

"},{"location":"learn/passing-data-deeply-with-context/#before-you-use-context","title":"Before you use context","text":"
  • Does a component only forward the prop? If it uses the value, pass a prop. If it is one level deep, pass a prop. Context pays off when the value crosses several levels that do not care about it.
  • Is the value genuinely shared? A value used by one subtree is simpler as a prop, and easier to trace when it is wrong.
  • Are you hoping it saves renders? It does not. ReactPy has no render bailout, so when the provider re-renders, everything below it re-renders too, whether or not the value changed. Context is a plumbing feature, never a performance one \u2014 see What context does not do.
  • Do you want to change a value from below? Then context is the wrong shape. Put the state in a common ancestor and pass the setter (or a dispatch) down with it \u2014 see Scaling Up with Reducer and Context.
"},{"location":"learn/passing-data-deeply-with-context/#recap","title":"Recap","text":"
  • Context replaces props that components only forward. It does not replace props a component actually uses.
  • Three steps: create_context(default) once at module level, use_context(Context) in the component that needs it, Context(children, value=...) above that subtree.
  • The default from create_context is what an unprovided reader gets \u2014 not None. Verified.
  • The nearest provider wins; a nested provider shadows the outer one, and siblings outside a provider see the default. Verified across four readers.
  • Call use_context in the component that uses the value. Formatting it inside the parent's arguments reads the parent's context.
  • A component cannot read the context it provides \u2014 verified reading the default while its own child read the provided value. Read the underlying state instead.
  • Keep the provider element in the tree and vary only its value. Conditionally wrapping in a provider unmounts the subtree and discards its state; a key does not prevent it (verified).
  • Context is plumbing only: no render bailout, no reactivity, no shared state. See Use Context for the parameter-level detail and error messages.
"},{"location":"learn/passing-data-deeply-with-context/#related","title":"Related","text":"
  • Use Context \u2014 parameters, caveats, error messages, and what context does not do
  • Scaling Up with Reducer and Context \u2014 sharing a reducer and its dispatch, the usual next step
  • Choosing the State Structure \u2014 what belongs in state at all, and where
  • Queueing a Series of State Updates \u2014 the updater form, when two handlers race
  • Reusing Logic with Custom Hooks \u2014 the other way to share behaviour, for logic rather than values
"},{"location":"learn/passing-props-to-a-component/","title":"Passing Props to a Component","text":""},{"location":"learn/passing-props-to-a-component/#overview","title":"Overview","text":"

ReactPy components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any Python value through them, including dictionaries, lists, and functions.

You will learn

  • How to pass props to a component
  • How to read props from a component
  • How to specify default values for props
  • How to pass some markup to a component
  • How props change over time
"},{"location":"learn/passing-props-to-a-component/#familiar-props","title":"Familiar props","text":"

Props are the information that you pass to a tag. For example, className, src, alt, width, and height are some of the props you can pass to an <img>:

app.pystyles.css Run
@component\ndef avatar():\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": \"https://i.imgur.com/1bX5QH6.jpg\",\n            \"alt\": \"Lin Lanying\",\n            \"width\": 100,\n            \"height\": 100,\n        }\n    )\n\n\n@component\ndef profile():\n    return avatar()\n\n\n@component\ndef app():\n    return profile()\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n

The props you can pass to an html.img tag are predefined (the browser conforms to the HTML standard), and they keep their familiar camelCase spelling because they are handed straight to the DOM. But you can pass any props to your own components, such as avatar(), to customize them. Here's how!

"},{"location":"learn/passing-props-to-a-component/#passing-props-to-a-component","title":"Passing props to a component","text":"

In this code, the profile() component isn't passing any props to its child component, avatar():

@component\ndef profile():\n    return avatar()\n

You can give avatar() some props in two steps.

"},{"location":"learn/passing-props-to-a-component/#step-1-pass-props-to-the-child-component","title":"Step 1: Pass props to the child component","text":"

First, pass some props to avatar(). For example, let's pass two props: person (a dictionary), and size (a number):

@component\ndef profile():\n    return html.div(\n        avatar(\n            person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"},\n            size=100,\n        ),\n    )\n

Unlike an HTML tag, a component you write takes its props as ordinary Python arguments. There is no attributes dictionary here \u2014 the values land directly in the function's parameters.

"},{"location":"learn/passing-props-to-a-component/#step-2-read-props-inside-the-child-component","title":"Step 2: Read props inside the child component","text":"

You read props by listing their names person, size directly after def avatar. This lets you use them inside the avatar() code, like you would with a variable.

@component\ndef avatar(person, size):\n    # person and size are available here\n    ...\n

Add some logic to avatar() that uses the person and size props for rendering, and you're done.

Now you can configure avatar() to render in many different ways with different props. Try tweaking the values!

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef profile():\n    return html.div(\n        avatar(\n            person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"},\n            size=100,\n        ),\n        avatar(\n            person={\"name\": \"Katsuko Saruhashi\", \"image_id\": \"YfeOqp2\"},\n            size=100,\n        ),\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n

Props let you think about parent and child components independently. For example, you can change the person or the size props inside profile() without having to think about how avatar() uses them. Similarly, you can change how avatar() uses these props, without looking at profile().

You can think of props like \"knobs\" that you can adjust. They serve the same role as arguments serve for functions\u2014in fact, props are the only argument to your component! A component is nothing but a Python function, and a function can only accept arguments.

Every argument is a prop

In JavaScript, a component receives a single props object which you then destructure into function Avatar({ person, size }). Python needs no such ceremony: the parameters are the props.

@component\ndef avatar(person, size): ...\n

is already the direct, Pythonic form of that idea. But it means every parameter you declare is a prop a caller can pass by name \u2014 so keep the signature of a component to the values it actually renders.

"},{"location":"learn/passing-props-to-a-component/#specifying-a-default-value-for-a-prop","title":"Specifying a default value for a prop","text":"

If you want to give a prop a default value to fall back on when no value is specified, put = and the default value right after the parameter:

@component\ndef avatar(person, size=100):\n    # ...\n

Now, if avatar(person=...) is rendered with no size prop, the size will be set to 100.

The default value is used only when the caller omits the argument entirely. If you pass size=None or size=0, the default value will not be used \u2014 Python applies defaults for missing arguments, not for \"empty\" ones.

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size=100):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef profile():\n    return html.div(\n        avatar(person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"}),\n        avatar(\n            person={\"name\": \"Katsuko Saruhashi\", \"image_id\": \"YfeOqp2\"},\n            size=200,\n        ),\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n
"},{"location":"learn/passing-props-to-a-component/#forwarding-props-with-keyword-argument-unpacking","title":"Forwarding props with keyword argument unpacking","text":"

Sometimes, passing props gets very repetitive:

@component\ndef profile(person, size, is_sepia, thick_border):\n    return html.div(\n        {\"className\": \"card\"},\n        avatar(\n            person=person,\n            size=size,\n            is_sepia=is_sepia,\n            thick_border=thick_border,\n        ),\n    )\n

There's nothing wrong with repetitive code\u2014it can be more legible. But at times you may value conciseness. Some components forward all of their props to their children, like this profile() does with avatar(). Because they don't use any of their props directly, it can make sense to use Python's keyword argument unpacking instead:

@component\ndef profile(**props):\n    return html.div({\"className\": \"card\"}, avatar(**props))\n

This forwards all of profile()'s props to the avatar() without listing each of their names. Inside the function, props is a plain dictionary, so you can also merge or filter it before forwarding.

Use unpacking with restraint. If you're using it in every other component, something is wrong. It hides the component's real interface, so neither your editor nor a reader can tell which props it accepts. Often, it indicates that you should split your components and pass children as arguments. More on that next!

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size=100):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef profile(**props):\n    return html.div({\"className\": \"card\"}, avatar(**props))\n\n\n@component\ndef app():\n    return profile(\n        person={\"name\": \"Lin Lanying\", \"image_id\": \"1bX5QH6\"},\n        size=100,\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n
"},{"location":"learn/passing-props-to-a-component/#passing-children-to-a-component","title":"Passing children to a component","text":"

It is common to nest built-in browser tags:

html.div(html.img())\n

Sometimes you'll want to nest your own components the same way:

card(avatar())\n

Anything you pass positionally lands in a *args parameter of the parent component, which is the ReactPy equivalent of React's children prop. For example, the card() component below collects its children and renders them inside a wrapper <div>:

app.pystyles.css Run
def get_image_url(person, size=\"s\"):\n    return f\"https://i.imgur.com/{person['image_id']}{size}.jpg\"\n\n\n@component\ndef avatar(person, size=100):\n    return html.img(\n        {\n            \"className\": \"avatar\",\n            \"src\": get_image_url(person),\n            \"alt\": person[\"name\"],\n            \"width\": size,\n            \"height\": size,\n        }\n    )\n\n\n@component\ndef card(*children):\n    return html.div({\"className\": \"card\"}, *children)\n\n\n@component\ndef profile():\n    return card(\n        avatar(person={\"name\": \"Katsuko Saruhashi\", \"image_id\": \"YfeOqp2\"}),\n    )\n
body {\n    min-height: 120px;\n}\n\n.avatar {\n    margin: 20px;\n    border-radius: 50%;\n}\n
# TODO\n

Try replacing the avatar() inside card() with some text to see how the card() component can wrap any nested content. It doesn't need to \"know\" what's being rendered inside of it. You will see this flexible pattern in many places.

You can think of a component with a *children parameter as having a \"hole\" that can be \"filled in\" by its parent components with arbitrary markup. You will often use children for visual wrappers: panels, grids, etc.

Keyword-only children

Because positional arguments become children, a component that accepts both should make its props keyword-only to avoid an ambiguous signature:

@component\ndef card(*children, title=None):\n    return html.div({\"className\": \"card\"}, title, *children)\n

Now title= can never be mistaken for a child.

"},{"location":"learn/passing-props-to-a-component/#how-props-change-over-time","title":"How props change over time","text":"

The clock() component below receives two props from its parent component: color and time.

Try changing the color in the select box below:

app.py Run
@component\ndef clock(color, time):\n    return html.h1({\"style\": {\"color\": color}}, time)\n\n\n@component\ndef app():\n    color, set_color = use_state(\"lightcoral\")\n    now, set_now = use_state(datetime.now(timezone.utc))\n\n    async def tick():\n        while True:\n            await asyncio.sleep(1)\n            set_now(datetime.now(timezone.utc))\n\n    use_async_effect(tick, dependencies=[])\n\n    async def handle_change(event):\n        set_color(await event.target.value)\n\n    return html.div(\n        html.p(\n            \"Pick a color: \",\n            html.select(\n                {\"value\": color, \"onChange\": handle_change},\n                html.option({\"value\": \"lightcoral\"}, \"lightcoral\"),\n                html.option({\"value\": \"midnightblue\"}, \"midnightblue\"),\n                html.option({\"value\": \"rebeccapurple\"}, \"rebeccapurple\"),\n            ),\n        ),\n        clock(color=color, time=now.strftime(\"%H:%M:%S\")),\n    )\n
# TODO\n

This example illustrates that a component may receive different props over time. Props are not always static! Here, the time prop changes every second, and the color prop changes when you select another color. Props reflect a component's data at any point in time, rather than only in the beginning.

However, props are immutable\u2014a term from computer science meaning \"unchangeable\". When a component needs to change its props (for example, in response to a user interaction or new data), it will have to \"ask\" its parent component to pass it different props\u2014a new set of arguments! ReactPy re-renders the child with those new values rather than editing the old ones in place.

Don't try to change props

When you need to respond to user input (like changing the selected color), you will need to \"set state\", which you can learn about in State: A Component's Memory. Assigning to a prop parameter inside a component only rebinds the local name; the parent's value is untouched, and the next render overwrites it anyway.

"},{"location":"learn/passing-props-to-a-component/#recap","title":"Recap","text":"
  • To pass props, add them as arguments to the component call, just like you would with an HTML tag's attributes.
  • To read props, list them as parameters: def avatar(person, size).
  • You can specify a default value like size=100, which is used when the caller omits the argument.
  • You can forward all props with def profile(**props) and avatar(**props), but don't overuse it!
  • Nested children like card(avatar()) arrive as a *children parameter.
  • Props are read-only snapshots in time: every render receives a new version of props.
  • You can't change props. When you need interactivity, you'll need to set state.
"},{"location":"learn/preserving-and-resetting-state/","title":"Preserving and Resetting State","text":""},{"location":"learn/preserving-and-resetting-state/#overview","title":"Overview","text":"

State is isolated between components. ReactPy keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders.

You will learn

  • How state is tied to a position in the tree
  • When state is preserved, and when it resets
  • How to reset a component's state on purpose, with position or with key
  • Why defining a component inside another one loses state
"},{"location":"learn/preserving-and-resetting-state/#state-is-tied-to-a-position-in-the-tree","title":"State is tied to a position in the tree","text":"

When you give a component state, you might think the state \"lives\" inside the component. It doesn't. ReactPy holds the state, and associates each piece of it with a component by where that component sits in the tree.

Here counter() is rendered at two different positions:

app.pystyles.css Run
@component\ndef counter():\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(score),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef app():\n    # The same component function, rendered at two different positions in the\n    # tree. ReactPy tracks state by position, so these two counters each get\n    # their own score.\n    return html.div(counter(), counter())\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

Click both counters and notice they don't affect each other. Each position gets its own state, so counter() is really two separate components here.

It makes no difference whether you build the child once and reuse it, or call it twice:

one_counter = counter()\nhtml.div(one_counter, one_counter)  # same as: html.div(counter(), counter())\n

Both render counter() at two positions, and both get independent state.

"},{"location":"learn/preserving-and-resetting-state/#same-component-at-the-same-position-preserves-state","title":"Same component at the same position preserves state","text":"

In this example a checkbox flips a prop on the counter, but there is always exactly one counter() at that position:

app.pystyles.css Run
@component\ndef counter(is_fancy):\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": f\"counter{' fancy' if is_fancy else ''}\"},\n        html.h1(score),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef app():\n    is_fancy, set_is_fancy = use_state(False)\n\n    async def handle_is_fancy_change(event):\n        set_is_fancy(await event.target.checked)\n\n    return html.div(\n        # Only one counter() is ever rendered at this position, and it is the\n        # same component function either way, so ReactPy keeps its state across\n        # the toggle; only the is_fancy prop changes.\n        counter(is_fancy),\n        html.label(\n            html.input(\n                {\n                    \"type\": \"checkbox\",\n                    \"checked\": is_fancy,\n                    \"onChange\": handle_is_fancy_change,\n                }\n            ),\n            \"Use fancy styling\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

Raise the score, then toggle \"Use fancy styling\". The score survives, because the same component stayed at the same position \u2014 only its is_fancy prop changed.

This is the default, and usually what you want.

"},{"location":"learn/preserving-and-resetting-state/#different-components-at-the-same-position-reset-state","title":"Different components at the same position reset state","text":"

Now switch the child to something else entirely:

app.pystyles.css Run
@component\ndef counter():\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(score),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef app():\n    is_paused, set_is_paused = use_state(False)\n\n    async def handle_is_paused_change(event):\n        set_is_paused(await event.target.checked)\n\n    return html.div(\n        # Switching between a counter() and an html.p() puts a *different* kind\n        # of child at this position, so ReactPy discards the counter's state\n        # and starts a brand new one when the break ends.\n        html.p(\"See you later!\") if is_paused else counter(),\n        html.label(\n            html.input(\n                {\n                    \"type\": \"checkbox\",\n                    \"checked\": is_paused,\n                    \"onChange\": handle_is_paused_change,\n                }\n            ),\n            \"Take a break\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

Raise the score, tick \"Take a break\", then untick it. The counter comes back at zero. Replacing the child with an html.p() destroyed the old state, and the returning counter() is a brand new one.

The rule so far: same component at the same position keeps state; anything else at that position does not.

"},{"location":"learn/preserving-and-resetting-state/#resetting-state-at-the-same-position","title":"Resetting state at the same position","text":"

Suppose a scoreboard shows one player's score at a time, and \"Next player\" swaps Taylor for Sarah:

counter(\"Taylor\") if is_player_a else counter(\"Sarah\")\n

Both branches call counter() at the same position, so ReactPy sees one component whose person prop changed \u2014 and the score follows the player over. That's wrong here: Taylor's score and Sarah's score are different things.

There are two ways to get them treated as separate components.

"},{"location":"learn/preserving-and-resetting-state/#option-1-render-them-at-different-positions","title":"Option 1: render them at different positions","text":"

Give each player a container that always exists:

app.pystyles.css Run
@component\ndef counter(person):\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(f\"{person}'s score: {score}\"),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef scoreboard():\n    is_player_a, set_is_player_a = use_state(True)\n\n    async def handle_is_player_a_click(event):\n        set_is_player_a(not is_player_a)\n\n    return html.div(\n        # Two containers that always exist, so each player's counter has its\n        # own position in the tree. Only one holds a counter at a time, and the\n        # container that loses it destroys that state.\n        #\n        # Do not \"simplify\" this to *([] if cond else [counter(...)]): an empty\n        # expansion contributes no children, so both counters end up at\n        # position 0 and ReactPy treats them as one component -- no reset.\n        html.div(counter(\"Taylor\") if is_player_a else None),\n        html.div(counter(\"Sarah\") if not is_player_a else None),\n        html.button(\n            {\"onClick\": handle_is_player_a_click},\n            \"Next player!\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n
  • At first the first html.div() holds Taylor's counter and the second is empty.
  • After \"Next player!\" the first is empty and the second holds Sarah's.

Each counter is destroyed when its container empties, so every switch starts from zero. This works nicely when there are only a couple of alternatives to lay out side by side.

An empty list expansion is not a placeholder

The tempting Python spelling of \"or nothing\" collapses the whole mechanism:

*([] if not is_player_a else [counter(\"Taylor\")]),  # wrong: no child at all\n

An empty expansion contributes no children, so the two counters land at position 0 and ReactPy treats them as one component \u2014 no reset, and Sarah inherits Taylor's score. It is the two html.div() containers above that create the positions. (React avoids this because false still occupies a JSX slot.)

"},{"location":"learn/preserving-and-resetting-state/#option-2-reset-state-with-a-key","title":"Option 2: reset state with a key","text":"

You have seen \"key\" when rendering lists. Keys are not only for lists. By default ReactPy identifies a child by its index within its parent \u2014 \"the first counter\", \"the second counter\" \u2014 and a key lets you say this is not the first counter, it is Taylor's counter.

app.pystyles.css Run
@component\ndef counter(person):\n    score, set_score = use_state(0)\n\n    async def handle_score_click(event):\n        set_score(score + 1)\n\n    return html.div(\n        {\"className\": \"counter\"},\n        html.h1(f\"{person}'s score: {score}\"),\n        html.button({\"onClick\": handle_score_click}, \"Add one\"),\n    )\n\n\n@component\ndef scoreboard():\n    is_player_a, set_is_player_a = use_state(True)\n\n    async def handle_is_player_a_click(event):\n        set_is_player_a(not is_player_a)\n\n    return html.div(\n        # Both branches render counter() at the same position, so without a key\n        # ReactPy would treat them as the same component and carry the score\n        # from one player to the next. The key gives each player their own\n        # identity, so switching players resets the score.\n        (\n            counter(\"Taylor\", key=\"Taylor\")\n            if is_player_a\n            else counter(\"Sarah\", key=\"Sarah\")\n        ),\n        html.button(\n            {\"onClick\": handle_is_player_a_click},\n            \"Next player!\",\n        ),\n    )\n
.counter {\n    width: 130px;\n    text-align: center;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 12px;\n    margin: 0 12px 12px 0;\n}\n\n.counter h1 {\n    font-size: 18px;\n    margin: 0 0 6px;\n}\n\n.fancy {\n    background: #ffffd8;\n    border-color: #e0b400;\n}\n
# TODO\n

The two counters no longer share state, even though they appear in the same place. Give \"key\" a value that identifies what the component is \u2014 the contact id, the player name \u2014 not where it sits.

Keys are only unique within their parent

Two components with the same key under different parents are fine: keys are not globally unique, they only identify a position within one parent.

Note also that an unkeyed child is identified by its index, which shares a namespace with explicit keys. html.div(contact_list(...), chat(to, key=0)) raises ValueError: Duplicate keys [0], because the unkeyed contact_list() already occupies position 0. Prefix your keys (key=f\"chat-{to['id']}\") or key all the siblings.

"},{"location":"learn/preserving-and-resetting-state/#resetting-a-form-with-a-key","title":"Resetting a form with a key","text":"

Keying is most useful for forms. In this chat app, chat() holds the half-written message:

app.pydata.pystyles.css Run
@component\ndef chat(contact):\n    draft, set_draft = use_state(\"\")\n\n    async def handle_draft_click(event):\n        set_draft(\"\")\n\n    return html.section(\n        {\"className\": \"chat\"},\n        html.h3(f\"To: {contact['name']} <{contact['email']}>\"),\n        html.textarea({\"placeholder\": \"Write a message\", \"value\": draft}),\n        html.button({\"onClick\": handle_draft_click}, \"Clear\"),\n    )\n\n\n@component\ndef contact_list(selected_contact, on_select):\n    def make_select(contact):\n        # A factory, not a lambda: a handler must be a coroutine, and the\n        # contact it acts on is bound by the closure.\n        async def choose(event):\n            on_select(contact)\n\n        return choose\n\n    return html.section(\n        {\"className\": \"contact-list\"},\n        html.ul(\n            [\n                html.li(\n                    {\"key\": contact[\"id\"]},\n                    html.button(\n                        {\"onClick\": make_select(contact)},\n                        contact[\"name\"],\n                    ),\n                )\n                for contact in contacts\n            ]\n        ),\n    )\n\n\n@component\ndef messenger():\n    to, set_to = use_state(contacts[0])\n\n    return html.div(\n        contact_list(to, set_to),\n        # Without the key, switching contacts keeps the half-written message,\n        # because chat() stays at the same position. The key ties the draft to\n        # the contact instead of the slot.\n        chat(to, key=f\"chat-{to['id']}\"),\n    )\n
contacts = [\n    {\"id\": 0, \"name\": \"Taylor\", \"email\": \"taylor@mail.com\"},\n    {\"id\": 1, \"name\": \"Alice\", \"email\": \"alice@mail.com\"},\n    {\"id\": 2, \"name\": \"Bob\", \"email\": \"bob@mail.com\"},\n]\n
.contact-list ul {\n    list-style: none;\n    padding: 0;\n    display: flex;\n    gap: 8px;\n}\n\n.chat {\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    padding: 10px;\n    margin-top: 10px;\n}\n\n.chat textarea {\n    display: block;\n    width: 100%;\n    min-height: 60px;\n}\n
# TODO\n

Type a draft, then pick another contact. Without the key the draft would follow you over, because chat() never left its position. With key=f\"chat-{to['id']}\" the draft belongs to the conversation rather than the slot.

"},{"location":"learn/preserving-and-resetting-state/#keeping-state-for-a-component-that-is-no-longer-rendered","title":"Keeping state for a component that is no longer rendered","text":"

Resetting is often not what you want \u2014 a real chat app should restore the draft when you go back to Alice. Keying does not do that: an unmounted component loses its state, so returning to Taylor starts at zero. Options:

  • Render all the chats and hide the inactive ones with CSS. Nothing is unmounted, so state survives. Great for small UIs, wasteful when the hidden trees are large.
  • Lift the state up and keep each recipient's draft in the parent, in a dict keyed by recipient. Children coming and going stops mattering. This is the most common answer.
  • Keep it outside ReactPy entirely \u2014 a database, or localStorage in the browser \u2014 which you want anyway if a draft should survive a page reload.

Whichever you choose, a chat with Alice is conceptually different from a chat with Bob, so it still deserves a distinct key.

"},{"location":"learn/preserving-and-resetting-state/#nested-component-definitions-reset-state-accidentally","title":"Nested component definitions reset state accidentally","text":"

This one bites quietly:

@component\ndef outer():\n    @component\n    def nested():\n        value, set_value = use_state(...)\n        ...\n\n    return html.div(nested())\n

Every render of outer() creates a new function object for nested(). A new function is a new component, so ReactPy discards the old state and mounts a fresh one on every render \u2014 your state resets itself, with no warning. Measured: a nested child's mount counter climbs by one on each parent render, while a module-level child mounted once.

Define components at module level, or pass data through props.

"},{"location":"learn/preserving-and-resetting-state/#recap","title":"Recap","text":"
  • ReactPy keeps state for as long as the same component is rendered at the same position.
  • State is not stored in the call that produced the VDOM; it belongs to the tree position you put it in.
  • Force a subtree to reset by rendering it at a different position, or by giving it a different \"key\".
  • Unkeyed siblings are identified by index, in the same key namespace as explicit keys \u2014 prefix your keys to avoid ValueError: Duplicate keys.
  • Don't define a component inside another component, or you'll reset state by accident.
"},{"location":"learn/python-in-psx-with-curly-braces/","title":"Python in psx with curly braces","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

See this issue for more details.

"},{"location":"learn/queueing-a-series-of-state-updates/","title":"Queueing a Series of State Updates","text":""},{"location":"learn/queueing-a-series-of-state-updates/#overview","title":"Overview","text":"

Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render. To do this, it helps to understand how ReactPy batches state updates.

You will learn

  • How ReactPy batches state updates
  • Updating the same state multiple times before the next render
  • What happens when you mix values and updater functions
"},{"location":"learn/queueing-a-series-of-state-updates/#reactpy-batches-state-updates","title":"ReactPy batches state updates","text":"

You might expect that clicking the \"+3\" button will increment the counter three times, because it calls set_number(number + 1) three times:

app.pystyles.css Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(number + 1)\n        set_number(number + 1)\n        set_number(number + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+3\"),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n
# TODO\n

It lands on 1, not 3. Two things combine to produce that.

First, as you may recall from State as a Snapshot, each render's state values are fixed. Inside this render's event handler number is always 0, no matter how many times you call the setter, so all three lines evaluate to the same call:

set_number(0 + 1)\nset_number(0 + 1)\nset_number(0 + 1)\n

Second, a handler which does not await never yields, so nothing can render until it returns. ReactPy's setter applies its value immediately and only flags the component for re-render (core/hooks.py, _CurrentState.dispatch), and the flag is deduplicated \u2014 a component already flagged is not scheduled twice (core/_life_cycle_hook.py, LifeCycleHook.schedule_render and its _scheduled_render guard). Three calls to the setter inside one such handler therefore leave one render queued, showing 1.

This lets you update several state variables \u2014 even across several components \u2014 without triggering a render for each. It also avoids \"half-finished\" renders in which only some variables have been updated.

Batching stops at an await

The batching above belongs to a handler that never yields, and it is narrower than React's. Because the values are already stored, an await hands the event loop to the queued render task \u2014 which clears the flag, so the next setter schedules a second render. Nothing about the handler is special; the only difference is who holds the loop. Since every handler is a coroutine, the distinction is not def versus async def but whether anything is awaited between the setter calls.

Measured in a real browser, watching every value the counter takes after a single click:

Handler Observed sequence three set_number(lambda n: n + 1) with no await between them 0 \u2192 3 (one render) the same, with await asyncio.sleep(0.3) between the calls 0 \u2192 1 \u2192 2 \u2192 3 (three renders)

React 18+ would batch both into a single render. In ReactPy, an async def handler that awaits between updates renders \u2014 and repaints \u2014 in between. If that flicker matters, do the awaits first and set state once at the end, or collect the results and make a single setter call.

ReactPy does not batch across multiple intentional events like clicks \u2014 each click is handled separately. This ensures that, for example, if the first click disables a form, the second click cannot submit it again.

"},{"location":"learn/queueing-a-series-of-state-updates/#updating-the-same-state-multiple-times-before-the-next-render","title":"Updating the same state multiple times before the next render","text":"

It is an uncommon use case, but if you would like to update the same state variable multiple times before the next render, pass a function that computes the next value from the previous one instead of the value itself: set_number(lambda n: n + 1). This is a way of saying \"do something with the current state\" rather than \"replace the state\".

app.pystyles.css Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(lambda n: n + 1)\n        set_number(lambda n: n + 1)\n        set_number(lambda n: n + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+3\"),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n
# TODO\n

This time the counter reaches 3, because each updater receives whatever the previous one produced.

"},{"location":"learn/queueing-a-series-of-state-updates/#what-happens-if-you-update-state-after-replacing-it","title":"What happens if you update state after replacing it","text":"

What does this handler produce?

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        # number is 0 for this render, so this reads as set_number(5).\n        set_number(number + 5)\n        set_number(lambda n: n + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"Increase the number\"),\n    )\n
# TODO\n

Step by step, starting from number == 0:

  1. set_number(number + 5) \u2014 number is 0, so this is set_number(5). The state becomes 5.
  2. set_number(lambda n: n + 1) \u2014 an updater, so it runs against the current value: 5 + 1.

The next render shows 6, confirmed by rendering the example.

set_number(5) behaves like set_number(lambda n: 5)

A plain value ignores the argument entirely \u2014 it just replaces. That equivalence is what makes the next case surprising.

"},{"location":"learn/queueing-a-series-of-state-updates/#what-happens-if-you-replace-state-after-updating-it","title":"What happens if you replace state after updating it","text":"

One more:

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(number + 5)\n        set_number(lambda n: n + 1)\n        set_number(42)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"Increase the number\"),\n    )\n
# TODO\n
  1. set_number(number + 5) \u2014 number is 0, so state becomes 5.
  2. set_number(lambda n: n + 1) \u2014 updater, so 5 + 1 = 6.
  3. set_number(42) \u2014 a plain value, so state becomes 42.

The result is 42. The two updaters still ran, and their work was then overwritten \u2014 which is the same answer React's queue produces, arrived at by a different route.

To summarize what you can pass to a state setter:

  • An updater function (e.g. lambda n: n + 1) is applied to the current value.
  • Any other value (e.g. 5) replaces the state outright, discarding whatever the updaters produced.

Updater functions run while your handler runs, inside the setter call itself, so \u2014 like components \u2014 they must be pure. Don't set state from inside one or perform side effects there. They are eager, not deferred, which means the one thing they can still get wrong is the closure: a name the updater reads from the enclosing render is the value that render saw, not the current one. The updater argument is the only value guaranteed fresh, so take everything you need from n.

An updater that raises fails silently

Updaters run eagerly, inside the setter call itself (core/hooks.py, _CurrentState.dispatch calls new(self.value) before scheduling anything) \u2014 not later during rendering. Verified: an updater's side effect lands before the line after its set_number(...).

That has a nasty consequence. If an updater raises, the state is never assigned and no render is scheduled; ReactPy logs Failed to execute event handler on the server and swallows the error. The browser shows nothing, the console stays quiet, and the component simply stops responding to that button. Reproduced: a lambda x: 1 / 0 updater produced a server-side traceback and then an indefinite absence of any render.

Two habits follow: keep updaters to one pure expression, and when a counter \"stops working\" with no error in the browser, check the server log for Failed to execute event handler.

"},{"location":"learn/queueing-a-series-of-state-updates/#naming-conventions","title":"Naming conventions","text":"

Name the updater's argument after the state variable it updates. Python has no single-letter convention as strong as JavaScript's, but the intent is the same \u2014 the argument is the previous value:

set_enabled(lambda enabled: not enabled)\nset_last_name(lambda last_name: last_name[::-1])\nset_friend_count(lambda friend_count: friend_count * 2)\n

For clarity, prefer a descriptive name over n: lambda previous: previous + 1 reads better in a handler that also does other work.

"},{"location":"learn/queueing-a-series-of-state-updates/#recap","title":"Recap","text":"
  • Setting state does not change the variable in the existing render; it requests a new render.
  • ReactPy processes state updates after a handler which never awaits has finished. That is batching.
  • Batching does not survive an await inside an async def handler \u2014 each await can flush a render with partial updates.
  • To update the same state several times in one handler, use updater functions: set_number(lambda n: n + 1).
  • A plain value replaces the state and discards earlier updater results; an updater is applied to the current value.
  • Updater functions must be pure, and ReactPy will not check them for you.
"},{"location":"learn/quick-start/","title":"Quick Start","text":""},{"location":"learn/quick-start/#overview","title":"Overview","text":"

Welcome to the ReactPy documentation! This page will give you an introduction to the 80% of React concepts that you will use on a daily basis.

You will learn

  • How to create and nest components
  • How to add markup and styles
  • How to display data
  • How to render conditions and lists
  • How to respond to events and update the screen
  • How to share data between components
"},{"location":"learn/quick-start/#creating-and-nesting-components","title":"Creating and nesting components","text":"

React apps are made out of components. A component is a piece of the UI (user interface) that has its own logic and appearance. A component can be as small as a button, or as large as an entire page.

React components are Python functions that return markup:

@component\ndef my_button():\n    return html.button(\"I'm a button!\")\n

Now that you've declared my_button, you can nest it into another component:

@component\ndef my_app():\n    return html.div(\n        html.h1(\"Welcome to my app\"),\n        my_button(),\n    )\n

Have a look at the result:

app.py Run
from reactpy import component, html\n\n\n@component\ndef my_button():\n    return html.button(\"I'm a button!\")\n\n\n@component\ndef my_app():\n    return html.div(\n        html.h1(\"Welcome to my app\"),\n        my_button(),\n    )\n
# TODO\n
"},{"location":"learn/quick-start/#adding-styles","title":"Adding styles","text":"

In React, you specify a CSS class with className. It works the same way as the HTML class attribute:

html.img({\"className\": \"avatar\"})\n

Then you write the CSS rules for it in a separate CSS file:

/* In your CSS */\n.avatar {\n    border-radius: 50%;\n}\n

React does not prescribe how you add CSS files. In the simplest case, you'll add a <link> tag to your HTML. If you use a build tool or web framework, consult its documentation to learn how to add a CSS file to your project.

"},{"location":"learn/quick-start/#displaying-data","title":"Displaying data","text":"

You can fetch data from a variety of sources and directly embed it into your components. You can also use the style attribute when your styles depend on JavaScript variables.

app.pystyles.css Run
from reactpy import component, html\n\nuser = {\n    \"name\": \"Hedy Lamarr\",\n    \"image_url\": \"https://i.imgur.com/yXOvdOSs.jpg\",\n    \"image_size\": 90,\n}\n\n\n@component\ndef profile():\n    return html.div(\n        html.h3(user[\"name\"]),\n        html.img(\n            {\n                \"className\": \"avatar\",\n                \"src\": user[\"image_url\"],\n                \"alt\": f\"Photo of {user['name']}\",\n                \"style\": {\n                    \"width\": user[\"image_size\"],\n                    \"height\": user[\"image_size\"],\n                },\n            }\n        ),\n    )\n
.avatar {\n    border-radius: 50%;\n}\n\n.large {\n    border: 4px solid gold;\n}\n
# TODO\n
"},{"location":"learn/quick-start/#conditional-rendering","title":"Conditional rendering","text":"

In React, there is no special syntax for writing conditions. Instead, you'll use the same techniques as you use when writing regular Python code. For example, you can use an if statement to conditionally include components:

@component\ndef my_component():\n    if is_logged_in:\n        content = admin_panel()\n    else:\n        content = login_form()\n    return html.div(content)\n

If you prefer more compact code, you can use the ternary operator.:

@component\ndef my_component():\n    return html.div(admin_panel() if is_logged_in else login_form())\n

When you don't need the else branch, unpack a list that is either one item or empty \u2014 and not Python's logical and, which React's cond && <X/> habit tempts people into writing:

@component\ndef my_component():\n    # No `else` branch needed: unpack a one-item list, or an empty one.\n    # Unpack the *component*, never the dict it returns -- `*admin_panel()`\n    # would iterate the dict and emit its keys as text.\n    # `is_logged_in and admin_panel()` would render the word \"False\".\n    return html.div(*([admin_panel()] if is_logged_in else []))\n

is_logged_in and admin_panel() returns False when the test fails, and ReactPy renders that as the literal word False on the page \u2014 only None and an absent child disappear. See Conditional Rendering for the same trap with numbers.

All of these approaches also work for conditionally specifying attributes. If you're unfamiliar with some of this Python syntax, you can start by always using if...else.

"},{"location":"learn/quick-start/#rendering-lists","title":"Rendering lists","text":"

You will rely on Python features like for loop and list comprehension to render lists of components.

For example, let's say you have an array of products:

products = [\n    {\"title\": \"Cabbage\", \"id\": 1},\n    {\"title\": \"Garlic\", \"id\": 2},\n    {\"title\": \"Apple\", \"id\": 3},\n]\n

Inside your component, use list comprehension to transform an array of products into an array of <li> items:

list_items = [html.li({\"key\": product[\"id\"]}, product[\"title\"]) for product in products]\n

Notice how <li> has a key attribute. For each item in a list, you should pass a string or a number that uniquely identifies that item among its siblings. Usually, a key should be coming from your data, such as a database ID. React uses your keys to know what happened if you later insert, delete, or reorder the items.

app.py Run
from reactpy import component, html\n\nproducts = [\n    {\"title\": \"Cabbage\", \"is_fruit\": False, \"id\": 1},\n    {\"title\": \"Garlic\", \"is_fruit\": False, \"id\": 2},\n    {\"title\": \"Apple\", \"is_fruit\": True, \"id\": 3},\n]\n\n\n@component\ndef shopping_list():\n    list_items = [\n        html.li(\n            {\n                \"key\": product[\"id\"],\n                \"style\": {\"color\": \"magenta\" if product[\"is_fruit\"] else \"darkgreen\"},\n            },\n            product[\"title\"],\n        )\n        for product in products\n    ]\n\n    return html.ul(list_items)\n
# TODO\n
"},{"location":"learn/quick-start/#responding-to-events","title":"Responding to events","text":"

You can respond to events by declaring event handler functions inside your components:

@component\ndef my_button():\n    async def handle_click(event):\n        print(\"You clicked me!\")\n\n    return html.button(\n        {\"onClick\": handle_click},\n        \"Click me\",\n    )\n

Notice how \"onClick\": handle_click has no parentheses at the end! Do not call the event handler function: you only need to pass it down. React will call your event handler when the user clicks the button.

"},{"location":"learn/quick-start/#updating-the-screen","title":"Updating the screen","text":"

Often, you'll want your component to \"remember\" some information and display it. For example, maybe you want to count the number of times a button is clicked. To do this, add state to your component.

First, import use_state from reactpy:

from reactpy import use_state\n

Now you can declare a state variable inside your component:

@component\ndef my_button():\n    count, set_count = use_state(0)\n    # ...\n

You\u2019ll get two things from use_state: the current state (count), and the function that lets you update it (set_count). You can give them any names, but the convention is to write something, set_something = ....

The first time the button is displayed, count will be 0 because you passed 0 to use_state(). When you want to change state, call set_count() and pass the new value to it. Clicking this button will increment the counter:

@component\ndef my_button():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.button({\"onClick\": handle_click}, f\"Clicked {count} times\")\n

React will call your component function again. This time, count will be 1. Then it will be 2. And so on.

If you render the same component multiple times, each will get its own state. Click each button separately:

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef my_app():\n    return html.div(\n        html.h1(\"Counters that update separately\"),\n        my_button(),\n        my_button(),\n    )\n\n\n@component\ndef my_button():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.button({\"onClick\": handle_click}, f\"Clicked {count} times\")\n
button {\n    display: block;\n    margin-bottom: 5px;\n}\n
# TODO\n

Notice how each button \"remembers\" its own count state and doesn't affect other buttons.

"},{"location":"learn/quick-start/#using-hooks","title":"Using Hooks","text":"

Functions starting with use are called Hooks. use_state is a built-in Hook provided by ReactPy. You can find other built-in Hooks in the API reference. You can also write your own Hooks by combining the existing ones.

Hooks are more restrictive than other functions. You can only call Hooks at the top of your components (or other Hooks). If you want to use use_state in a condition or a loop, extract a new component and put it there.

"},{"location":"learn/quick-start/#sharing-data-between-components","title":"Sharing data between components","text":"

In the previous example, each my_button had its own independent count, and when each button was clicked, only the count for the button clicked changed:

However, often you'll need components to share data and always update together.

To make both my_button components display the same count and update together, you need to move the state from the individual buttons \"upwards\" to the closest component containing all of them.

In this example, it is my_app.

Now when you click either button, the count in my_app will change, which will change both of the counts in my_button. Here's how you can express this in code.

First, move the state up from my_button into my_app:

@component\ndef my_app():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(\"Counters that update separately\"),\n        my_button(),\n        my_button(),\n    )\n\n\n@component\ndef my_button(): ...  # this code is being moved up into `my_app`\n

Then, pass the state down from my_app to each my_button, together with the shared click handler. You can pass information to my_button using props:

@component\ndef my_app():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(\"Counters that update together\"),\n        my_button(count, handle_click),\n        my_button(count, handle_click),\n    )\n

The information you pass down like this is called props. Now the my_app component contains the count state and the handle_click event handler, and passes both of them down as props to each of the buttons.

Finally, change my_button to read the props you have passed from its parent component:

@component\ndef my_button(count, on_click):\n    return html.button({\"onClick\": on_click}, f\"Clicked {count} times\")\n

When you click the button, the on_click handler fires. Each button's on_click prop was set to the handle_click function inside my_app, so the code inside of it runs. That code calls set_count(count + 1), incrementing the count state variable. The new count value is passed as a prop to each button, so they all show the new value. This is called \"lifting state up\". By moving state up, you've shared it between components.

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef my_app():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(\"Counters that update together\"),\n        my_button(count, handle_click),\n        my_button(count, handle_click),\n    )\n\n\n@component\ndef my_button(count, on_click):\n    return html.button({\"onClick\": on_click}, f\"Clicked {count} times\")\n
button {\n    display: block;\n    margin-bottom: 5px;\n}\n
# TODO\n
"},{"location":"learn/quick-start/#next-steps","title":"Next Steps","text":"

By now, you know the basics of how to write React code!

Check out the Tutorial to put them into practice and build your first mini-app with React.

"},{"location":"learn/react-developer-tools/","title":"React developer tools","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

See this issue for more details.

"},{"location":"learn/reacting-to-input-with-state/","title":"Reacting to Input with State","text":""},{"location":"learn/reacting-to-input-with-state/#overview","title":"Overview","text":"

ReactPy provides a declarative way to manipulate the UI. Instead of manipulating individual pieces of the UI directly, you describe the different states your component can be in, and switch between them in response to the user input. This is similar to how designers think about the UI.

You will learn

  • How declarative UI programming differs from imperative UI programming
  • How to enumerate the different visual states your component can be in
  • How to trigger the changes between the different visual states from code
"},{"location":"learn/reacting-to-input-with-state/#how-declarative-ui-compares-to-imperative","title":"How declarative UI compares to imperative","text":"

When you design UI interactions, you probably think about how the UI changes in response to user actions. Consider a form that lets the user submit an answer:

  • When you type something into the form, the \"Submit\" button becomes enabled.
  • When you press \"Submit\", both the form and the button become disabled, and a spinner appears.
  • If the request succeeds, the form gets hidden, and a \"Thank you\" message appears.
  • If the request fails, an error message appears, and the form becomes enabled again.

In imperative programming, the above corresponds directly to how you implement interaction. You have to write the exact instructions to manipulate the UI depending on what just happened. Here's another way to think about this: imagine riding next to someone in a car and telling them turn by turn where to go. They don't know where you want to go, they just follow your commands. (And if you get the directions wrong, you end up in the wrong place!) It's called imperative because you have to \"command\" each element, from the spinner to the button, telling the computer how to update the UI:

async function handleFormSubmit(e) {\n    e.preventDefault();\n    disable(textarea);\n    disable(button);\n    show(loadingMessage);\n    hide(errorMessage);\n    try {\n        await submitForm(textarea.value);\n        show(successMessage);\n        hide(form);\n    } catch (err) {\n        show(errorMessage);\n        errorMessage.textContent = err.message;\n    } finally {\n        hide(loadingMessage);\n        enable(textarea);\n        enable(button);\n    }\n}\n\nfunction handleTextareaChange() {\n    if (textarea.value.length === 0) {\n        disable(button);\n    } else {\n        enable(button);\n    }\n}\n\nfunction hide(el) {\n    el.style.display = \"none\";\n}\n\nfunction show(el) {\n    el.style.display = \"\";\n}\n\nfunction enable(el) {\n    el.disabled = false;\n}\n\nfunction disable(el) {\n    el.disabled = true;\n}\n

Manipulating the UI imperatively works well enough for isolated examples, but it gets exponentially more difficult to manage in more complex systems. Adding a new UI element or a new interaction would require carefully checking all existing code to make sure you haven't introduced a bug (for example, forgetting to show or hide something).

ReactPy was built to solve this problem.

In ReactPy, you don't directly manipulate the UI \u2014 meaning you don't enable, disable, show, or hide components directly. Instead, you declare what you want to show, and ReactPy figures out how to update the UI.

This is less a stylistic preference than a physical constraint: a ReactPy server component has no DOM to manipulate. Your code runs on the server and returns a description of the UI; there is no element object to set .style.display on. See Manipulating the DOM with Refs for the one narrow escape hatch and why it rarely helps.

"},{"location":"learn/reacting-to-input-with-state/#thinking-about-ui-declaratively","title":"Thinking about UI declaratively","text":"

You've learned that CSS enables declarative string-based styling so you can describe what to show, not how to show it. You can also learn to think about your ReactPy components declaratively.

ReactPy stands at the intersection of design and computer science, so both of these ideas are sources of inspiration. To think declaratively, take each possible state of a component's UI, break it down into a list, and then write code to describe each state at once.

"},{"location":"learn/reacting-to-input-with-state/#step-1-identify-your-components-different-visual-states","title":"Step 1: Identify your component's different visual states","text":"

First, enumerate the visual states of the UI the user could see:

  1. Empty \u2014 the form has no answer yet.
  2. Typing \u2014 the form has a non-empty answer.
  3. Submitting \u2014 the answer has just been submitted and nothing else can be clicked.
  4. Success \u2014 the user wants to see a \"Correct!\" message instead of the form.
  5. Error \u2014 like Typing, but with an error message.

Replace the form() component with a mock that returns the right UI based on a status prop:

app.pystyles.css Run
@component\ndef form(status=\"empty\"):\n    \"\"\"A mock: the visual states are driven by a prop, not by real logic.\n\n    Rendering this with each status is the fastest way to enumerate the states\n    before writing any interaction code.\n    \"\"\"\n    if status == \"success\":\n        return html.h1(\"That's right!\")\n\n    return html.div(\n        html.h2(\"City quiz\"),\n        html.p(\n            \"In which city is there a billboard that turns air into drinkable water?\"\n        ),\n        html.form(\n            html.textarea({\"disabled\": status == \"submitting\"}),\n            html.br(),\n            html.button(\n                {\"disabled\": status in {\"empty\", \"submitting\"}},\n                \"Submit\",\n            ),\n            (\n                html.p(\n                    {\"className\": \"Error\"}, \"Good guess but a wrong answer. Try again!\"\n                )\n                if status == \"error\"\n                else None\n            ),\n        ),\n    )\n
.Error {\n    color: red;\n}\n\ntextarea {\n    width: 260px;\n    height: 60px;\n}\n\nsection {\n    border-bottom: 1px solid #ddd;\n    padding-bottom: 10px;\n    margin-bottom: 10px;\n}\n\nh4 {\n    margin: 4px 0;\n    color: #888;\n}\n
# TODO\n

The status prop is a \"mock\" of the state inside the component, and controls which visual states the user will see. This mock is controlled by a prop called status with a default value of \"empty\".

You could call that prop anything you like, the naming is not important. Try changing the default to status=\"success\" to see the success message appear. Mocking lets you quickly iterate on the UI before you wire up any logic.

Two details in that mock are worth pointing out. \"Typing\" and \"Submitting\" render the same layout, differing only in whether the controls are disabled \u2014 so the difference is expressed as data, not as a different branch of markup:

html.button({\"disabled\": status in {\"empty\", \"submitting\"}}, \"Submit\")\n

And the error state deliberately keeps the form enabled. A state that showed an error while also disabling the input would leave the user unable to correct their answer \u2014 imperative code has to enforce that by hand, and it is exactly the kind of thing that gets forgotten.

Displaying many visual states at once

You could try each state by editing the source, but then you'd navigate back and forth many times. It's often easier to show many visual states at once, like a \"living styleguide\" or \"storybook\":

app.pystyles.css Run
@component\ndef form(status=\"empty\"):\n    if status == \"success\":\n        return html.h1(\"That's right!\")\n\n    return html.div(\n        html.h2(\"City quiz\"),\n        html.p(\n            \"In which city is there a billboard that turns air into drinkable water?\"\n        ),\n        html.form(\n            html.textarea({\"disabled\": status == \"submitting\"}),\n            html.br(),\n            html.button(\n                {\"disabled\": status in {\"empty\", \"submitting\"}},\n                \"Submit\",\n            ),\n            (\n                html.p(\n                    {\"className\": \"Error\"}, \"Good guess but a wrong answer. Try again!\"\n                )\n                if status == \"error\"\n                else None\n            ),\n        ),\n    )\n\n\n@component\ndef app():\n    \"\"\"Render every visual state at once -- a \"living styleguide\".\n\n    Because the mock is driven by a prop, all five states can sit side by side\n    in one page and be compared without clicking anything.\n    \"\"\"\n    statuses = [\"empty\", \"typing\", \"submitting\", \"success\", \"error\"]\n    return html.div(\n        [\n            html.section(\n                {\"key\": status},\n                html.h4(status),\n                form(status=status),\n            )\n            for status in statuses\n        ]\n    )\n
.Error {\n    color: red;\n}\n\ntextarea {\n    width: 260px;\n    height: 60px;\n}\n\nsection {\n    border-bottom: 1px solid #ddd;\n    padding-bottom: 10px;\n    margin-bottom: 10px;\n}\n\nh4 {\n    margin: 4px 0;\n    color: #888;\n}\n
# TODO\n

Because the mock is driven by a prop, all five states mount side by side and can be compared without a single click. Each html.section() carries a \"key\" because they are children of a list.

"},{"location":"learn/reacting-to-input-with-state/#step-2-determine-what-triggers-those-state-changes","title":"Step 2: Determine what triggers those state changes","text":"

There are two categories of \"cause\" that can change state in a component:

  • Input (the user typing, clicking, changing a field, and so on)
  • Output (one of the component's effects completing \u2014 a timer firing, a request finishing, and so on)

In both cases, you must set state variables to update the UI.

  • Changing the text input (human) should switch it from the Empty state to the Typing state or back, depending on whether the text box is empty or not.
  • Clicking the Submit button (human) should switch it to the Submitting state.
  • A successful response (computer) should switch it to the Success state.
  • A failed response (computer) should switch it to the Error state with the matching error message.

Notice that human inputs often require event handlers!

"},{"location":"learn/reacting-to-input-with-state/#step-3-represent-the-state-in-memory-with-use_state","title":"Step 3: Represent the state in memory with use_state","text":"

Next you'll need to represent the visual states of your component in memory with use_state.

Simplicity is key: each piece of state is a \"moving piece\", and you want as few \"moving pieces\" as possible. More complexity leads to more bugs!

Start with the state that everyone agrees on:

answer, set_answer = use_state(\"\")\nerror, set_error = use_state(\"\")\n

Then you'll need a state variable representing which of the visual states to display. There's usually more than a single way to represent that in memory, so you'll need to experiment with it. Here's one attempt \u2014 a boolean per state:

is_empty, set_is_empty = use_state(True)\nis_typing, set_is_typing = use_state(False)\nis_submitting, set_is_submitting = use_state(False)\nis_success, set_is_success = use_state(False)\nis_error, set_is_error = use_state(False)\n
"},{"location":"learn/reacting-to-input-with-state/#step-4-remove-any-non-essential-state-variables","title":"Step 4: Remove any non-essential state variables","text":"

You want to avoid duplication in the state content so you're only tracking what is essential. Spending a little time refactoring your state structure will make your components easier to understand, reduce duplication, and avoid unintended meanings. Your goal is to prevent the cases where the state in memory doesn't represent any valid UI that you'd want a user to see.

Take those five booleans:

  • is_typing and is_submitting can't both be True. A paradox usually means the state is not constrained enough. There are four possible combinations of two booleans, but only three correspond to valid states. To remove the \"impossible\" state, combine them into a status that must be one of three values: \"typing\", \"submitting\", or \"success\".
  • Another paradox: is_empty and is_typing can't both be True. Keeping them as separate state variables risks them going out of sync and causing bugs. Fortunately you can drop is_empty and check len(answer) == 0 instead.
  • is_error is not needed, because you can check if error instead.

After this clean-up you're left with 3 (down from 7!) essential state variables:

answer, set_answer = use_state(\"\")\nerror, set_error = use_state(\"\")\nstatus, set_status = use_state(\"typing\")  # 'typing', 'submitting', or 'success'\n

You know they are essential, because you can't remove any of them without breaking the functionality.

Eliminating 'impossible' states with a reducer

These three variables are a good enough representation of this form's state. However, some intermediate states still don't fully make sense. A non-empty error doesn't make sense when status is \"success\". Reducers let you unify multiple state variables into a single object and consolidate all the related logic!

"},{"location":"learn/reacting-to-input-with-state/#step-5-connect-the-event-handlers-to-set-state","title":"Step 5: Connect the event handlers to set state","text":"

Finally, connect the event handlers to the set functions from Step 4:

app.pystyles.css Run
@component\ndef form():\n    answer, set_answer = use_state(\"\")\n    error, set_error = use_state(\"\")\n    status, set_status = use_state(\"typing\")  # 'typing', 'submitting', or 'success'\n\n    if status == \"success\":\n        return html.h1(\"That's right!\")\n\n    async def handle_submit(event):\n        # Awaiting inside the handler is fine: ReactPy still suppresses the\n        # form's default navigation, because it detects the preventDefault()\n        # call by reading this function's bytecode -- whether or not the handler\n        # ever finishes does not matter. The call must appear in this function\n        # itself, though: hiding it in a helper defeats the inspection.\n        event.preventDefault()\n        set_status(\"submitting\")\n        try:\n            await submit_form(answer)\n        except ValueError as exc:\n            set_status(\"typing\")\n            set_error(str(exc))\n        else:\n            set_status(\"success\")\n\n    async def handle_textarea_change(event):\n        set_answer(await event.target.value)\n\n    return html.div(\n        html.h2(\"City quiz\"),\n        html.p(\n            \"In which city is there a billboard that turns air into drinkable water?\"\n        ),\n        html.form(\n            {\"onSubmit\": handle_submit},\n            html.textarea(\n                {\n                    \"value\": answer,\n                    \"onChange\": handle_textarea_change,\n                    \"disabled\": status == \"submitting\",\n                }\n            ),\n            html.br(),\n            html.button(\n                {\n                    \"disabled\": len(answer) == 0 or status == \"submitting\",\n                    \"type\": \"submit\",\n                },\n                \"Submit\",\n            ),\n            html.p({\"className\": \"Error\"}, error) if error else None,\n        ),\n    )\n
.Error {\n    color: red;\n}\n\ntextarea {\n    width: 260px;\n    height: 60px;\n}\n\nsection {\n    border-bottom: 1px solid #ddd;\n    padding-bottom: 10px;\n    margin-bottom: 10px;\n}\n\nh4 {\n    margin: 4px 0;\n    color: #888;\n}\n
# TODO\n

Three things are worth noticing.

The handlers only set state. You never say \"hide the textarea\" or \"enable the button\". The disabled attributes and the error paragraph are computed from status and error on every render, so it's impossible to end up in a state where one was updated and the other wasn't. Submitting a wrong answer really does lock the form, then bring back the form with the error message and the input usable again.

The error is stored as a string, not an exception. React's version keeps an Error object and reads .message off it. In Python, store the message \u2014 str(exc) \u2014 and render it directly.

event.preventDefault() matters here, and ReactPy reads your code to find it. Without it the browser navigates away and your app is gone. Detection happens by inspecting the handler's code rather than at call time, so an async def handler that calls event.preventDefault() before its await is still detected. What defeats it is indirection: rebinding the argument (e = event) or delegating to a helper hides the call from the handler's own code, and the default is not prevented. If you can't put the call in the handler itself, declare it with @event(prevent_default=True).

An await in the handler means an extra paint

Calling set_status(\"submitting\") before await submit_form(answer) is what makes the locked-form state visible at all: ReactPy renders what has been set so far when the handler suspends, so you see the form lock, then the result arrive. That is the behaviour you want here.

It is narrower than React's batching, and will surprise you when you don't want the intermediate paint. See Queueing a Series of State Updates for the measured difference.

Although this code is longer than the original imperative example, it is much less fragile. Expressing all interactions as state changes lets you introduce new visual states later without breaking existing ones. It also lets you change what should be displayed in each state without changing the logic of the interaction itself.

"},{"location":"learn/reacting-to-input-with-state/#recap","title":"Recap","text":"
  • Declarative programming means describing the UI for each visual state rather than micromanaging the UI (imperative).
  • Identify the visual states, and mock them with a prop so all of them can be seen at once.
  • Then:
    • Determine the possible causes \u2014 user inputs and computer outputs.
    • Represent those states with as few state variables as possible, and remove any that can be derived.
    • Connect the event handlers to set state.
"},{"location":"learn/referencing-values-with-refs/","title":"Referencing Values with Refs","text":""},{"location":"learn/referencing-values-with-refs/#overview","title":"Overview","text":"

When you want a component to \"remember\" some information, but you don't want that information to trigger new renders, you can use a ref.

You will learn

  • How to add a ref to your component
  • How to update a ref's value
  • How refs are different from state
  • How to use refs safely
"},{"location":"learn/referencing-values-with-refs/#adding-a-ref-to-your-component","title":"Adding a ref to your component","text":"

You can add a ref to your component by importing the use_ref hook from ReactPy:

from reactpy import use_ref\n

Inside your component, call the use_ref hook and pass the initial value that you want to reference as the only argument. For example, here is a ref to the value 0:

clicks = use_ref(0)\n

use_ref returns a Ref object holding that value:

Ref(0)  # holds the value you passed to use_ref\n

You can access the current value of that ref through the clicks.current attribute. This value is intentionally mutable, meaning you can both read and write to it. It's like a secret pocket of your component that ReactPy doesn't track. (This is what makes it an \"escape hatch\" from ReactPy's one-way data flow \u2014 more on that below!)

Here, a button will increment clicks.current on every click:

app.py Run
@component\ndef counter():\n    clicks = use_ref(0)\n\n    async def handle_click(event):\n        clicks.current += 1\n        # Nothing on screen changes, but the server console proves the ref is\n        # really incrementing between clicks.\n        print(f\"You clicked {clicks.current} times!\")\n\n    return html.button({\"onClick\": handle_click}, \"Click me!\")\n
# TODO\n

The ref points to a number, but, like state, you could point to anything: a string, an object, or even a function. Unlike state, a ref is a plain Python object with a current attribute that you can read and modify.

Note that the component doesn't re-render with every increment. Like state, refs are retained by ReactPy between re-renders. However, setting state re-renders a component. Changing a ref does not! Watch the server console: each click prints a new count, yet the page never redraws.

Pass the initial value

Unlike useRef() in React, use_ref() has no no-argument form \u2014 initial_value is required, so use_ref() fails outright with TypeError: use_ref() missing 1 required positional argument: 'initial_value'. Pass None explicitly when you have no better starting value: tick_task = use_ref(None).

The lower-level reactpy.utils.Ref() is the one that accepts no arguments, and there current starts unset rather than at None, so the first read raises AttributeError: 'Ref' object has no attribute 'current'. Components should call use_ref rather than construct a Ref directly.

"},{"location":"learn/referencing-values-with-refs/#example-building-a-stopwatch","title":"Example: building a stopwatch","text":"

You can combine refs and state in a single component. For example, let's make a stopwatch that the user can start or stop by pressing a button. In order to display how much time has passed since the user pressed \"Start\", you will need to keep track of when the Start button was pressed and what the current time is. This information is used for rendering, so you'll keep it in state:

elapsed, set_elapsed = use_state(0.0)\nrunning, set_running = use_state(False)\n

When the user presses \"Start\", you'll update the elapsed time about ten times a second. ReactPy has no setInterval, but it runs on asyncio, so the equivalent is a coroutine that sleeps and sets state, started with use_async_effect.

Now the awkward part. That coroutine runs for a long time, and it needs to know when the user pressed Start in order to compute time.monotonic() - <start>. It cannot read it from a local variable, because the render that created the coroutine is long gone by the time the coroutine wakes up. Since the start time is not used for rendering directly, you can keep it in a ref:

app.py Run
@component\ndef stopwatch():\n    # What the UI renders lives in state. What only the handlers and the\n    # ticking effect read -- and which must never trigger a render of its own\n    # -- lives in a ref.\n    elapsed, set_elapsed = use_state(0.0)\n    running, set_running = use_state(False)\n    started_at: Ref[float | None] = use_ref(None)\n\n    async def tick():\n        # The ref is the same object on every render, so this long-lived loop\n        # always sees whatever the button handlers wrote into it.\n        while started_at.current is not None:\n            await asyncio.sleep(0.1)\n            set_elapsed(time.monotonic() - started_at.current)\n\n    use_async_effect(tick, dependencies=[running])\n\n    async def start(event=None):\n        started_at.current = time.monotonic()\n        set_running(True)\n\n    async def stop(event=None):\n        if started_at.current is not None:\n            # Write the exact final value; the ticking effect only samples\n            # every 0.1s, so relying on it alone would leave the display\n            # slightly behind.\n            set_elapsed(time.monotonic() - started_at.current)\n            started_at.current = None\n        set_running(False)\n\n    async def reset(event=None):\n        started_at.current = None\n        set_running(False)\n        set_elapsed(0.0)\n\n    return html.div(\n        html.h1(f\"{elapsed:.1f}s\"),\n        html.button(\n            {\"onClick\": stop if running else start},\n            \"Stop\" if running else \"Start\",\n        ),\n        html.button({\"onClick\": reset}, \"Reset\"),\n    )\n
# TODO\n

Press Start and the counter climbs; press Stop and it freezes at the exact final value. The started_at ref is the same object on every render, so the button handlers and the long-lived ticking coroutine all see whatever the others wrote into it.

Note what the ref is not doing here: it is not holding the asyncio.Task. The ticking coroutine is owned by use_async_effect, which cancels it when running flips to False and when the component unmounts. Storing a manually created task in a ref is tempting \u2014 tick_task.current = asyncio.create_task(keep_ticking()) \u2014 but nothing ever cancels it. Measure it by unmounting the component while it runs and the loop keeps rendering forever. Reach for an effect to own background work, and use the ref for the plain values the handlers need to share.

When a piece of information is used for rendering, keep it in state. When a piece of information is only needed by event handlers and changing it doesn't require a re-render, using a ref may be more efficient.

"},{"location":"learn/referencing-values-with-refs/#differences-between-refs-and-state","title":"Differences between refs and state","text":"

Perhaps you're thinking refs seem less \"strict\" than state \u2014 you can mutate them instead of always having to use a state setting function, for instance. But in most cases, you'll want to use state. Refs are an \"escape hatch\" you won't need often. Here's how state and refs compare:

refs state use_ref(initial_value) returns Ref(current=initial_value) use_state(initial_value) returns the current value of a state variable and a state setter function Doesn't trigger re-render when you change it. Triggers re-render when you change it. Mutable \u2014 you can modify and update current outside of the rendering process. \"Immutable\" \u2014 you must use the state setting function to modify state variables to queue a re-render. You shouldn't read (or write) current during rendering. You can read state at any time. However, each render has its own snapshot of state which does not change.

Here is a counter button that's implemented with state:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_click(event):\n        set_count(count + 1)\n\n    return html.button({\"onClick\": handle_click}, f\"You clicked {count} times\")\n
# TODO\n

Because the count value is displayed, it makes sense to use a state value for it. When the counter's value is set with set_count(), ReactPy re-renders the component and the screen updates to reflect the new count.

If you tried to implement this with a ref, ReactPy would never re-render the component, so you'd never see the count change! See how clicking this button does not update its text:

app.py Run
@component\ndef counter():\n    count = use_ref(0)\n\n    async def handle_click(event):\n        # This doesn't re-render the component!\n        count.current += 1\n\n    return html.button({\"onClick\": handle_click}, f\"You clicked {count.current} times\")\n
# TODO\n

This is why reading ref.current during render leads to unreliable code. If you need that, use state instead.

"},{"location":"learn/referencing-values-with-refs/#how-does-use_ref-work-inside","title":"How does use_ref work inside?","text":"

Although both use_state and use_ref are provided by ReactPy, use_ref really is implemented on top of use_state. In reactpy/core/hooks.py:

def use_ref(initial_value):\n    return _use_const(lambda: Ref(initial_value))\n\n\ndef _use_const(function):\n    return HOOK_STACK.current_hook().use_state(function)\n

During the first render, use_ref stores a Ref object as if it were state. That object is retained by ReactPy, so every later render gets the very same object back. There is no setter in the picture because use_ref always needs to return the same object \u2014 which is exactly why it can reuse state machinery without re-rendering.

If you're familiar with object-oriented programming, refs might remind you of instance fields \u2014 but instead of self.something you write something.current.

"},{"location":"learn/referencing-values-with-refs/#when-to-use-refs","title":"When to use refs","text":"

Typically, you will use a ref when your component needs to \"step outside\" ReactPy and communicate with external APIs \u2014 often an API that won't impact the appearance of the component. Here are a few of these rare situations:

  • Storing asyncio tasks or timer handles, as in the stopwatch above
  • Storing other objects that aren't needed to calculate the VDOM \u2014 a client, a cache, a handle from an external library

If your component needs to store some value, but it doesn't impact the rendering logic, choose refs.

A ref cannot hold a DOM node

React's docs list \"accessing a DOM element\" among the top reasons to reach for a ref. That one does not carry over: reactpy.utils.Ref is a plain Python box that lives on the server, and there is no attribute that hands a browser node to Python. What you do instead \u2014 give the element an \"id\" and reach it from inline JavaScript \u2014 uses no ref at all. See Manipulating the DOM with Refs for why the name is a misnomer and what replaces each React example.

"},{"location":"learn/referencing-values-with-refs/#best-practices-for-refs","title":"Best practices for refs","text":"

Following these principles will make your components more predictable:

  • Treat refs as an escape hatch. Refs are useful when you work with external systems. If much of your application logic and data flow relies on refs, you might want to rethink your approach.
  • Don't read or write ref.current during rendering. If some information is needed during rendering, use state instead. Since ReactPy doesn't know when ref.current changes, even reading it while rendering makes your component's behavior difficult to predict. (The only exception to this is code like if not ref.current: ref.current = Thing(), which only sets the ref once during the first render.)

Limitations of ReactPy state don't apply to refs. For example, state acts like a snapshot for every render and doesn't update synchronously. But when you mutate the current value of a ref, it changes immediately:

clicks.current = 5\nprint(clicks.current)  # 5\n

This is because the ref itself is a regular Python object, and so it behaves like one.

You also don't need to worry about avoiding mutation when you work with a ref. As long as the object you're mutating isn't used for rendering, ReactPy doesn't care what you do with the ref or its contents.

"},{"location":"learn/referencing-values-with-refs/#refs-and-the-dom","title":"Refs and the DOM","text":"

You can point a ref to any value \u2014 but not to a DOM element. ReactPy has no ref attribute to pass to a tag, and no server-side handle on a browser node: use_ref lives in the Python process, the node lives in the page. The React idiom input_ref.current.focus() therefore has no Python spelling at all. What replaces it is to give the element an \"id\" and reach it from inline JavaScript, which runs in the page \u2014 and which uses no ref. The trap to avoid is reaching for it from an effect: effects run on the server too, where document does not exist. Manipulating the DOM with Refs walks through focus, scrolling, and play/pause.

"},{"location":"learn/referencing-values-with-refs/#summary","title":"Summary","text":"
  • A ref is a value that lets a component \"remember\" something without triggering a re-render.
  • Call use_ref(initial_value) and read or write ref.current.
  • Setting state re-renders; changing a ref does not, which makes refs suitable for values only event handlers need.
  • Don't read or write ref.current during rendering \u2014 use state for anything that affects what's on screen.
"},{"location":"learn/removing-effect-dependencies/","title":"Removing Effect Dependencies","text":""},{"location":"learn/removing-effect-dependencies/#overview","title":"Overview","text":"

Every dependency you list is a promise: \"when this changes, redo the work.\" Removing one you don't need is fine \u2014 and proving it is not needed is the whole job. This page covers what counts as a change in ReactPy, and the six legitimate reasons a value can leave the list.

You will learn

  • How to prove a value is not a dependency, rather than guess
  • How ReactPy actually compares dependencies \u2014 not what the docstring says
  • The six safe ways to remove a dependency
  • Why a rebuilt object is not \"new\" here, and when that saves you
  • What to do when a value must stay out of the list for real
"},{"location":"learn/removing-effect-dependencies/#dependencies-should-match-the-code","title":"Dependencies should match the code","text":"

The list describes the code, not your intent. Look at the Effect body, list every name it reads that came from the component body, and that is the list. Nothing more, nothing less.

A missing dependency is a correctness bug: the Effect reports values from an older render. An extra dependency is a behaviour bug: the work runs when it shouldn't, tearing down something that should have stayed up. Both are worth fixing, and they are fixed differently.

To remove a dependency, prove one of these about it:

  • it cannot change between renders (module constant, imported helper)
  • it is read only to compute a new value, not to decide what to do
  • it is read for a \"latest value\" snapshot, and reacting to it is unwanted
  • the code that reads it should be in an event handler, not an Effect
  • the Effect is doing two jobs and only one of them needs it
  • it changes unintentionally, and the real fix is to stop it changing

If none of those is true, the value stays. Deleting it from the list to quiet a symptom produces an Effect that looks fine and reports the past.

"},{"location":"learn/removing-effect-dependencies/#a-missing-dependency-is-visible-if-you-look","title":"A missing dependency is visible if you look","text":"

This Effect writes a summary into state but lists only topic:

app.py Run
@component\ndef reporter(topic):\n    count, set_count = use_state(0)\n    theme, set_theme = use_state(\"light\")\n    summary, set_summary = use_state(\"no report yet\")\n\n    def report():\n        # reads `count` and `theme`, but neither is in the dependency list\n        set_summary(f\"topic={topic} count={count} theme={theme}\")\n        print(f\"report topic={topic} count={count} theme={theme}\")\n\n    use_effect(report, dependencies=[topic])\n\n    async def handle_count_click(e):\n        set_count(count + 1)\n\n    async def handle_theme_click(e):\n        set_theme(\"dark\" if theme == \"light\" else \"light\")\n\n    return html.div(\n        html.button({\"onClick\": handle_count_click}, f\"Count {count}\"),\n        html.button(\n            {\"onClick\": handle_theme_click},\n            f\"Theme {theme}\",\n        ),\n        html.p(f\"live: count={count} theme={theme}\"),\n        html.p(f\"summary: {summary}\"),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        reporter(topic),\n    )\n
# TODO\n

Verified: press Count twice and Theme once, and the page reads

live:    count=2 theme=dark\nsummary: topic=news count=0 theme=light\n

The summary describes the render that created the Effect. Press Topic and both lines agree again \u2014 because a new Effect was built from the current render. That pair of lines is the whole missing-dependency bug made visible: it is not frozen at mount, and it is not current. It is from some render.

"},{"location":"learn/removing-effect-dependencies/#how-reactpy-compares-dependencies","title":"How ReactPy compares dependencies","text":"

This is where React's advice and ReactPy's behaviour part ways, and it decides which removals are safe.

Dependencies go through core/hooks.py, strictly_equal:

  1. Type gate first. If type(x) is not type(y), it is a change \u2014 1 and 1.0 never compare equal here.
  2. Local functions compare code. If both values have a __qualname__ containing <locals> or <lambda> and both expose __code__, their co_* bytecode attributes are compared \u2014 same code, and the same __qualname__, means unchanged. One side qualifying is not enough: a locally-defined function compared against an import is not code-compared.
  3. Otherwise ==, but only if the value has an __eq__ at all (hasattr(x, \"__eq__\")), and falling back to is if == raises.

Note step 3. The strictly_equal docstring claims only int, float, str, bytes and friends are compared by equality, but the implementation reaches x == y for anything with an __eq__ \u2014 which is everything. Measured over 5 renders each (mount plus four unrelated state changes), counting how many times the Effect ran:

Dependency, rebuilt on every render Runs {\"sort\": \"date\", \"limit\": 10} \u2014 never changes 1 [1, 2, 3] \u2014 never changes 1 DataFilter(\"a\") (@dataclass) \u2014 content changed twice 3 TupleFilter(\"a\") (NamedTuple) \u2014 content changed twice 3 {\"sort\": \"date\", \"limit\": count} \u2014 content tracks state 3 plain class instance, no __eq__ 5 (every render) (1, 2, 3) alternating with [1, 2, 3] 3 (type gate)

Consequences:

  • React's \"a rebuilt object is a new dependency\" warning does not apply here. A dict or list you rebuild on every render is not a dependency change, so it will not loop. That is why the classic infinite-loop example needs no fix in ReactPy \u2014 and why you must not rely on it either, since it depends on values comparing equal, not on identity.
  • A plain class instance always counts as changed, because it inherits object.__eq__ and identity never holds across renders. That is the shape that genuinely spins.
  • The type gate catches things == would not: strictly_equal(1, True) is False even though 1 == True, and a tuple swapped for a list is a change.

Because ReactPy cannot check your list for you \u2014 reactpy-flake8 reports a missing dependency only for an inline lambda, never for a named def or a decorated Effect \u2014 proving the list is yours to do. See Separating Events from Effects and Lifecycle of Reactive Effects for the linter's exact coverage and for the Layout-based test that catches what it cannot.

"},{"location":"learn/removing-effect-dependencies/#removing-unnecessary-dependencies","title":"Removing unnecessary dependencies","text":""},{"location":"learn/removing-effect-dependencies/#should-this-code-move-to-an-event-handler","title":"Should this code move to an event handler?","text":"

If the work should happen because the user did something, it belongs in the handler, and the dependency disappears along with the Effect. This is the most common removal and the one that deletes the most code \u2014 see Separating Events from Effects.

"},{"location":"learn/removing-effect-dependencies/#is-your-effect-doing-several-unrelated-things","title":"Is your Effect doing several unrelated things?","text":"

One Effect connecting a socket and loading rows means each job inherits the other's dependencies:

app.py Run
@component\ndef feed(topic):\n    page, set_page = use_state(1)\n    rows, set_rows = use_state([])\n    online, set_online = use_state(True)\n\n    def subscribe():\n        # one Effect doing two unrelated jobs: connect AND load rows\n        if not online:\n            print(\"offline \u2014 nothing to do\")\n            return\n        print(f\"connect {topic} page={page}\")\n        set_rows([f\"{topic} row {i} (page {page})\" for i in range(3)])\n\n        def close():\n            print(f\"disconnect {topic} page={page}\")\n\n        return close\n\n    use_effect(subscribe, dependencies=[topic, page, online])\n\n    async def handle_page_click(e):\n        set_page(page + 1)\n\n    async def handle_online_click(e):\n        set_online(not online)\n\n    return html.div(\n        html.button({\"onClick\": handle_page_click}, f\"Page {page}\"),\n        html.button({\"onClick\": handle_online_click}, \"Toggle online\"),\n        html.ul([html.li({\"key\": row}, row) for row in rows]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        feed(topic),\n    )\n
# TODO\n

Verified: press Toggle online and the log shows disconnect news page=2 before offline \u2014 nothing to do. Going offline tears down a connection by way of a page number. Split the Effect in two and each list shrinks to what its own job reads.

"},{"location":"learn/removing-effect-dependencies/#are-you-reading-some-state-to-calculate-the-next-state","title":"Are you reading some state to calculate the next state?","text":"

Then that state is not a dependency \u2014 it is an input to the next value. Take an updater and it leaves the list:

app.py Run
@component\ndef log_viewer(topic):\n    lines, set_lines = use_state([])\n    visible, set_visible = use_state(3)\n\n    def record():\n        # `lines` is only used to build the next value, so it is not a\n        # dependency \u2014 and listing it would make this Effect re-trigger itself\n        def append(current):\n            return [*current, f\"opened {topic}\"]\n\n        set_lines(append)\n\n    use_effect(record, dependencies=[topic])\n\n    async def handle_visible_click(e):\n        set_visible(visible + 1)\n\n    return html.div(\n        html.button({\"onClick\": handle_visible_click}, f\"Show {visible}\"),\n        html.ul([html.li({\"key\": i}, line) for i, line in enumerate(lines[-visible:])]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        log_viewer(topic),\n    )\n
# TODO\n

Verified: each topic change appends exactly one entry; changing Show does not append anything.

Now the reason this rule matters. Listing lines as well \u2014 \"just to be safe\", exactly the mistake the ReactJS page warns about in the other direction \u2014 gives:

def record():\n    def append(current):\n        return [*current, f\"opened {topic}\"]\n\n    set_lines(append)\n\nuse_effect(record, dependencies=[topic, lines])   # never settles\n

Measured: the connection rendered 400 times without settling and the component body ran 800 times. The Effect appends, lines changes, the Effect runs again. A functional updater is not a micro-optimisation here; it is what breaks the cycle.

"},{"location":"learn/removing-effect-dependencies/#do-you-want-to-read-a-value-without-reacting-to-its-changes","title":"Do you want to read a value without reacting to its changes?","text":"

Keep it in a ref that you update during render, and leave it out of the list. The difference is easy to see on a poll loop with a user-adjustable delay.

The version that lists the delay:

app.py Run
@component\ndef poller(topic):\n    delay, set_delay = use_state(0.2)\n    polls, set_polls = use_state(0)\n\n    async def loop():\n        print(f\"start polling {topic} delay={delay:.1f}s\")\n        try:\n            while True:\n                await asyncio.sleep(delay)\n                print(f\"tick {topic} delay={delay:.1f}s\")\n                # the loop is created once per run, so read state via an updater\n                set_polls(lambda current: current + 1)\n        except asyncio.CancelledError:\n            print(f\"stop polling {topic} delay={delay:.1f}s\")\n            raise\n\n    # `delay` here means adjusting it cancels and recreates the whole loop\n    use_async_effect(loop, dependencies=[topic, delay])\n\n    async def handle_delay_click(e):\n        set_delay(delay + 0.2)\n\n    return html.div(\n        html.p(f\"{topic}: {polls} polls\"),\n        html.button({\"onClick\": handle_delay_click}, f\"Delay {delay:.1f}s\"),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        poller(topic),\n    )\n
# TODO\n

The version that keeps it in a ref:

app.py Run
@component\ndef poller(topic):\n    delay, set_delay = use_state(0.2)\n    polls, set_polls = use_state(0)\n    # updated during render, so the loop always sleeps with the newest value\n    delay_ref = use_ref(delay)\n    delay_ref.current = delay\n\n    async def loop():\n        print(f\"start polling {topic}\")\n        try:\n            while True:\n                await asyncio.sleep(delay_ref.current)\n                print(f\"tick {topic} delay={delay_ref.current:.1f}s\")\n                # the loop is created once, so read state through an updater\n                set_polls(lambda current: current + 1)\n        except asyncio.CancelledError:\n            print(f\"stop polling {topic}\")\n            raise\n\n    # `delay` is deliberately absent: adjusting it must not reconnect\n    use_async_effect(loop, dependencies=[topic])\n\n    async def handle_delay_click(e):\n        set_delay(delay + 0.2)\n\n    return html.div(\n        html.p(f\"{topic}: {polls} polls\"),\n        html.button({\"onClick\": handle_delay_click}, f\"Delay {delay:.1f}s\"),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        poller(topic),\n    )\n
# TODO\n

Verified with the same script on both \u2014 two delay adjustments, half a second of ticking, then a topic change:

Loop starts Loop stops delay in the list 4 2 delay in a ref 2 1

Two of those restarts are pure waste: adjusting a setting nobody asked to reconnect for. And because the ref is assigned during render, the loop still sleeps with the newest delay \u2014 tick news delay=0.6s appears in the log of the ref version even though the Effect never re-ran.

The ref assignment during render looks like a write in a render, and it is the one case that is safe: the box is created once, the write is idempotent, and nothing derived from it feeds the render output.

"},{"location":"learn/removing-effect-dependencies/#does-some-reactive-value-change-unintentionally","title":"Does some reactive value change unintentionally?","text":"

Sometimes the dependency is correct and the value is the problem.

Move static values out of the component. A module-level constant, an imported helper, or a frozen config is the same object on every render and every connection, so it is not a dependency at all:

app.py Run
PAGE_SIZE = 20\nHEADERS = {\"Accept\": \"application/json\"}\n\n\ndef format_rows(topic, raw):\n    \"\"\"Module-level helper: same object on every render and every connection.\"\"\"\n    return [f\"{topic}: {name}\" for name in raw]\n\n\n@component\ndef directory(topic):\n    query, set_query = use_state(\"\")\n    rows, set_rows = use_state([])\n\n    def load():\n        raw = [f\"user-{i}\" for i in range(PAGE_SIZE // 4)]\n        print(f\"load topic={topic} page_size={PAGE_SIZE} headers={HEADERS}\")\n        set_rows(format_rows(topic, raw))\n\n    # neither PAGE_SIZE, HEADERS, nor format_rows belongs in this list\n    use_effect(load, dependencies=[topic])\n\n    async def handle_query_change(e):\n        set_query(await e.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": query,\n                \"onChange\": handle_query_change,\n            }\n        ),\n        html.p(f\"query={query!r} rows={len(rows)}\"),\n        html.ul([html.li({\"key\": row}, row) for row in rows]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        directory(topic),\n    )\n
# TODO\n

Verified: typing in the box re-renders the component and the Effect does not run; only the topic change logs load topic=newsx page_size=20 headers={'Accept': 'application/json'}.

Read primitive values out of objects. user.id is a dependency; user is a wider promise than you need, and any unrelated field change re-runs the Effect. Depend on the scalars you actually use.

Calculate primitives from functions, not objects. This is also the fix for the trap that silently freezes an Effect: a locally defined function passed as a dependency compares code-wise and therefore never changes. Depend on the string or number the function would have produced.

"},{"location":"learn/removing-effect-dependencies/#rebuilding-an-object-is-not-a-dependency-change-but-dont-rely-on-it","title":"Rebuilding an object is not a dependency change \u2014 but don't rely on it","text":"app.py Run
@component\ndef feed(topic):\n    page, set_page = use_state(1)\n    rows, set_rows = use_state([])\n\n    # rebuilt on every render, but compares equal to the previous one\n    options = {\"topic\": topic, \"per_page\": 20}\n\n    def load():\n        print(f\"loading {options} page={page}\")\n        set_rows([f\"{topic} row {i} (page {page})\" for i in range(3)])\n\n    use_effect(load, dependencies=[options, page])\n\n    async def handle_page_click(e):\n        set_page(page + 1)\n\n    return html.div(\n        html.button({\"onClick\": handle_page_click}, f\"Page {page}\"),\n        html.ul([html.li({\"key\": row}, row) for row in rows]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n    refreshes, set_refreshes = use_state(0)\n\n    async def handle_refreshes_click(e):\n        set_refreshes(refreshes + 1)\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_refreshes_click},\n            f\"Re-render ({refreshes})\",\n        ),\n        feed(topic),\n    )\n
# TODO\n

Verified: press Re-render three times, which rebuilds options every time \u2014 the Effect runs zero extra times. Press Page and it runs, because page is a real dependency.

It is useful to know this: query-option dicts and filter objects do not have to be use_memo-ed to avoid a reconnect storm. But it is a behaviour, not a contract. It holds only while your values compare equal, and it silently inverts for a plain class instance (5 runs out of 5 renders above). Write the list you mean, and let equality be a bonus.

"},{"location":"learn/removing-effect-dependencies/#when-a-dependency-must-come-out","title":"When a dependency must come out","text":"

There are cases where the honest answer is \"I read this value, I do not want to react to it, and a ref is the wrong shape for it.\" Then be explicit about the exception rather than quietly deleting the name:

def effect():\n    notify(user_id, channel_id)   # reads channel_id\n\nuse_effect(effect, dependencies=[user_id])\n# channel_id is read but not reacted to: it changes only when the user\n# switches channel, which unmounts this component anyway.\n

The comment is the review surface. An unexplained missing name is indistinguishable from the bug in the first example on this page \u2014 and, as measured, nothing in ReactPy will tell you which one you wrote.

What dependencies=None does not mean

None is not \"no dependencies\"; it means re-run after every render. To create the Effect once and never replace it, use dependencies=[] \u2014 which also seals the body against future renders. See the four-way table in Lifecycle of Reactive Effects.

"},{"location":"learn/removing-effect-dependencies/#recap","title":"Recap","text":"
  • The list describes the code. Prove a value is not a dependency; don't vote it off.
  • ReactPy compares dependencies with strictly_equal: type gate, then code comparison for local functions, then ==. Rebuilt dicts and lists are not changes; plain class instances always are.
  • A missing dependency is silent \u2014 no warning, no log. Make the staleness visible in the page, as the first example does, or assert it in a test.
  • The six removals: hoist what cannot change, take an updater for next-state reads, keep latest-value reads in a ref, move interaction logic to handlers, split Effects that do two jobs, and stop values changing unintentionally.
  • Listing state you only append to is the classic self-triggering loop: measured, 400 renders and no settle.
  • reactpy-flake8 will not check the list for you, so an explanatory comment is the only reviewer you have.
"},{"location":"learn/removing-effect-dependencies/#related","title":"Related","text":"
  • Lifecycle of Reactive Effects \u2014 dependency semantics, the four settings, and silent Effect failures
  • Synchronizing with Effects \u2014 the patterns that genuinely need an Effect
  • Separating Events from Effects \u2014 moving interaction logic into handlers
  • You Might Not Need an Effect \u2014 logic that belongs in the render
  • Use Effect \u2014 parameter-level reference
"},{"location":"learn/render-and-commit/","title":"Render and Commit","text":""},{"location":"learn/render-and-commit/#overview","title":"Overview","text":"

Before your components are displayed on screen, they must be rendered by ReactPy. Understanding the steps in this process will help you think about how your code executes and explain its behavior.

You will learn

  • What rendering means in ReactPy
  • When and why ReactPy renders a component
  • The steps involved in displaying a component on screen
  • Why rendering does not always produce a DOM update

A ReactPy component is a Python function that returns a description of the UI. That description has to travel from your server to someone else's browser before it becomes pixels. The journey has four steps:

  1. Triggering a render \u2014 mounting the app, or updating state.
  2. Rendering the component \u2014 Python calls your functions and builds a VDOM tree.
  3. Committing to the DOM \u2014 the diff is sent over the websocket and patched into the page.
  4. Painting \u2014 the browser redraws.

The first two happen in Python, on your server. The last two happen in the browser. Keeping that boundary in mind explains a lot of ReactPy's behavior that differs from a browser-only React app \u2014 most obviously the round-trip latency on every interaction.

"},{"location":"learn/render-and-commit/#step-1-trigger-a-render","title":"Step 1: Trigger a render","text":"

There are two reasons for a component to render:

  1. It's the component's initial render, caused by mounting it.
  2. The component's (or one of its ancestors') state has been updated.
"},{"location":"learn/render-and-commit/#initial-render","title":"Initial render","text":"

When your app starts, you need to mount a root component. Unlike React, where you call createRoot yourself, in ReactPy the executor does the mounting, and which executor you pick determines what the mounting line looks like:

app.py Run
@component\ndef image():\n    return html.img(\n        {\n            \"src\": \"https://i.imgur.com/ZF6s192.jpg\",\n            \"alt\": \"'Floralis Gen\u00e9rica' by Eduardo Catalano: a gigantic \"\n            \"metallic flower sculpture with reflective petals\",\n        }\n    )\n\n\n# Mounting is what triggers the initial render. Which line you use depends on\n# how the app is served; see the executors reference.\nmy_app = ReactPy(image)\n
# TODO\n

ReactPy(image) is the line that matters. It registers image as the root component and, when a browser connects, calls it and sends the result down. There is no DOM node to hand in \u2014 the executor owns the mount point (<div id=\"app\"> in the page it generates).

The other executors mount differently: ReactPyMiddleware takes dotted paths to components so a URL can select one, and ReactPyCsr mounts a component from a Python file that the browser runs itself. See Creating a Standalone React App for those.

"},{"location":"learn/render-and-commit/#re-renders-when-state-updates","title":"Re-renders when state updates","text":"

Once the component has been initially rendered, you can trigger further renders by updating its state with the set function from use_state. Updating your component's state automatically queues a render.

app.py Run
@component\ndef clock():\n    now, set_now = use_state(datetime.now(timezone.utc))\n\n    async def tick():\n        while True:\n            await asyncio.sleep(1)\n            set_now(datetime.now(timezone.utc))\n\n    use_async_effect(tick, dependencies=[])\n\n    return html.h1(now.strftime(\"%H:%M:%S\"))\n
# TODO\n

Note the difference from a browser-only React app: the state lives on the server. Calling set_now(...) schedules a render on the server, and the new VDOM reaches the browser a websocket message later. That is why an interaction in ReactPy feels slightly laggy compared to the same UI written in JavaScript, and why holding a lock or sleeping inside an event handler stalls the whole connection.

Setting the same value does not re-render

Calling a setter with a value equal to the current one renders nothing \u2014 verified: set_n(0) when n is already 0 produces no further render at all, while set_n(n + 1) does. The comparison is not an identity check: it accepts anything that compares equal, so even a freshly built list or dict schedules nothing when its contents match. Note this is a state-update bailout only; it is not component memoization \u2014 see step 2.

"},{"location":"learn/render-and-commit/#step-2-reactpy-renders-your-components","title":"Step 2: ReactPy renders your components","text":"

After you trigger a render, ReactPy calls your components to figure out what to display. \"Rendering\" is ReactPy calling your functions.

  • On initial render, ReactPy calls the root component.
  • For subsequent renders, ReactPy calls the function component whose state update triggered the render.

This process is recursive: if the updated component returns some other component, ReactPy renders that component next, and if that component returns yet another, ReactPy renders that one too. In this way, a state update inside a component can trigger renders all the way down its children.

Under the hood, Layout._render_component compares the new component against the previous one at the same position. Two things matter here, and both are worth internalizing:

  • A component is matched by its function object identity. If you define a @component inside another component, it is a brand-new function object on every render, so ReactPy unmounts the old one and loses all its state. Always define components at module level.
  • Children are matched by key, falling back to position. See Keeping list items in order with key.

Every child re-renders; there is no memo

ReactPy has no component-level memo. A child function runs again whenever its parent renders, even when its props are identical \u2014 verified by instrumenting a sibling: it re-ran on the parent's state update. use_memo caches values inside a component, not whole component subtrees.

This makes the \"don't nest component definitions\" rule above more than a style preference, and it means expensive children stay expensive. If a subtree is genuinely costly, the tools are use_memo for the computation and moving state down so fewer components re-render \u2014 not wrapping the child.

ReactPy will not render twice to catch you

React's Strict Mode double-invokes components in development to expose impure rendering. ReactPy has no Strict Mode and never double-invokes. A component that mutates module state or reads the clock will render perfectly once and misbehave later. If you want that safety net, write the test: render the component twice inside a reactpy.core.layout.Layout and assert the two results are equal.

"},{"location":"learn/render-and-commit/#step-3-commit-to-the-dom","title":"Step 3: Commit to the DOM","text":"

After a component renders, ReactPy has a Python description of the UI. This is not yet on the screen \u2014 ReactPy must deliver it to the browser, which must then update the DOM:

  1. Update the DOM. ReactPy's client applies the changes to existing DOM nodes, patching rather than replacing wherever it can.
  2. Display the DOM. The browser updates the screen to reflect the changes.

The first half happens on the server: ReactPy walks the new tree, computes a diff, and sends it over the websocket as a path plus a partial model. The second half happens in the browser, where the bundled client patches that subtree in place.

This is where ReactPy is most different from React, and it's the most useful thing to understand. React re-renders from the root and diffs the whole tree; ReactPy sends a patch addressed to one path in the tree. Observed directly: after clicking a button inside the first of two children, the update carried the path \"/children/0/children/0\" and a model describing only that button's subtree. Nothing about the untouched sibling went over the wire.

Because the patch is applied to existing nodes rather than rebuilt, DOM state that ReactPy doesn't know about survives. Verified in a browser: an <input> with no value attribute kept both its typed text and its focus across a parent re-render, and was the same node object before and after.

That is the practical payoff of the \"controlled vs uncontrolled\" distinction:

app.py Run
@component\ndef app():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.button({\"onClick\": handle_count_click}, \"Re-render\"),\n        html.p(f\"Rendered {count} times\"),\n        # No `value`: the browser owns this field, so ReactPy never rewrites it.\n        html.input({\"placeholder\": \"Type here and then click the button\"}),\n    )\n
# TODO\n

Type into the field, then click Re-render. The text stays. ReactPy re-runs app(), produces a new tree, and the diff for the untouched input is empty \u2014 so the browser never touches it, and neither the user's text nor their cursor position is disturbed.

Had that input carried \"value\": something, ReactPy would own it and rewrite it on every render, which is what you want when the value is genuinely derived from state \u2014 and what makes typing feel broken when you forget the matching onChange.

You can skip the browser entirely

Because rendering is pure Python, the same components can be rendered to an HTML string with reactpy_to_string from reactpy.utils \u2014 no browser, no websocket. That is useful for email bodies, static fragments, and tests. It is not server-side rendering of an app: it calls component.render() directly, with no Layout behind it, so a component that uses any hook raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 including the use_state example further up this page. Plain html.* elements and hook-free components are all it can render. And it is only safe because rendering is pure: a component that reads the clock or mutates a global will leak one request's output into another's.

"},{"location":"learn/render-and-commit/#epilogue-browser-paint","title":"Epilogue: Browser paint","text":"

After ReactPy has patched the DOM, the browser repaints the screen. Although this process is known as \"browser rendering\", we'll refer to it as \"painting\" to avoid confusion throughout the docs.

Because a round trip sits between the click and the paint, ReactPy apps feel more like a networked app than a local one. Two habits follow from that: keep event handlers short (they run on the server, blocking that connection), and prefer optimistic UI where a wrong guess is cheap.

"},{"location":"learn/render-and-commit/#recap","title":"Recap","text":"
  • Any screen update in a ReactPy app happens in four steps:
    1. Trigger
    2. Render (Python, on the server)
    3. Commit (a path-addressed patch over the websocket)
    4. Paint (the browser)
  • Mounting a root component triggers the initial render; the executor decides how.
  • Setting state queues a render; setting it to an equal value queues nothing.
  • Children always re-run \u2014 there is no memo, so keep definitions at module level and expensive work in use_memo.
  • ReactPy has no Strict Mode, so nothing will double-render your components to expose impurity for you.
  • Untouched parts of the tree are neither recomputed for the wire nor rewritten in the DOM, which is why an uncontrolled input keeps what the user typed.
"},{"location":"learn/rendering-lists/","title":"Rendering Lists","text":""},{"location":"learn/rendering-lists/#overview","title":"Overview","text":"

You will often want to display multiple similar components from a collection of data. You can use Python's list comprehensions to manipulate a list of data. On this page, you'll use comprehensions with ReactPy to filter and transform your data into lists of components.

You will learn

  • How to render components from a list using a comprehension
  • How to render only specific components using a filtering condition
  • When and why to use ReactPy keys
"},{"location":"learn/rendering-lists/#rendering-data-from-lists","title":"Rendering data from lists","text":"

Say that you have a list of content.

html.ul(\n    html.li(\"Creola Katherine Johnson: mathematician\"),\n    html.li(\"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez: chemist\"),\n    html.li(\"Mohammad Abdus Salam: physicist\"),\n    html.li(\"Percy Lavon Julian: chemist\"),\n    html.li(\"Subrahmanyan Chandrasekhar: astrophysicist\"),\n)\n

The only difference among those list items is their contents, their data. You will often need to show several instances of the same component using different data when building interfaces: from lists of comments to galleries of profile images. In these situations, you can store that data in Python dictionaries and lists and use comprehensions to render lists of components from them.

Here's a short example of how to generate a list of items from a list:

  1. Move the data into a list:
people = [\n    \"Creola Katherine Johnson: mathematician\",\n    \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez: chemist\",\n    \"Mohammad Abdus Salam: physicist\",\n    \"Percy Lavon Julian: chemist\",\n    \"Subrahmanyan Chandrasekhar: astrophysicist\",\n]\n
  1. Build a new list of VDOM nodes from people:
list_items = [html.li(person) for person in people]\n
  1. Return list_items from your component wrapped in a <ul>:
return html.ul(list_items)\n

Here is the result:

app.pystyles.css Run
people = [\n    \"Creola Katherine Johnson: mathematician\",\n    \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez: chemist\",\n    \"Mohammad Abdus Salam: physicist\",\n    \"Percy Lavon Julian: chemist\",\n    \"Subrahmanyan Chandrasekhar: astrophysicist\",\n]\n\n\n@component\ndef people_list():\n    list_items = [html.li(person) for person in people]\n    return html.ul(list_items)\n
li {\n    margin-bottom: 10px;\n}\n
# TODO\n

If you run this with REACTPY_DEBUG=1, ReactPy prints a warning for each child:

Key not specified for child in list row(7f2c11ac75e0, person={...})\n

You'll learn how to fix this warning later on this page. Before we get to that, let's add some structure to your data.

"},{"location":"learn/rendering-lists/#filtering-lists-of-items","title":"Filtering lists of items","text":"

This data can be structured even more. Rather than strings, each person becomes a dictionary:

people = [\n    {\n        \"id\": 0,\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    # ...\n]\n

Say you want to display only the chemists. In JavaScript React you would chain filter() and map(); in Python a single comprehension with an if clause does both:

chemists = [person for person in people if person[\"profession\"] == \"chemist\"]\nlist_items = [html.li(person[\"name\"]) for person in chemists]\n
app.pydata.pystyles.css Run
@component\ndef people_list():\n    chemists = [person for person in people if person[\"profession\"] == \"chemist\"]\n    list_items = [\n        html.li(\n            {\"key\": person[\"id\"]},\n            html.img({\"src\": get_image_url(person), \"alt\": person[\"name\"]}),\n            html.p(\n                html.b(person[\"name\"]),\n                f\" {person['profession']} known for {person['accomplishment']}\",\n            ),\n        )\n        for person in chemists\n    ]\n    return html.ul(list_items)\n
people = [\n    {\n        \"id\": 0,  # used as the key\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,  # used as the key\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"discovery of Arctic ozone hole\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,  # used as the key\n        \"name\": \"Mohammad Abdus Salam\",\n        \"profession\": \"physicist\",\n        \"accomplishment\": \"electromagnetism theory\",\n        \"image_id\": \"bE7W1ji\",\n    },\n    {\n        \"id\": 3,  # used as the key\n        \"name\": \"Percy Lavon Julian\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"pioneering cortisone drugs, steroids and birth control pills\",\n        \"image_id\": \"IOjWm71\",\n    },\n    {\n        \"id\": 4,  # used as the key\n        \"name\": \"Subrahmanyan Chandrasekhar\",\n        \"profession\": \"astrophysicist\",\n        \"accomplishment\": \"white dwarf star mass calculations\",\n        \"image_id\": \"lrWQx8l\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}s.jpg\"\n
ul {\n    list-style-type: none;\n    padding: 0 10px;\n}\n\nli {\n    margin-bottom: 10px;\n    display: grid;\n    grid-template-columns: auto 1fr;\n    gap: 20px;\n    align-items: center;\n}\n\nimg {\n    width: 100px;\n    height: 100px;\n    border-radius: 50%;\n}\n
# TODO\n

A helper that forgets return renders nothing

The JavaScript \"=> with a block body needs a return\" trap has a direct Python twin that is worse, because Python fails silently. If a helper used inside a comprehension forgets its return, it evaluates to None \u2014 and ReactPy skips None children instead of raising:

def render_person(person):  # WRONG: no return\n    html.li(person[\"name\"])\n\n\nlist_items = [render_person(person) for person in people]\n

list_items becomes a list of None values and the <ul> renders empty, with no error. Use a comprehension directly when the expression is simple, and add a -> VdomDict return annotation to helpers so type checkers catch the omission.

"},{"location":"learn/rendering-lists/#keeping-list-items-in-order-with-key","title":"Keeping list items in order with key","text":"

When you render children from a list, ReactPy needs a way to tell which child is which. Give each item a key \u2014 a string or a number that uniquely identifies it among the other items in that list. Unlike React, where key is a special JSX attribute, in ReactPy it lives in the attributes dictionary like any other attribute:

html.li({\"key\": person[\"id\"]}, ...)\n

Children built from a list always need keys

Any child that comes out of a comprehension \u2014 or any other list \u2014 should carry a key. ReactPy does not warn about this by default; the check is part of its debug mode, so run your app with REACTPY_DEBUG=1 (or set reactpy.config.REACTPY_DEBUG.current = True) while developing to surface them.

Keys tell ReactPy which list item each component corresponds to, so that it can match them up later. This becomes important if your list items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen key helps ReactPy infer what exactly has happened, and make the correct updates to the DOM tree.

Rather than generating keys on the fly, you should include them in your data:

app.pydata.pystyles.css Run
@component\ndef people_list():\n    list_items = [\n        html.li(\n            {\"key\": person[\"id\"]},\n            html.img({\"src\": get_image_url(person), \"alt\": person[\"name\"]}),\n            html.p(\n                html.b(person[\"name\"]),\n                f\" {person['profession']} known for {person['accomplishment']}\",\n            ),\n        )\n        for person in people\n    ]\n    return html.ul(list_items)\n
people = [\n    {\n        \"id\": 0,  # used as the key\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,  # used as the key\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"discovery of Arctic ozone hole\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,  # used as the key\n        \"name\": \"Mohammad Abdus Salam\",\n        \"profession\": \"physicist\",\n        \"accomplishment\": \"electromagnetism theory\",\n        \"image_id\": \"bE7W1ji\",\n    },\n    {\n        \"id\": 3,  # used as the key\n        \"name\": \"Percy Lavon Julian\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"pioneering cortisone drugs, steroids and birth control pills\",\n        \"image_id\": \"IOjWm71\",\n    },\n    {\n        \"id\": 4,  # used as the key\n        \"name\": \"Subrahmanyan Chandrasekhar\",\n        \"profession\": \"astrophysicist\",\n        \"accomplishment\": \"white dwarf star mass calculations\",\n        \"image_id\": \"lrWQx8l\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}s.jpg\"\n
ul {\n    list-style-type: none;\n    padding: 0 10px;\n}\n\nli {\n    margin-bottom: 10px;\n    display: grid;\n    grid-template-columns: auto 1fr;\n    gap: 20px;\n    align-items: center;\n}\n\nimg {\n    width: 100px;\n    height: 100px;\n    border-radius: 50%;\n}\n
# TODO\n
"},{"location":"learn/rendering-lists/#displaying-several-dom-nodes-for-each-list-item","title":"Displaying several DOM nodes for each list item","text":"

What do you do when each item needs to render not one, but several DOM nodes?

A plain html(...) fragment takes no positional attributes, but it does accept an attributes dictionary \u2014 so pass the key there:

[\n    html(\n        {\"key\": person[\"id\"]},\n        html.h1(person[\"name\"]),\n        html.p(person[\"profession\"]),\n    )\n    for person in people\n]\n
app.pydata.py Run
@component\ndef people_list():\n    list_items = [\n        html(\n            {\"key\": person[\"id\"]},\n            html.h1(person[\"name\"]),\n            html.p(person[\"profession\"]),\n        )\n        for person in people\n    ]\n    return html.div(list_items)\n
people = [\n    {\n        \"id\": 0,  # used as the key\n        \"name\": \"Creola Katherine Johnson\",\n        \"profession\": \"mathematician\",\n        \"accomplishment\": \"spaceflight calculations\",\n        \"image_id\": \"MK3eW3A\",\n    },\n    {\n        \"id\": 1,  # used as the key\n        \"name\": \"Mario Jos\u00e9 Molina-Pasquel Henr\u00edquez\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"discovery of Arctic ozone hole\",\n        \"image_id\": \"mynHUSa\",\n    },\n    {\n        \"id\": 2,  # used as the key\n        \"name\": \"Mohammad Abdus Salam\",\n        \"profession\": \"physicist\",\n        \"accomplishment\": \"electromagnetism theory\",\n        \"image_id\": \"bE7W1ji\",\n    },\n    {\n        \"id\": 3,  # used as the key\n        \"name\": \"Percy Lavon Julian\",\n        \"profession\": \"chemist\",\n        \"accomplishment\": \"pioneering cortisone drugs, steroids and birth control pills\",\n        \"image_id\": \"IOjWm71\",\n    },\n    {\n        \"id\": 4,  # used as the key\n        \"name\": \"Subrahmanyan Chandrasekhar\",\n        \"profession\": \"astrophysicist\",\n        \"accomplishment\": \"white dwarf star mass calculations\",\n        \"image_id\": \"lrWQx8l\",\n    },\n]\n\n\ndef get_image_url(person):\n    return f\"https://i.imgur.com/{person['image_id']}s.jpg\"\n
# TODO\n

Fragments disappear from the DOM, so this produces a flat list of <h1>, <p>, <h1>, <p>, and so on.

"},{"location":"learn/rendering-lists/#where-to-get-your-key","title":"Where to get your key","text":"

Different sources of data provide different sources of keys:

  • Data from a database: If your data is coming from a database, you can use the database keys/IDs, which are unique by nature.
  • Locally generated data: If your data is generated and persisted locally (e.g. notes in a note-taking app), use an incrementing counter from itertools.count or a UUID from uuid.uuid4 when creating the item \u2014 never while rendering.
"},{"location":"learn/rendering-lists/#rules-of-keys","title":"Rules of keys","text":"
  • Keys must be unique among siblings. However, it's okay to use the same keys for nodes in different lists.
  • Keys must not change or that defeats their purpose! Don't generate them while rendering.

Duplicate keys are a hard error in ReactPy

React logs a warning for duplicate keys and carries on. ReactPy raises instead:

ValueError: Duplicate keys [1] at '/children/0'\n

The whole component fails to render, so a data set with repeated IDs takes down the section rather than degrading it. If your data can legitimately repeat \u2014 for example, two rows that render the same underlying record \u2014 make the key unique among siblings by combining the fields that distinguish them.

"},{"location":"learn/rendering-lists/#why-does-reactpy-need-keys","title":"Why does ReactPy need keys?","text":"

Imagine that files on your desktop didn't have names. Instead, you'd refer to them by their order \u2014 the first file, the second file, and so on. You could get used to it, but once you delete a file, it would get confusing. The second file would become the first file, the third file would be the second file, and so on.

File names in a folder and keys in a list serve a similar purpose. They let us uniquely identify an item between its siblings. A well-chosen key provides more information than the position within the list. Even if the position changes due to reordering, the key lets ReactPy identify the item throughout its lifetime.

This is observable. Give each row its own use_state seeded from the person it renders, then reverse the list. With keys, every row's state travels with its person. Without keys, the state stays behind in the old slot:

keyed=True\n   before: shown=ada   state=ada#0\n   after : shown=alan  state=alan#2\n\nkeyed=False\n   before: shown=ada   state=ada#0\n   after : shown=alan  state=ada#0    # the row for \"alan\" inherited ada's state\n

Index-based keys are a trap

You might be tempted to use an item's index in the list as its key. In fact, that's effectively what ReactPy uses if you don't specify a key at all. But the order in which you render items will change over time if an item is inserted, deleted, or if the list gets reordered, and \u2014 as the output above shows \u2014 state then attaches to the wrong item.

Similarly, do not generate keys on the fly, e.g. with key=uuid.uuid4() or key=id(person) inside the render. Keys that differ on every render never match up, so every component and DOM node is recreated each time. Not only is this slow, it will also lose any user input inside the list items. Instead, use a stable ID that comes from the data.

Note that your component never receives key as a prop \u2014 ReactPy reserves it. Declaring a key parameter raises immediately:

TypeError: Component render function <function row ...> uses reserved parameter 'key'\n

If your component needs an ID, pass it as a separate prop: profile(id=id, key=id).

Pass a list, not a generator

html.ul(html.li(person) for person in people) renders, but a generator has no length, so ReactPy cannot verify key integrity and warns about it in debug mode. Bracket the comprehension so the children are a real list: html.ul([html.li(person) for person in people]).

"},{"location":"learn/rendering-lists/#recap","title":"Recap","text":"

On this page you learned:

  • How to move data out of components and into data structures like lists and dictionaries.
  • How to generate sets of similar components with a list comprehension.
  • How to create filtered lists with an if clause in a comprehension.
  • Why and how to set key on each component in a collection so ReactPy can keep track of each of them even if their position or data changes.
  • That ReactPy checks keys only in debug mode, treats duplicate keys as an error, and reserves key so components can never receive it as a prop.
"},{"location":"learn/responding-to-events/","title":"Responding to Events","text":""},{"location":"learn/responding-to-events/#overview","title":"Overview","text":"

ReactPy lets you add event handlers to your VDOM. Event handlers are your own functions that will be triggered in response to interactions like clicking, hovering, focusing form inputs, and so on.

You will learn

  • Different ways to write an event handler
  • How to pass event handling logic from a parent component
  • How events propagate and how to stop them
  • What the event object actually contains
"},{"location":"learn/responding-to-events/#adding-event-handlers","title":"Adding event handlers","text":"

To add an event handler, you first define a function and then pass it as a prop to the appropriate tag. For example, here is a button that doesn't do anything yet:

app.py Run
@component\ndef button():\n    return html.button(\"I don't do anything\")\n
# TODO\n

You can make it show a message when a user clicks by following these three steps:

  1. Declare a function called handle_click inside your def button() component.
  2. Implement the logic inside that function (use print to show the message).
  3. Add \"onClick\": handle_click to the html.button attributes dictionary.
app.pystyles.css Run
from reactpy import component, html\n\n\n@component\ndef button():\n    async def handle_click(event):\n        print(\"You clicked me!\")\n\n    return html.button({\"onClick\": handle_click}, \"Click me\")\n
button {\n    margin-right: 10px;\n}\n
# TODO\n

You defined the handle_click function and then passed it as a prop to html.button. handle_click is an event handler. Event handler functions:

  • Are usually defined inside your components.
  • Have names that start with handle, followed by the name of the event.

By convention, it is common to name event handlers as handle followed by the event name. You'll often see \"onKeyDown\": handle_key_down, \"onMouseEnter\": handle_mouse_enter, and so on.

The handler key is camelCase, and it lives in the attributes dict

This is the single most common mistake when moving from ReactJS examples. The key is the DOM event name in camelCase \u2014 \"onClick\", \"onChange\", \"onSubmit\" \u2014 and it goes inside the attributes dictionary, not as a keyword argument:

html.button({\"onClick\": handle_click}, \"Click me\")   # correct\n

Writing html.button({\"on_click\": handle_click}, ...) does not raise. ReactPy ships it to the browser verbatim as a handler for an event called _click \u2014 the client registers a listener for the key with the leading on stripped \u2014 and no browser ever fires one. The button looks fine and simply never responds. Only the names you define (variables, components, component props) are snake_case.

Note what does and does not decide \"this is a handler\". In core/vdom.py, separate_attributes_handlers_and_inline_javascript the test is callable(v) on the value, checked before the key is ever looked at: a callable under \"on_click\", or even under \"foo\", lands in eventHandlers. That sort step is also where the coroutine requirement is enforced, because it builds an EventHandler for the value \u2014 so any callable which cannot be awaited raises TypeError at element-build time, whatever key it sits under. The key pattern ^on[A-Z]\\w+ (core/vdom.py, EVENT_ATTRIBUTE_PATTERN) is used only for the string case below \u2014 it decides whether a string becomes inlineJavaScript or a plain attribute. So {\"onClick\": \"alert(1)\"} runs as JavaScript, while {\"onclick\": \"alert(1)\"} and {\"on_click\": \"alert(1)\"} become inert attributes.

An event handler has to be declared with async def, so it cannot be written inline:

async def handle_click(event):\n    print(\"You clicked me!\")\n\n\nhtml.button({\"onClick\": handle_click}, \"Click me\")\n

A handler must be a coroutine \u2014 so a lambda never works

The first rule of an event handler is that it is declared async def. ReactPy checks it the moment the element is built, and refuses anything else:

TypeError: handle_click cannot be used as an event handler because it is not a coroutine.\nEvent handlers must be declared with 'async def':\n\n    async def on_click(event):\n        ...\n

This is not ceremony. Reading the object an event fired from means waiting on the browser, and only a coroutine can await that round trip without stalling the layout \u2014 a blocking read would have to run off-loop, which is the one thread where set_state() fails with no running event loop.

The consequence for a lambda is direct: a lambda can never be a coroutine, so {\"onClick\": lambda event: ...} raises. Name the handler instead. Passing a state setter directly ({\"onClick\": set_count}) is likewise refused \u2014 a setter is not a handler, and the TypeError says so and shows you the fix.

What is accepted: a plain async def, a callable object whose __call__ is a coroutine, and a functools.partial or functools.wraps wrapper around either \u2014 _unwrap_handler in core/events.py peels partials and then runs inspect.unwrap before asking whether the result is a coroutine.

A string is not a handler \u2014 it is JavaScript

ReactPy has a second, easy-to-miss form. If the value of an on* key is a string, ReactPy treats it as JavaScript to run in the browser instead of calling your Python code:

html.button({\"onClick\": \"alert('You clicked me!')\"}, \"Click me\")\n

That renders into an inlineJavaScript field on the VDOM node rather than an eventHandlers field, and nothing on the server ever runs. It is a real escape hatch for things only the browser can do, but it is never what you want when you meant to write a Python function. If your handler silently does nothing, check whether you passed a string.

Pass the function, don't call it

Functions passed to event handlers must be passed, not called:

html.button({\"onClick\": handle_click}, \"...\")    # correct: fires on click\nhtml.button({\"onClick\": handle_click()}, \"...\")  # wrong: fires during render\n

In the second case the () calls handle_click immediately during rendering, without any clicks \u2014 because the Python inside the call is evaluated right away, exactly like an expression inside JSX braces. Whatever it returns is what gets stored. Handlers are picked out by callable(v), so a None return is not a handler: it falls through to the plain attributes and ships as {\"onClick\": None}. The client never turns that into a DOM attribute \u2014 a value under an on* name is consumed as a listener, and a falsy one means \"register nothing\". Nothing fires, and the button renders and does nothing, forever.

If handle_click is a coroutine function \u2014 the only shape a handler may have \u2014 then calling it does not run its body at all; it returns a coroutine object. That is not callable either, so it takes the same fall through to the attributes as None does. The difference is what happens next: the layout renders, and the failure appears when the model is encoded for the wire \u2014 TypeError: Type is not JSON serializable: coroutine out of the orjson.dumps on send (executors/asgi/middleware.py). So one form of this mistake gives you a silently dead button, the other a traceback at send time. Either way, pass the function; do not call it.

The same trap appears with inline code:

# This prints when the component renders, not when clicked!\nhtml.button({\"onClick\": print(\"You clicked me!\")}, \"Click me\")\n\n# Wrap it in a named coroutine so it becomes a function to call later.\nasync def handle_click(event):\n    print(\"You clicked me!\")\n\nhtml.button({\"onClick\": handle_click}, \"Click me\")\n

In both working cases, what you pass is a function. A lambda cannot be the wrapper here, because a handler has to be async def.

"},{"location":"learn/responding-to-events/#reading-props-in-event-handlers","title":"Reading props in event handlers","text":"

Because event handlers are declared inside a component, they have access to the component's props. Here is a button that, when clicked, displays its message prop:

app.py Run
@component\ndef alert_button(message, *children):\n    shown, set_shown = use_state(\"\")\n\n    async def handle_click(event):\n        set_shown(message)\n\n    return html.div(\n        html.button({\"onClick\": handle_click}, *children),\n        html.p(shown),\n    )\n\n\n@component\ndef toolbar():\n    return html.div(\n        alert_button(\"Playing!\", \"Play Movie\"),\n        alert_button(\"Uploading!\", \"Upload Image\"),\n    )\n
# TODO\n

This lets the two buttons show different messages. Try changing the messages passed to them.

Note the set_shown(message) line: the handler closes over the message prop, which is a plain function parameter, so this behaves like any other Python closure.

"},{"location":"learn/responding-to-events/#passing-event-handlers-as-props","title":"Passing event handlers as props","text":"

Often you'll want the parent component to specify a child's event handler. Consider buttons: depending on where you're using a button() component, you might want to execute a different function\u2014perhaps one plays a movie and another uploads an image.

To do this, pass a prop the component receives from its parent as the event handler:

app.py Run
@component\ndef button(on_click, *children):\n    return html.button({\"onClick\": on_click}, *children)\n\n\n@component\ndef play_button(movie_name):\n    shown, set_shown = use_state(\"\")\n\n    async def handle_play_click(event):\n        set_shown(f\"Playing {movie_name}!\")\n\n    return html.div(\n        button(handle_play_click, f'Play \"{movie_name}\"'),\n        html.p(shown),\n    )\n\n\n@component\ndef upload_button():\n    shown, set_shown = use_state(\"\")\n\n    async def handle_upload_click(event):\n        set_shown(\"Uploading!\")\n\n    return html.div(\n        button(handle_upload_click, \"Upload Image\"),\n        html.p(shown),\n    )\n\n\n@component\ndef toolbar():\n    return html.div(play_button(\"Kiki's Delivery Service\"), upload_button())\n
# TODO\n

Here, toolbar() renders a play_button() and an upload_button():

  • play_button() passes handle_play_click as the on_click prop to the button() inside.
  • upload_button() passes handle_upload_click as the on_click prop.

Finally, the button() component accepts a prop called on_click and passes it straight to the built-in browser button with {\"onClick\": on_click}. This tells ReactPy to call the passed function on click.

Notice the two levels of naming: the prop your own component accepts is an ordinary Python parameter (on_click, snake_case), while the key handed to the browser tag is \"onClick\". A design system commonly puts styling in components like buttons but leaves the behavior to callers, so play_button() and upload_button() pass handlers down.

"},{"location":"learn/responding-to-events/#naming-event-handler-props","title":"Naming event handler props","text":"

Built-in tags like html.button and html.div only support DOM event names as handler keys \u2014 \"onClick\", \"onChange\", and so on. When you build your own components, however, you can name their handler props anything you like, because they are just Python parameters.

By convention, name them on_ followed by the interaction:

app.py Run
@component\ndef button(on_smash, *children):\n    return html.button({\"onClick\": on_smash}, *children)\n\n\n@component\ndef app():\n    status, set_status = use_state(\"Nothing selected\")\n\n    async def handle_play(event):\n        set_status(\"Playing!\")\n\n    async def handle_upload(event):\n        set_status(\"Uploading!\")\n\n    return html.div(\n        html.p(status),\n        button(handle_play, \"Play Movie\"),\n        button(handle_upload, \"Upload Image\"),\n    )\n
# TODO\n

In this example {\"onClick\": on_smash} shows that the browser button still needs the key \"onClick\", but the prop name received by your own button() component is up to you.

When a component supports multiple interactions, name handler props after app-specific concepts. A toolbar() could receive on_play_movie and on_upload_image. The calling app() does not need to know what toolbar() will do with them \u2014 that's an implementation detail. It could wire them to a click today and a keyboard shortcut tomorrow. Naming props after app-specific interactions gives you that flexibility.

Use the right tag

Use the semantically correct HTML tag for the interaction. To handle clicks, use html.button({\"onClick\": handle_click}) rather than html.div({\"onClick\": handle_click}). A real browser <button> gives you built-in behavior such as keyboard navigation and focus handling. If you dislike the default styling, restyle it with CSS rather than downgrading it to a <div>. Learn more about writing accessible markup.

"},{"location":"learn/responding-to-events/#the-event-object","title":"The event object","text":"

Handlers receive the event data as their single positional argument. By convention it's called event.

It is not React's SyntheticEvent, and it is not a serialized snapshot either. There is nothing to persist and no native object to unwrap: persist() and nativeEvent are React's answers to a pooling problem this design never had. They are not members of Event \u2014 but __getattr__ will happily hand you a proxy for either name, truthy and waiting to fail, so if event.persist: and hasattr(event, \"nativeEvent\") both say yes and only the await raises EventProxyNotFound. Never duck-type a proxy by attribute; read what you know is there. What your handler holds is a reactpy.types.Event \u2014 a live, lazily resolved view onto the browser's object (_event_proxy.py, Event). Reading any property of it hands back another proxy, and awaiting the proxy is what asks the browser for the value:

async def handle_change(event):\n    value = await event.target.value   # one round trip\n    await event.target.focus()         # methods are callable too\n

The property chain is spelled exactly as it is in JavaScript. The one difference is the await, because the value lives in the browser rather than in this process. There is no dictionary behind it: event[\"target\"][\"value\"] raises TypeError: 'Event' object is not subscriptable, because a proxy is deliberately not a container and has no __getitem__, __iter__, or __len__.

Nothing is fetched until a handler asks for it, so a handler which only calls set_state() never sends a request. Two things keep the count down for the handlers which do read. A property which has been read is remembered against its event, so reading it twice costs one trip. And properties which are in flight at the same moment go out as a single message:

value, key = await event.read(\"target.value\", \"key\")\nvalue, key = await asyncio.gather(event.target.value, event.key)\n

Both cost one round trip. Two await expressions written one after the other cannot be batched \u2014 the second is not known until the first has landed.

A read belongs to the moment it is awaited

await event.target.value reports what the element holds now, just as the same expression would in JavaScript \u2014 not what it held when the keystroke fired. A handler still queued behind another event's work sees whatever that element has become by the time it runs. A handler which must act on the value as its own event saw it has to read it before anything else can change it.

Reads are cached too, so a second read hands back what the first one found however much the page has moved on since. Ask the browser what it holds by now with refresh:

(latest,) = await event.read(\"target.value\", refresh=True)\n

Nothing is quietly answered with None. A property the browser will not give \u2014 one it blocks, an object it has forgotten, a path that is not there \u2014 raises a subclass of EventProxyError (Unavailable, Timeout, NotFound, Evicted, AccessError). A typo such as event.target.vallue is therefore an error, not a silent None. What is quiet is the mistake in the other direction: leave the await off and you hold a proxy, which is always truthy and never raises on its own \u2014 it reaches set_state() or an f-string as its own repr, or fails at the wire. A None you did read is a real answer from the page: a property that exists and holds null comes back as one.

The reads worth knowing:

Read Meaning await event.target.value Text of an <input>, <textarea>, or selected <option> await event.target.checked Whether a checkbox or radio is on \u2014 read it from \"onChange\", which is the event that fires when one is toggled await event.target.files Files from an <input type=\"file\"> await event.key Which key, on onKeyDown / onKeyUp await event.type The event name, e.g. \"click\" \u2014 the one value the client volunteers with the id, so awaiting it sends nothing await event.target.dataset The element's data-* attributes await event.composedPath() The event's path, read from the browser

Since the client holds the real object instead of serializing it, that list is a shortlist and not a ceiling. Anything the browser can answer is readable, and whatever it cannot turn into JSON \u2014 a DOM node, a DataTransfer \u2014 comes back as a reference which is itself a proxy, so you can keep walking. What the client refuses is anything that could be used to break out of the page (window, document, parentNode, __proto__, eval, ...) and anything that looks like an event handler, which is matched as /^on[a-z]/i and so includes onPointerDown. Whole-object views omit methods; an explicit method path still returns something callable.

Form submits

For onSubmit the target is the <form> itself, which means value is not there to read \u2014 await event.target.value raises EventProxyNotFound, and batching it into a read() fails the whole read. A form does have a name, so (name,) = await event.read(\"target.name\") works; to reach what was typed, ask for the control collection: controls = await event.target.elements, then index c[\"value\"] on the entries it returns. See Communicate Data Between Server and Client.

"},{"location":"learn/responding-to-events/#event-propagation","title":"Event propagation","text":"

Event handlers also catch events from any children a component has. We say that an event \"bubbles\" or \"propagates\" up the tree: it starts where the event happened, and then goes up.

ReactPy attaches an ordinary DOM listener to each element, so bubbling is the browser's native behavior rather than something ReactPy implements. That has one consequence worth knowing: unlike React, which delegates every event to the document root and then replays it down your component tree, ReactPy's handlers follow the real DOM tree. A parent handler fires for events from anything nested inside it, including elements you did not render yourself.

This <div> contains a button. Both the <div> and the button have their own \"onClick\" handlers. Which do you think fire when you click the button?

app.pystyles.css Run
@component\ndef toolbar():\n    messages, set_messages = use_state([])\n\n    async def handle_toolbar_click(event):\n        set_messages([*messages, \"You clicked on the toolbar!\"])\n\n    async def handle_play_click(event):\n        set_messages([*messages, \"Playing!\"])\n\n    return html.div(\n        {\"className\": \"Toolbar\", \"onClick\": handle_toolbar_click},\n        html.button({\"onClick\": handle_play_click}, \"Play Movie\"),\n    )\n
.toolbar {\n    background: #aaa;\n    padding: 5px;\n}\n\nbutton {\n    margin: 5px;\n}\n
# TODO\n

If you click the button, its \"onClick\" runs first, followed by the parent <div>'s. Two messages appear. If you click the toolbar itself, only the parent's handler runs.

Scroll does not bubble

Because propagation is the browser's own, the browser's exceptions apply. \"onScroll\" does not bubble: attaching it to an ancestor will not catch scrolls from descendants. Attach it to the element that actually scrolls.

"},{"location":"learn/responding-to-events/#stopping-propagation","title":"Stopping propagation","text":"

To prevent an event from reaching parent components, call event.stopPropagation():

app.pystyles.css Run
@component\ndef button(on_click, *children):\n    async def handle_click(event):\n        # Detected from this line, so the client stops the event before\n        # it reaches the parent's handler.\n        event.stopPropagation()\n        on_click()\n\n    return html.button({\"onClick\": handle_click}, *children)\n\n\n@component\ndef toolbar():\n    messages, set_messages = use_state([])\n\n    async def handle_toolbar_click(event):\n        set_messages([*messages, \"You clicked on the toolbar!\"])\n\n    def handle_play_click():\n        set_messages([*messages, \"Playing!\"])\n\n    return html.div(\n        {\"className\": \"Toolbar\", \"onClick\": handle_toolbar_click},\n        button(handle_play_click, \"Play Movie\"),\n    )\n
.toolbar {\n    background: #aaa;\n    padding: 5px;\n}\n\nbutton {\n    margin: 5px;\n}\n
# TODO\n

Now clicking the button produces only \"Playing!\".

ReactPy detects this call by reading your bytecode

Calling event.stopPropagation() inside a handler does not itself stop anything: by the time your handler runs, the event has already bubbled. What stops propagation is a flag sent to the browser in advance, and ReactPy derives it by inspecting the bytecode of your handler (core/_event_inspect.py, inspect_event_handler).

It looks for .stopPropagation (and .preventDefault, and .stopImmediatePropagation) called directly on the first parameter. That produces some sharp edges, all of them observable:

  • The handler must take a parameter at all. async def handler(): can never be detected.
  • It must be the first positional parameter. Renaming it is fine; the position is what matters.
  • Rebinding defeats it. e = event followed by e.stopPropagation() is not detected, and propagation silently continues.
  • Passing the event into a helper that calls it is likewise not detected \u2014 the inspector reads one function's bytecode and does not follow calls.

When in doubt, skip detection and declare the intent explicitly:

from reactpy import event\n\n@event(stop_propagation=True, prevent_default=True)\nasync def handle_click(event): ...\n

Because the live proxy keeps the browser object alive, the call is also forwarded to it without being waited for \u2014 but by then the browser has usually moved on, so rely on the handler flag when the action truly must not happen. event.propagationStopped and event.defaultPrevented answer synchronously, and with what Python asked for. What the browser settled on is a property like any other: (prevented,) = await event.read(\"defaultPrevented\"), which is worth the round trip when other scripts on the page may have prevented the action too.

"},{"location":"learn/responding-to-events/#preventing-default-behavior","title":"Preventing default behavior","text":"

Call event.preventDefault() the same way to stop the browser's built-in action. The classic case is a form submit, which would otherwise navigate away from the page:

app.py Run
@component\ndef signup():\n    signed_up, set_signed_up = use_state(0)\n    name, set_name = use_state(\"\")\n\n    async def handle_name_change(event):\n        set_name(await event.target.value)\n\n    async def handle_submit(event):\n        # Without this line the browser would navigate away from the page.\n        event.preventDefault()\n        set_signed_up(signed_up + 1)\n        set_name(\"\")\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.input({\"value\": name, \"onChange\": handle_name_change}),\n        html.button({\"type\": \"submit\"}, \"Sign up\"),\n        html.p(f\"{signed_up} signed up\"),\n    )\n
# TODO\n

The same detection rules \u2014 and the same @event(prevent_default=True) escape hatch \u2014 apply.

Capture-phase handlers

To see an event on the way down, before your children handle it, append Capture to the event name:

async def handle_toolbar_click(event):\n    ...\n\n\nasync def handle_play_click(event):\n    ...\n\n\nhtml.div(\n    {\"onClickCapture\": handle_toolbar_click},\n    html.button({\"onClick\": handle_play_click}, \"Play Movie\"),\n)\n

ReactPy forwards the key untouched \u2014 there is no capture logic in vdom.tsx at all. The suffix is honoured one layer down, by Preact, which strips a trailing Capture, registers the listener with addEventListener(name, handler, true), and dispatches \"click\" to it in the capture phase. So \"onClickCapture\" fires before the button's own \"onClick\", and the ordering is the browser's, not ReactPy's. Nothing in ReactPy checks that the name you gave is a real event, which is why the same freedom lets \"onMyThing\" through and lets \"on_click\" register a listener for \"_click\" that nothing ever dispatches.

"},{"location":"learn/responding-to-events/#can-event-handlers-have-side-effects","title":"Can event handlers have side effects?","text":"

Yes. Unlike component bodies, which must stay pure, event handlers run in response to an action rather than during rendering, so they are the correct place to change things. See Where you can cause side effects for where side effects belong and why.

"},{"location":"learn/responding-to-events/#recap","title":"Recap","text":"
  • Declare the handler with async def. A def, a lambda, or a state setter is refused with a TypeError when the element is built.
  • Add a handler by putting the DOM event name in camelCase into the attributes dictionary: {\"onClick\": handle_click}.
  • Only names you define are snake_case; \"on_click\" renders but never fires.
  • Pass a function, never the result of calling one; a string value means client-side JavaScript, not Python.
  • Handlers may read props, call setters, and have side effects.
  • The event object is a live proxy onto the browser object, not a snapshot and not a SyntheticEvent. Read await event.target.value and await event.key; batch with await event.read(\"target.value\", \"key\"). Subscripting it raises.
  • Events bubble natively, with the browser's own exceptions such as \"onScroll\". Stop them with event.stopPropagation(), which is detected from your bytecode \u2014 or declare it with @event(stop_propagation=True).
"},{"location":"learn/reusing-logic-with-custom-hooks/","title":"Reusing Logic with Custom Hooks","text":""},{"location":"learn/reusing-logic-with-custom-hooks/#overview","title":"Overview","text":"

A custom Hook is a plain Python function whose name starts with use_ and which calls other Hooks. It lets you reuse stateful logic between components \u2014 each caller still gets its own state, and nothing about it is special to ReactPy beyond the naming rule that makes the linter work.

You will learn

  • How to extract a custom Hook, and what it does and does not share
  • Why each call site gets independent state
  • Why passing a callback into a Hook silently freezes it
  • What actually happens when a Hook is called conditionally
  • What reactpy-flake8 can and cannot see inside a custom Hook
"},{"location":"learn/reusing-logic-with-custom-hooks/#custom-hooks-sharing-logic-between-components","title":"Custom Hooks: sharing logic between components","text":"

Consider a shopping list where each row can be selected. The selection logic is stateful, and a second screen will need it too. Pulling it out gives you a function that reads like a Hook and behaves like one:

app.py Run
def use_cart(items):\n    \"\"\"Selection logic for a list of items \u2014 the state lives in the caller.\"\"\"\n    selected, set_selected = use_state([])\n\n    def handler_for(item):\n        async def toggle(event):\n            if item in selected:\n                set_selected([name for name in selected if name != item])\n            else:\n                set_selected([*selected, item])\n\n        return toggle\n\n    return selected, handler_for\n\n\n@component\ndef checkout(items):\n    selected, handler_for = use_cart(items)\n    total = 10 * len(selected)\n    return html.div(\n        html.ul(\n            [\n                html.li(\n                    {\"key\": item},\n                    html.button(\n                        {\"onClick\": handler_for(item)},\n                        f\"{item} {'[x]' if item in selected else '[ ]'}\",\n                    ),\n                )\n                for item in items\n            ]\n        ),\n        html.p(f\"{len(selected)} selected, total ${total}\"),\n    )\n\n\n@component\ndef app():\n    return checkout([\"apple\", \"bread\", \"cheese\"])\n
# TODO\n

Verified: pressing apple, cheese, bread, then apple again walks the counter 1 \u2192 2 \u2192 3 \u2192 2 and the total follows it. Nothing about use_cart is registered, exported, or declared \u2014 it is an ordinary function that happens to call use_state.

Two rules make it a Hook rather than a helper:

  • Its name starts with use_. This is not cosmetic; see below.
  • It is called during rendering, at the top level of a component or another Hook \u2014 never in an if, a loop, a callback, or after an early return.
"},{"location":"learn/reusing-logic-with-custom-hooks/#hook-names-must-match-_use_w","title":"Hook names must match ^_*use_\\w+$","text":"

reactpy_flake8 decides what counts as a Hook with a regex, DEFAULT_HOOK_FUNCTION_PATTERN = r\"^_*use_\\w+$\" (reactpy_flake8/run.py). Measured against the same file with only the function name changed, calling it conditionally from a component:

Function name Reported use_form REACTPY102 \u2014 hook used inside if _use_form REACTPY102 useForm REACTPY101 \u2014 hook used outside component or hook get_form REACTPY101

So the naming rule is what buys you the good diagnostic. A camelCase useForm still gets flagged \u2014 but as REACTPY101 at every use_state line inside it, which points at the wrong thing and reads like you called a Hook from the wrong place. Name them use_snake_case, and the linter can tell control-flow violations apart from misplaced calls.

Not every render-time function needs the prefix

Add use_ when the function calls Hooks. Leave it off when it does not \u2014 a format_rows() or total_price() is an ordinary function, and prefixing it would make the linter police it as a Hook.

"},{"location":"learn/reusing-logic-with-custom-hooks/#custom-hooks-share-logic-not-state","title":"Custom Hooks share logic, not state","text":"

A common fear is that a Hook is a singleton. It is not: state is stored per call site, in order. Call the same Hook twice in one component and you get two independent boxes.

app.py Run
def use_toggle(initial=False):\n    on, set_on = use_state(initial)\n\n    async def flip(event):\n        set_on(not on)\n\n    return on, flip\n\n\n@component\ndef panel():\n    left, flip_left = use_toggle()\n    right, flip_right = use_toggle()\n    return html.div(\n        html.button({\"onClick\": flip_left}, f\"left {'ON' if left else 'off'}\"),\n        html.button({\"onClick\": flip_right}, f\"right {'ON' if right else 'off'}\"),\n        html.p(f\"both on: {left and right}\"),\n    )\n\n\n@component\ndef app():\n    return panel()\n
# TODO\n

Verified: pressing left gives left ON right off; pressing right gives left ON right ON; pressing left again gives left off right ON. Same function, same arguments, two unrelated pieces of state \u2014 and both on only when both are flipped.

The same Hook called from two different components is equally independent, since each component instance has its own Hook slots. There is no shared registry, no module-level store, and no Context involved.

This is also why a custom Hook is not a place to put values you want everyone to see. For that, pass props or use create_context (Passing Data Deeply with Context).

"},{"location":"learn/reusing-logic-with-custom-hooks/#passing-reactive-values-between-hooks","title":"Passing reactive values between Hooks","text":"

A Hook receives plain values during render and returns plain values. It re-runs whenever its caller re-renders, so it sees the same props and state the caller does.

def use_selection(items, limit):\n    selected, set_selected = use_state([])\n    too_many = len(selected) > limit          # re-evaluated every render\n    return selected, set_selected, too_many\n

What you cannot do is pass a callback in and list it as a dependency.

"},{"location":"learn/reusing-logic-with-custom-hooks/#the-callback-you-pass-in-will-go-stale","title":"The callback you pass in will go stale","text":"

use_polling below takes a result handler and lists it as the Effect's dependency \u2014 which looks exactly right, and never works:

app.py Run
def use_polling(on_result):\n    async def loop():\n        while True:\n            await asyncio.sleep(0.2)\n            on_result(\"poll finished\")\n\n    # `on_result` looks like a sensible dependency; it never counts as changed\n    use_async_effect(loop, dependencies=[on_result])\n\n\n@component\ndef dashboard():\n    topic, set_topic = use_state(\"news\")\n    last, set_last = use_state(\"nothing yet\")\n    tick = use_ref(0)\n\n    def on_result(value):\n        tick.current += 1\n        # reads `topic` from whichever render created this callback\n        set_last(f\"{value} #{tick.current} (handler saw topic={topic})\")\n\n    use_polling(on_result)\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        html.p(last),\n    )\n\n\n@component\ndef app():\n    return dashboard()\n
# TODO\n

Verified: press Topic once so the page reads Topic newsx, and the line still says handler saw topic=news. The poll counter keeps climbing, so it looks alive \u2014 but it is calling the handler from the first render, forever.

The reason is in core/hooks.py, strictly_equal: a def nested in a component is compared by its co_* bytecode attributes, not by identity. on_result re-created on the next render compares code-equal, so the dependency never changes and the Effect is never replaced.

The fix is to keep the callback in a ref that is refreshed during render, and leave it out of the list:

app.py Run
def use_polling(topic, on_result):\n    latest = use_ref(on_result)\n    # always the newest callback, without making it a dependency\n    latest.current = on_result\n\n    async def loop():\n        while True:\n            await asyncio.sleep(0.2)\n            latest.current(f\"result for {topic}\")\n\n    # deliberately excludes on_result: listing it would never help anyway\n    use_async_effect(loop, dependencies=[topic])\n\n\n@component\ndef dashboard():\n    topic, set_topic = use_state(\"news\")\n    last, set_last = use_state(\"nothing yet\")\n\n    def on_result(value):\n        # reads `topic` from the render that created it\n        set_last(f\"{value} (topic was {topic})\")\n\n    use_polling(topic, on_result)\n\n    async def handle_topic_click(e):\n        set_topic(topic + \"x\")\n\n    return html.div(\n        html.button({\"onClick\": handle_topic_click}, f\"Topic {topic}\"),\n        html.p(last),\n    )\n\n\n@component\ndef app():\n    return dashboard()\n
# TODO\n

Verified: after changing the topic, the same line reads (topic was newsx). The Effect still has exactly one dependency; only the ref's contents changed.

This is the same trap as a locally defined function used as an Effect dependency (Lifecycle of Reactive Effects), and it is nastier inside a Hook because the code that lists the dependency and the code that created the callback live in different files.

"},{"location":"learn/reusing-logic-with-custom-hooks/#when-a-hook-is-called-conditionally-nothing-fails","title":"When a Hook is called conditionally, nothing fails","text":"

This is the section React's version of the page does not need, because React's compiler and dev-mode warnings are not present here.

ReactPy matches Hooks to their state slots by call order. Nothing names them, and nothing checks them. When the number of Hooks called changes between renders, the slots shift and values land in the wrong variables:

app.py Run
def use_profile(show_nickname):\n    name, set_name = use_state(\"name\")\n    if show_nickname:\n        nickname, set_nickname = use_state(\"nickname\")\n    else:\n        nickname = \"-\"\n    # `bio` is the third hook when the branch is taken and the second when it\n    # is not, so it inherits the slot that `nickname` used to own\n    bio, set_bio = use_state(\"bio\")\n    return name, nickname, bio, set_bio\n\n\n@component\ndef profile(show_nickname):\n    name, nickname, bio, set_bio = use_profile(show_nickname)\n\n    async def extend_bio(event):\n        set_bio(bio + \"!\")\n\n    return html.div(\n        html.button({\"onClick\": extend_bio}, f\"bio={bio}\"),\n        html.p(f\"name={name} nickname={nickname}\"),\n    )\n\n\n@component\ndef app():\n    show, set_show = use_state(True)\n\n    async def handle_show_click(e):\n        set_show(not show)\n\n    return html.div(\n        html.button({\"onClick\": handle_show_click}, \"toggle nickname\"),\n        profile(show),\n    )\n
# TODO\n

Verified: press bio= twice, then toggle nickname. The page prints

bio=bio!!          # before the branch changes\nbio=nickname       # after \u2014 bio is now nickname's state\n

No exception. No warning. No log record \u2014 the probe captured a completely silent reactpy logger across the whole transition. bio is the third Hook when the branch is taken and the second when it is not, so after the flip it reads the box that nickname used to own.

The corrected version calls every Hook unconditionally and makes only the return value conditional:

app.py Run
def use_profile(show_nickname):\n    name, set_name = use_state(\"name\")\n    # the hook is called unconditionally; only the returned value is conditional\n    nickname, set_nickname = use_state(\"nickname\")\n    bio, set_bio = use_state(\"bio\")\n    return name, (nickname if show_nickname else \"-\"), bio, set_bio\n\n\n@component\ndef profile(show_nickname):\n    name, nickname, bio, set_bio = use_profile(show_nickname)\n\n    async def extend_bio(event):\n        set_bio(bio + \"!\")\n\n    return html.div(\n        html.button({\"onClick\": extend_bio}, f\"bio={bio}\"),\n        html.p(f\"name={name} nickname={nickname}\"),\n    )\n\n\n@component\ndef app():\n    show, set_show = use_state(True)\n\n    async def handle_show_click(e):\n        set_show(not show)\n\n    return html.div(\n        html.button({\"onClick\": handle_show_click}, \"toggle nickname\"),\n        profile(show),\n    )\n
# TODO\n

Verified with the same script: bio=bio!! before and after the toggle. The toggle now changes only what is displayed.

"},{"location":"learn/reusing-logic-with-custom-hooks/#what-the-linter-can-see","title":"What the linter can see","text":"

REACTPY102 does fire on the broken file above \u2014 hook 'use_state' used inside if statement \u2014 provided the function is named as a Hook. That coverage is real and worth wiring into CI. So does REACTPY103, which catches the early-return case: a hook after a return reports hook 'use_state' used after an early return on line N. REACTPY101 catches hooks used outside any component or hook, and REACTPY100 hooks defined as closures. All four are in reactpy-flake8's RulesOfHooksVisitor. What it does not extend to:

  • calling a Hook-conditionally through a wrapper that the linter does not recognise (see the name table above),
  • a Hook list in one branch matching the caller's expectation in another,
  • the order the hooks actually run in \u2014 that is a runtime property, and nothing checks it.

And it checks dependency lists only under exhaustive_hook_deps=True, and even then only for a narrow set of shapes. See Lifecycle of Reactive Effects for exactly which. Treat the linter as a partial net and the fix \u2014 Hooks at the top level, always \u2014 as the actual rule.

Do not branch to disable a Hook

# wrong: the second Hook disappears when polling is off\nif polling:\n    use_async_effect(loop, dependencies=[topic])\n
Keep the call and branch inside the Hook body instead, returning None when there is nothing to do. See Lifecycle of Reactive Effects.

"},{"location":"learn/reusing-logic-with-custom-hooks/#passing-event-handlers-into-custom-hooks","title":"Passing event handlers into custom Hooks","text":"

A handler is an ordinary value: pass it in, call it, done \u2014 it does not need to be a dependency, and listing it does not make it fresher (measured above). The two shapes that work:

# the Hook calls it; the ref keeps it current\ndef use_polling(on_result):\n    latest = use_ref(on_result)\n    latest.current = on_result\n    ...\n\n# or the caller owns the callback entirely and the Hook just hands back a value\nselected, toggle = use_cart(items)\n

If you find yourself wanting to store a callback so it stays fresh, that is the same problem useEffectEvent solves in React \u2014 and ReactPy has no such Hook. The ref-updated-during-render idiom above is the equivalent, with the caveat that reading .current during render makes the render depend on an external value (Manipulating the DOM with Refs).

"},{"location":"learn/reusing-logic-with-custom-hooks/#when-to-use-custom-hooks","title":"When to use custom Hooks","text":"

Write one when you have logic \u2014 not markup \u2014 that two components genuinely need in the same shape. Good candidates in ReactPy:

  • selection, pagination, and form-draft logic
  • a subscription or polling loop with its clean-up wired correctly
  • reading a value from a use_connection-scoped resource
  • a compound updater that several components apply to the same kind of state

Keep them focused on a concrete use case. A use_async(fetcher, deps) that wraps use_async_effect in generic terms tends to encode one person's cancellation assumptions for everyone; the specific use_polling(topic, on_result) is easier to reason about and easier to change.

Two things a custom Hook is not:

  • A render bailout. ReactPy has no memo, so extracting a Hook does not reduce how often a component's body runs (Separating Events from Effects).
  • A state container. It cannot share state between components; only props and Context can.
"},{"location":"learn/reusing-logic-with-custom-hooks/#recap","title":"Recap","text":"
  • A custom Hook is a function named use_* that calls Hooks; extract one to reuse stateful logic.
  • State is per call site, in call order: the same Hook called twice is two independent boxes (verified).
  • Name them use_snake_case \u2014 the linter's regex is ^_*use_\\w+$, and camelCase versions get flagged as REACTPY101 instead of the accurate REACTPY102.
  • Never list a callback you were handed as a dependency: it compares code-equal and freezes the Effect on the first render. Read it through a ref updated during render.
  • Conditional Hook calls do not raise; state slots shift and values silently migrate between variables (verified: bio became nickname). Call every Hook unconditionally and branch on the return value.
  • reactpy-flake8 sees control-flow violations inside recognised Hooks, and nothing else \u2014 no dependency checks, no wrapper coverage.
"},{"location":"learn/reusing-logic-with-custom-hooks/#related","title":"Related","text":"
  • Synchronizing with Effects \u2014 the Effect patterns a Hook will most often wrap
  • Lifecycle of Reactive Effects \u2014 strictly_equal, frozen closures, and silent Effect failures
  • Separating Events from Effects \u2014 handlers, and why identity buys nothing here
  • Rules of Hooks and Components and Hooks Must Be Pure \u2014 the constraints a Hook inherits
  • Use State and Use Ref \u2014 parameter-level reference
"},{"location":"learn/scaling-up-with-reducer-and-context/","title":"Scaling Up with Reducer and Context","text":""},{"location":"learn/scaling-up-with-reducer-and-context/#overview","title":"Overview","text":"

A reducer keeps a component's update logic in one pure function; context carries values past components that only forward them. Together they let a deep tree read and change shared state without any wiring through props. In ReactPy there is one extra fact that makes this work cleanly: dispatch has a stable identity across renders, so it is safe to put in a context value.

You will learn

  • How to combine a reducer with context, in three steps
  • Why dispatch is safe to share through context in ReactPy
  • How to move all the wiring into a single module the UI never imports from directly
"},{"location":"learn/scaling-up-with-reducer-and-context/#combining-a-reducer-with-context","title":"Combining a reducer with context","text":"

Start with the version where state and dispatch travel as props. Only task_form and task want dispatch, but it has to be handed to everyone in between:

app.py Run
@component\ndef task_form(state, dispatch, task):\n    # The only component that actually wants dispatch...\n    async def handle_toggle(event):\n        dispatch({\"type\": \"toggled\", \"id\": task[\"id\"]})\n\n    return html.div(\n        {\"style\": {\"textDecoration\": \"line-through\" if task[\"done\"] else \"none\"}},\n        task[\"text\"],\n        html.button({\"onClick\": handle_toggle}, \"Toggle\"),\n    )\n\n\n@component\ndef task(state, dispatch, task):\n    # ...but must receive it and hand it on, unchanged.\n    async def handle_delete(event):\n        dispatch({\"type\": \"deleted\", \"id\": task[\"id\"]})\n\n    return html.li(\n        {\"key\": task[\"id\"]},\n        task_form(state, dispatch, task),\n        html.button({\"onClick\": handle_delete}, \"Delete\"),\n    )\n\n\n@component\ndef task_list(state, dispatch):\n    # ...and this one forwards both, without reading either.\n    return html.ul(\n        [task(state, dispatch, item, key=item[\"id\"]) for item in state[\"tasks\"]]\n    )\n\n\n@component\ndef task_app():\n    state, dispatch = use_reducer(tasks_reducer, {\"tasks\": INITIAL_TASKS, \"next_id\": 2})\n\n    async def handle_add(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add}, \"Add task\"),\n        task_list(state, dispatch),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
# TODO\n

Read the signatures: task_list(state, dispatch) receives both and reads neither \u2014 it only builds children and passes both along. task forwards state untouched to task_form, which uses only dispatch. Every new layer in the middle copies the same two parameters.

The reducer is already correct for ReactPy: module-level, reading nothing but its two arguments, returning new objects, and raising on an unknown action type. See Extracting State Logic into a Reducer for why each of those matters here.

"},{"location":"learn/scaling-up-with-reducer-and-context/#step-1-create-the-context","title":"Step 1: Create the context","text":"

One context carries both halves \u2014 the state to read and the dispatch to change it with. Keep it beside the reducer, in a module that does not import the UI:

app.pytasks.py Run
@component\ndef task_form(task):\n    # Reads dispatch from context; no prop arrived.\n    _, dispatch = use_context(TaskContext)\n\n    async def handle_toggle(event):\n        dispatch({\"type\": \"toggled\", \"id\": task[\"id\"]})\n\n    return html.div(\n        {\"style\": {\"textDecoration\": \"line-through\" if task[\"done\"] else \"none\"}},\n        task[\"text\"],\n        html.button({\"onClick\": handle_toggle}, \"Toggle\"),\n    )\n\n\n@component\ndef task(task):\n    _, dispatch = use_context(TaskContext)\n\n    async def handle_delete(event):\n        dispatch({\"type\": \"deleted\", \"id\": task[\"id\"]})\n\n    return html.li(\n        {\"key\": task[\"id\"]},\n        task_form(task),\n        html.button({\"onClick\": handle_delete}, \"Delete\"),\n    )\n\n\n@component\ndef task_list():\n    # Reads only the state; dispatch never has to come through here.\n    state, _ = use_context(TaskContext)\n    return html.ul([task(item, key=item[\"id\"]) for item in state[\"tasks\"]])\n\n\n@component\ndef task_app():\n    state, dispatch = use_reducer(tasks_reducer, INITIAL_STATE)\n\n    async def handle_add(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add}, \"Add task\"),\n        # One provider for the whole subtree.\n        TaskContext(task_list(), value=(state, dispatch)),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks\"),\n    )\n\n\n@component\ndef app():\n    return task_app()\n
\"\"\"The reducer, the context, and the initial state \u2014 all outside the UI.\"\"\"\n\nfrom collections.abc import Callable\nfrom typing import Any\n\nfrom reactpy import create_context\nfrom reactpy.types import Context\n\nTaskState = dict[str, Any]\nTaskValue = tuple[TaskState, Callable[[dict[str, Any]], None]]\n\n# The reducer never mutates what it is given, so one shared constant is\n# safe as the initial state for every instance.\nINITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n]\n\nINITIAL_STATE: TaskState = {\"tasks\": INITIAL_TASKS, \"next_id\": 2}\n\n# One context carries both halves: the state to read and the dispatch to change\n# it with. Annotate the name, or the default narrows the value type.\nTaskContext: Context[TaskValue] = create_context((INITIAL_STATE, lambda action: None))\n\n\ndef tasks_reducer(state: TaskState, action: dict[str, Any]) -> TaskState:\n    kind = action[\"type\"]\n    if kind == \"added\":\n        task = {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        return {\n            **state,\n            \"tasks\": [*state[\"tasks\"], task],\n            \"next_id\": state[\"next_id\"] + 1,\n        }\n    if kind == \"toggled\":\n        return {\n            **state,\n            \"tasks\": [\n                (\n                    {**task, \"done\": not task[\"done\"]}\n                    if task[\"id\"] == action[\"id\"]\n                    else task\n                )\n                for task in state[\"tasks\"]\n            ],\n        }\n    if kind == \"deleted\":\n        return {\n            **state,\n            \"tasks\": [task for task in state[\"tasks\"] if task[\"id\"] != action[\"id\"]],\n        }\n    raise ValueError(f\"unknown action type {kind!r}\")\n
# TODO\n

Two things to notice in tasks.py.

The context object is matched by identity, so create it once and import it from here everywhere. The annotation is load-bearing: create_context infers the value type from its default, so without Context[TaskValue] a type checker fixes the value to the default's shape and rejects a real one. Write the default first, then annotate. See Use Context.

The default value is a usable value, not None: an empty state and a dispatch that does nothing. A component rendered without a provider then renders an empty list instead of crashing when a handler fires.

"},{"location":"learn/scaling-up-with-reducer-and-context/#step-2-put-state-and-dispatch-into-context","title":"Step 2: Put state and dispatch into context","text":"

Call use_reducer once, in the component that owns the state, and provide both halves:

state, dispatch = use_reducer(tasks_reducer, INITIAL_STATE)\n...\nTaskContext(task_list(), value=(state, dispatch))\n

A tuple is the simplest value that carries both. If you prefer a dict or a dataclass, the same rules apply \u2014 but do not expect the value to control how often consumers render. ReactPy compares nothing here: use_context hands back whatever the provider is currently holding, and a provider that re-renders re-renders its children whether the value is new, equal, or literally the same object. Re-render cost is governed by where you place the provider, not by how you build the value.

"},{"location":"learn/scaling-up-with-reducer-and-context/#step-3-use-context-anywhere-in-the-tree","title":"Step 3: Use context anywhere in the tree","text":"

Now the middle of the tree forgets both parameters:

@component\ndef task_list():\n    state, _ = use_context(TaskContext)\n    return html.ul([task(item, key=item[\"id\"]) for item in state[\"tasks\"]])\n

task_list no longer mentions dispatch at all, and task and task_form each call use_context themselves. Verified: this version and the prop version above are behaviourally identical \u2014 the same initial two tasks, the same row struck through by Toggle, the same count after Add task, the same rows after Delete. All three versions in this page pass the same user-journey comparison.

Note

use_context reserves no state slot, so unlike use_state it is safe to call conditionally. That is not an invitation to hide it: read it at the top of the component, the way you would a prop, so it is obvious where the value comes from.

"},{"location":"learn/scaling-up-with-reducer-and-context/#why-dispatch-is-safe-to-share-in-reactpy","title":"Why dispatch is safe to share in ReactPy","text":"

In React, sharing dispatch through context is safe because React guarantees its identity. In ReactPy it is safe for a more concrete reason you can check in the source: use_reducer builds the dispatcher once with _use_const \u2014 a use_state holding the function \u2014 and returns that same object on every render. Verified: across five renders and four dispatches, id(dispatch) took exactly one value.

That matters because the dispatcher closes over the reducer, which ReactPy also freezes at the first render. A stale dispatcher is not a hazard here as long as the reducer is module-level \u2014 which is exactly the rule the reducer page already imposes. Share dispatch freely; do not share a value you expect to be re-read from a closure.

What context does not do: it is not a render optimisation. ReactPy has no memo, so when the provider re-renders with a new value, the whole subtree re-renders, including every component whose read value did not change. Context removes parameters, not work. See What context does not do.

"},{"location":"learn/scaling-up-with-reducer-and-context/#moving-all-wiring-into-a-single-file","title":"Moving all wiring into a single file","text":"

Once the UI reads everything from context, the reducer, the context object, and even use_reducer can disappear from it. Move the whole mechanism into tasks.py, and expose exactly two names \u2014 a provider component and an accessor hook:

app.pytasks.py Run
@component\ndef task_form(task):\n    # No props. The dispatch comes from the wiring file.\n    _, dispatch = use_tasks()\n\n    async def handle_toggle(event):\n        dispatch({\"type\": \"toggled\", \"id\": task[\"id\"]})\n\n    return html.div(\n        {\"style\": {\"textDecoration\": \"line-through\" if task[\"done\"] else \"none\"}},\n        task[\"text\"],\n        html.button({\"onClick\": handle_toggle}, \"Toggle\"),\n    )\n\n\n@component\ndef task(task):\n    _, dispatch = use_tasks()\n\n    async def handle_delete(event):\n        dispatch({\"type\": \"deleted\", \"id\": task[\"id\"]})\n\n    return html.li(\n        {\"key\": task[\"id\"]},\n        task_form(task),\n        html.button({\"onClick\": handle_delete}, \"Delete\"),\n    )\n\n\n@component\ndef task_list():\n    state, _ = use_tasks()\n    return html.ul([task(item, key=item[\"id\"]) for item in state[\"tasks\"]])\n\n\n@component\ndef task_app():\n    state, dispatch = use_tasks()\n\n    async def handle_add(event):\n        dispatch({\"type\": \"added\"})\n\n    return html.div(\n        html.h3(\"Prague itinerary\"),\n        html.button({\"onClick\": handle_add}, \"Add task\"),\n        task_list(),\n        html.p({\"id\": \"summary\"}, f\"{len(state['tasks'])} tasks\"),\n    )\n\n\n@component\ndef app():\n    # One name from the wiring file: the provider component.\n    return task_store(task_app())\n
\"\"\"All the reducer, context, and wiring lives here; the UI imports two names.\"\"\"\n\nfrom collections.abc import Callable\nfrom typing import Any\n\nfrom reactpy import component, create_context, use_context, use_reducer\nfrom reactpy.types import Context\n\nTaskState = dict[str, Any]\nTaskValue = tuple[TaskState, Callable[[dict[str, Any]], None]]\n\n# The reducer never mutates what it is given, so one shared constant is\n# safe as the initial state for every instance.\nINITIAL_TASKS = [\n    {\"id\": 0, \"text\": \"Packing\", \"done\": False},\n    {\"id\": 1, \"text\": \"Passport\", \"done\": True},\n]\n\nINITIAL_STATE: TaskState = {\"tasks\": INITIAL_TASKS, \"next_id\": 2}\n\nTaskContext: Context[TaskValue] = create_context((INITIAL_STATE, lambda action: None))\n\n\ndef tasks_reducer(state: TaskState, action: dict[str, Any]) -> TaskState:\n    kind = action[\"type\"]\n    if kind == \"added\":\n        task = {\"id\": state[\"next_id\"], \"text\": \"New task\", \"done\": False}\n        return {\n            **state,\n            \"tasks\": [*state[\"tasks\"], task],\n            \"next_id\": state[\"next_id\"] + 1,\n        }\n    if kind == \"toggled\":\n        return {\n            **state,\n            \"tasks\": [\n                (\n                    {**task, \"done\": not task[\"done\"]}\n                    if task[\"id\"] == action[\"id\"]\n                    else task\n                )\n                for task in state[\"tasks\"]\n            ],\n        }\n    if kind == \"deleted\":\n        return {\n            **state,\n            \"tasks\": [task for task in state[\"tasks\"] if task[\"id\"] != action[\"id\"]],\n        }\n    raise ValueError(f\"unknown action type {kind!r}\")\n\n\n@component\ndef task_store(*children: Any):\n    \"\"\"Owns the reducer and provides it. The UI never sees the reducer.\"\"\"\n    state, dispatch = use_reducer(tasks_reducer, INITIAL_STATE)\n    return TaskContext(*children, value=(state, dispatch))\n\n\ndef use_tasks() -> TaskValue:\n    \"\"\"The only accessor the UI needs: current state plus dispatch.\"\"\"\n    return use_context(TaskContext)\n
# TODO\n

The UI file now imports only task_store and use_tasks. It never mentions use_reducer, create_context, tasks_reducer, or TaskContext. Two consequences worth having:

  • The UI cannot reach the reducer except through an action, so there is no way to bypass it.
  • Changing how the state is stored \u2014 a different reducer, a different initial shape, even dropping context entirely \u2014 touches one file and no component.

task_store is a component that calls use_reducer and returns the provider. A component's render may return another component, so this composes without ceremony. Note that use_tasks is a plain function that calls use_context: it is a custom Hook, so name it use_* \u2014 the linter's regex is ^_*use_\\w+$, and camelCase variants get a less accurate diagnostic. See Reusing Logic with Custom Hooks.

Pitfall

Because the provider is now a component, removing it from the tree unmounts everything below it and throws their state away. Do not write task_store(page()) if logged_in else page(). Keep the provider in the tree and vary only its value \u2014 verified: a conditional provider resets a leaf's state (its render counter returns to 1 and its draft text clears), while a stable key on the child does not prevent it. See Removing a provider resets the subtree below it.

Two task_store instances are two independent stores, not one shared store: verified, three presses on one side moved one=5 while the other stayed at its initial two=2. Context propagates down from the provider that happens to be above you. To genuinely share state, there must be one provider above both consumers \u2014 usually near the root.

Deep Dive

Should you also provide a derived value, such as a filtered list or a total? Compute it in the component that needs it. ReactPy re-renders the subtree whenever the provided state changes anyway, so a cached derivation saves nothing, and use_memo around it adds a dependency list to keep correct. Reserve the context value for what is genuinely shared: the state object and dispatch. For the caching rules, see You Might Not Need an Effect.

"},{"location":"learn/scaling-up-with-reducer-and-context/#recap","title":"Recap","text":"
  • A reducer owns the transitions; context carries (state, dispatch) past components that would only forward them. Both are needed together \u2014 context without a reducer leaves the update logic scattered, a reducer without context leaves the props.
  • Create the context beside the reducer, at module level, with a usable default and a Context[...] annotation so the value type is not narrowed by the default.
  • Provide (state, dispatch) from one component that owns use_reducer; read it in each component that needs it.
  • dispatch is identity-stable across renders in ReactPy (verified: one id over five renders), which is what makes sharing it through context sound.
  • A module that exports a provider component and a use_* accessor lets the UI import two names and never touch the reducer.
  • Keep the provider in the tree. Removing it remounts the subtree and discards state; a key does not save it (verified).
  • One provider per shared store. Two providers are two stores (verified).
  • Context removes parameters, not renders \u2014 ReactPy has no render bailout.
"},{"location":"learn/scaling-up-with-reducer-and-context/#related","title":"Related","text":"
  • Extracting State Logic into a Reducer \u2014 the reducer rules this pattern depends on, including the frozen-first-reducer rule
  • Passing Data Deeply with Context \u2014 the context mechanics, and the provider-removal trap
  • Use Context \u2014 parameters, caveats, and error messages
  • Use Reducer \u2014 parameter-level reference and troubleshooting
  • Reusing Logic with Custom Hooks \u2014 naming and rules for the use_tasks accessor
  • Choosing the State Structure \u2014 what belongs in state before you decide who provides it
"},{"location":"learn/separating-events-from-effects/","title":"Separating Events from Effects","text":""},{"location":"learn/separating-events-from-effects/#overview","title":"Overview","text":"

Event handlers and Effects run at different times and for different reasons. Most Effect bugs are not Effect bugs \u2014 they are interaction logic that was put in the wrong place. Move it back and the Effect disappears.

You will learn

  • Why interaction logic belongs in a handler, not an Effect
  • Why handler logic is not reactive \u2014 and why that is a feature
  • How ReactPy picks which render's handler actually runs
  • What use_callback does in ReactPy, and what it does not buy you
  • How to read the newest value from a callback you cannot re-create
"},{"location":"learn/separating-events-from-effects/#choosing-between-event-handlers-and-effects","title":"Choosing between event handlers and Effects","text":"

The question is never \"which hook?\" but \"when should this logic run?\"

Event handler Effect Runs when the interaction happens after a render, whenever a dependency changed Re-runs when only if the user repeats the interaction any dependency changes \u2014 including one you forgot to list Right for submit, click, input, drag, \"do this now\" subscribing, polling, syncing to a server

Both cost one render per click in the examples below \u2014 the difference is not performance, it is what triggers the work. An Effect that sets state it also lists as a dependency re-triggers itself; measured, that loop reached 86 renders before it was stopped. A handler has no such feedback path, which is most of why interaction logic belongs there.

If the logic should run when the user does the thing, it goes in the handler. If it should run whenever a value is a certain way, it goes in an Effect.

"},{"location":"learn/separating-events-from-effects/#logic-in-an-effect-driven-by-click-only-state","title":"Logic in an Effect, driven by click-only state","text":"

Here the shipping quote is computed by an Effect watching a flag that only a click sets:

app.py Run
def calculate_shipping(city, country):\n    \"\"\"Plain function: takes what it needs, returns the result.\"\"\"\n    if not city or not country:\n        return \"\"\n    return f\"{city}, {country} \u2014 ${15 + 5}\"\n\n\n@component\ndef shipping_form():\n    city, set_city = use_state(\"\")\n    country, set_country = use_state(\"\")\n    # state that exists purely to make the Effect run\n    quoted, set_quoted = use_state(False)\n    shipping_info, set_shipping_info = use_state(\"\")\n    clicks, set_clicks = use_state(0)\n\n    async def submit(event):\n        set_clicks(clicks + 1)\n        set_quoted(True)\n\n    # the \"intent\" is buried in an Effect watching a flag a click merely sets\n    def recalculate():\n        if quoted:\n            result = calculate_shipping(city, country)\n            set_shipping_info(result)\n            if result:\n                print(f\"quoted {result}\")\n\n    use_effect(recalculate, dependencies=[quoted])\n\n    async def handle_city_change(e):\n        set_city(await e.target.value)\n\n    async def handle_country_change(e):\n        set_country(await e.target.value)\n\n    return html.div(\n        html.label(\n            \"City: \",\n            html.input({\"value\": city, \"onChange\": handle_city_change}),\n        ),\n        html.label(\n            \"Country: \",\n            html.input(\n                {\n                    \"value\": country,\n                    \"onChange\": handle_country_change,\n                }\n            ),\n        ),\n        html.button({\"onClick\": submit}, \"Get quote\"),\n        html.p(shipping_info or \"\u2014\"),\n        f\"Clicks: {clicks}\",\n    )\n\n\n@component\ndef app():\n    return shipping_form()\n
# TODO\n

This is where the ReactJS version of this example stops matching reality. Verified behaviour: type a city and country, then press Get quote three times.

  • Click 1 logs quoted Portland, US \u2014 $20.
  • Clicks 2 and 3 increment the counter and log nothing at all.

set_quoted(True) sets the same value every time, so ReactPy sees no change, and the dependency list says nothing re-synchronized. The user gets a button that works once. Nothing about this is obvious from the code \u2014 the Effect looks correct.

"},{"location":"learn/separating-events-from-effects/#the-same-logic-in-the-handler","title":"The same logic in the handler","text":"app.py Run
def calculate_shipping(city, country):\n    \"\"\"Plain function: takes what it needs, returns the result.\"\"\"\n    if not city or not country:\n        return \"\"\n    return f\"{city}, {country} \u2014 ${15 + 5}\"\n\n\n@component\ndef shipping_form():\n    city, set_city = use_state(\"\")\n    country, set_country = use_state(\"\")\n    shipping_info, set_shipping_info = use_state(\"\")\n    clicks, set_clicks = use_state(0)\n\n    async def submit(event):\n        set_clicks(clicks + 1)\n        result = calculate_shipping(city, country)\n        set_shipping_info(result)\n        if result:\n            print(f\"quoted {result}\")\n\n    async def handle_city_change(e):\n        set_city(await e.target.value)\n\n    async def handle_country_change(e):\n        set_country(await e.target.value)\n\n    return html.div(\n        html.label(\n            \"City: \",\n            html.input({\"value\": city, \"onChange\": handle_city_change}),\n        ),\n        html.label(\n            \"Country: \",\n            html.input(\n                {\n                    \"value\": country,\n                    \"onChange\": handle_country_change,\n                }\n            ),\n        ),\n        html.button({\"onClick\": submit}, \"Get quote\"),\n        html.p(shipping_info or \"\u2014\"),\n        f\"Clicks: {clicks}\",\n    )\n\n\n@component\ndef app():\n    return shipping_form()\n
# TODO\n

No trigger state, no Effect, no use_effect import. Verified: every click logs quoted Portland, US \u2014 $20, at the same one-render-per-click cost as a plain button.

The general rule: if the only thing that sets a piece of state is an event handler, and the only thing that reads it is an Effect, delete both and call the function directly.

When an Effect really is the right home

Keep the Effect when the logic must also run for reasons other than the interaction: the value changed while the user was idle, the component just mounted, another component changed something. \"The user pressed Save\" is a handler; \"the document is dirty and should be persisted\" is an Effect.

"},{"location":"learn/separating-events-from-effects/#reactive-values-and-reactive-logic","title":"Reactive values and reactive logic","text":"

Every value in the component body belongs to one render. Logic placed in the component body or in an Effect re-runs when those values change. Logic inside a handler does not \u2014 it runs when called, on whatever render produced it.

"},{"location":"learn/separating-events-from-effects/#logic-inside-event-handlers-is-not-reactive","title":"Logic inside event handlers is not reactive","text":"
async def submit(event):\n    print(f\"submitting {city}, {country}\")   # reads one render's values\n    send(city, country)\n

Nothing about this code will re-run when city changes. That is not a defect: a click should do the thing once, using the values the user saw when they clicked. Making handler logic \"reactive\" is what produces double-submits and the click-once bug above.

"},{"location":"learn/separating-events-from-effects/#logic-inside-effects-is-reactive","title":"Logic inside Effects is reactive","text":"
def reconnect():\n    open_socket(room)\n\nuse_effect(reconnect, dependencies=[room])\n

This one re-runs on its own whenever room changes, including changes you did not expect. That is the whole point of an Effect \u2014 and the whole reason logic you don't want repeated does not belong there.

"},{"location":"learn/separating-events-from-effects/#extracting-non-reactive-logic-out-of-effects","title":"Extracting non-reactive logic out of Effects","text":"

Once intent lives in the handler, the shared part is usually an ordinary function. Give it its inputs as parameters and both sides can use it:

def calculate_shipping(city, country):\n    if not city or not country:\n        return \"\"\n    return f\"{city}, {country} \u2014 ${15 + 5}\"\n\n\n@component\ndef shipping_form():\n    ...\n\n    async def submit(event):\n        set_shipping_info(calculate_shipping(city, country))\n

No hook needed. A function that only reads values and returns something does not need to be a hook, and making it one costs a render.

ReactPy has no useEffectEvent, no useOptimistic, and no useTransition \u2014 verified against reactpy at main. That is not a gap to route around with an Effect: the handler-is-not-reactive property that Effect Events exist to give you in React is what you already have here for free.

"},{"location":"learn/separating-events-from-effects/#which-handler-actually-runs","title":"Which handler actually runs","text":"

ReactPy keeps one handler per target, registered when the component renders (core/layout.py, _event_handlers), and looks it up at dispatch time \u2014 when the event is processed, not when it arrived.

Two things follow, both measured.

A normal click sees current values. Click after click with the connection settling between them, and the handler always reads the latest state:

added item, discount=0\nadded item, discount=10\nadded item, discount=20\n

Events that arrive before the next render all see the same snapshot. Deliver two clicks without a render in between and both run the same closure:

app.py Run
@component\ndef basket():\n    items, set_items = use_state([])\n    discount, set_discount = use_state(0)\n\n    async def add_naive(event):\n        # reads `items` and `discount` from whichever render created this handler\n        print(f\"naive: {len(items)} items, discount={discount}\")\n        set_items([*items, f\"item-{len(items) + 1}@{discount}\"])\n\n    async def add_safe(event):\n        # the updater receives the *current* state at the moment it is applied\n        def append(current):\n            print(f\"safe: {len(current)} items, discount={discount}\")\n            return [*current, f\"item-{len(current) + 1}@{discount}\"]\n\n        set_items(append)\n\n    async def handle_discount_click(e):\n        set_discount(discount + 10)\n\n    return html.div(\n        html.button({\"onClick\": add_naive}, \"Add (naive)\"),\n        html.button({\"onClick\": add_safe}, \"Add (safe)\"),\n        html.button({\"onClick\": handle_discount_click}, f\"Coupon {discount}%\"),\n        html.p(\", \".join(items) or \"empty basket\"),\n    )\n\n\n@component\ndef app():\n    return basket()\n
# TODO\n

Verified with two clicks delivered back-to-back:

Handler log Final list Add (naive) 0 items twice item-1@0 \u2014 the second click is gone Add (safe) 0 items, then 1 items item-1@0, item-2@0

This is not a hypothetical. On a real connection, events are queued per target (core/layout.py, _process_event_queue) and a double-click, a fast key repeat, or any latency between the browser and a busy server produces exactly this. The naive set_items(items + [...]) reads a snapshot; the updater set_items(lambda current: ...) is applied against the state at the moment it lands.

So the handler-staleness story in ReactPy is narrower than in React, and it is worth being precise:

  • You generally do not need use_callback to \"get fresh values\" in a handler \u2014 the handler that runs is the current one.
  • You do need the functional updater form whenever an update depends on the current state and two events can arrive in the same round trip.
"},{"location":"learn/separating-events-from-effects/#handlers-which-await","title":"Handlers which await","text":"

Every handler is a coroutine, and it may set state after an await:

async def submit(event):\n    set_status(\"saving\")\n    result = await save(document)\n    set_status(\"saved\")          # works \u2014 the render happens after the await\n

Verified: the page shows status=saving immediately, then status=saved once the await resolves \u2014 two renders, both after the click.

But if the component unmounts during the await, the update is discarded and the only trace is a debug line \u2014 Did not render component with model state ID ... - component already unmounted. Nothing errors, nothing warns, and the user who navigated away is unaffected. Do not treat \"the setter did not raise\" as evidence the write landed; if it matters, assert it in a test that keeps the component mounted.

"},{"location":"learn/separating-events-from-effects/#keeping-a-callbacks-identity-stable","title":"Keeping a callback's identity stable","text":"

use_callback exists and memoizes through use_memo, so it shares the dependency semantics described on Lifecycle of Reactive Effects \u2014 strictly_equal, not id.

app.py Run
@component\ndef toolbar():\n    count, set_count = use_state(0)\n    theme, set_theme = use_state(\"light\")\n    previous = use_ref(object())\n\n    async def stable_handler(event):\n        set_count(count + 1)\n\n    # inferred dependencies: `count` is the only free variable\n    on_click = use_callback(stable_handler)\n\n    reused = previous.current is on_click\n    previous.current = on_click\n\n    async def handle_theme_click(e):\n        set_theme(\"dark\" if theme == \"light\" else \"light\")\n\n    return html.div(\n        html.button({\"onClick\": on_click}, f\"Count {count}\"),\n        html.button(\n            {\"onClick\": handle_theme_click},\n            f\"Theme {theme}\",\n        ),\n        html.p(\"handler reused on this render\" if reused else \"handler recreated\"),\n    )\n\n\n@component\ndef app():\n    return toolbar()\n
# TODO\n

Verified: pressing Theme (not a dependency) reports handler reused on this render; pressing Count (a dependency) reports handler recreated.

Be clear about what that buys you. ReactPy has no render bailout: there is no memo (absent from both reactpy and reactpy.reactjs), and a child re-renders whenever its parent does. Measured \u2014 a child whose props and handler never changed ran its body 4 times across one mount and three parent re-renders. So a stable callback does not make anything re-render less. What it does is keep a value out of an Effect's or use_memo's dependency list without re-triggering it.

There is no useEvent, so the nearest equivalent to React's Effect Event \u2014 a stable callable that always reads the newest values \u2014 is a ref updated during render:

app.py Run
@component\ndef checkout():\n    coupon, set_coupon = use_state(\"\")\n    total, set_total = use_state(0)\n    coupon_ref = use_ref(\"\")\n    # updated during render, so any callback reads the value as of the click\n    coupon_ref.current = coupon\n\n    # pinned to nothing: the identity never changes, yet it reads `coupon_ref`\n    async def apply_coupon_handler(event):\n        print(f\"applying coupon={coupon_ref.current!r}\")\n\n    apply_coupon = use_callback(apply_coupon_handler, dependencies=[])\n\n    async def handle_coupon_change(e):\n        # The await comes first: `.upper()` runs on the string which comes back,\n        # not on the proxy. Without the parens you would call `.upper()` on the\n        # proxy itself and silently get the un-uppercased value.\n        set_coupon((await e.target.value).upper())\n\n    async def handle_total_click(e):\n        set_total(total + 10)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": coupon,\n                \"onChange\": handle_coupon_change,\n            }\n        ),\n        html.button({\"onClick\": apply_coupon}, \"Apply coupon\"),\n        html.button({\"onClick\": handle_total_click}, f\"Total {total}\"),\n        html.p(f\"page shows coupon={coupon!r} \u2014 compare with the console\"),\n    )\n\n\n@component\ndef app():\n    return checkout()\n
# TODO\n

Verified: Apply coupon logs the value currently in the box even though the callback is pinned to dependencies=[] and is never re-created.

A callback pinned to a local function never updates

Same trap as Effects, and it is worse here because the callback is supposed to be stable. dependencies=[describe], where describe is a local def, compares code-equal forever:

app.py Run
@component\ndef search():\n    query, set_query = use_state(\"\")\n    runs, set_runs = use_state(0)\n\n    def describe():\n        return f\"query={query!r}\"\n\n    async def run(event):\n        # `describe` is re-created every render but compares code-equal, so this\n        # callback is still the one from the very first render. The functional\n        # updater keeps the counter honest; only `describe()` is frozen.\n        print(f\"handler sees {describe()}\")\n        set_runs(lambda current: current + 1)\n\n    on_click = use_callback(run, dependencies=[describe])\n\n    async def handle_query_change(e):\n        set_query(await e.target.value)\n\n    return html.div(\n        html.input({\"value\": query, \"onChange\": handle_query_change}),\n        html.button({\"onClick\": on_click}, f\"Run ({runs})\"),\n        html.p(f\"page shows query={query!r} \u2014 compare with the console\"),\n    )\n\n\n@component\ndef app():\n    return search()\n
# TODO\n

Verified: type shoes, press Run, type shoes2, press Run again. The page shows the new query both times; the console says handler sees query='' both times. The counter keeps counting (Run (2)), which is what makes this so easy to miss \u2014 the callback looks alive while reading a snapshot from mount.

Depend on the scalar values the callback actually reads, and never on a function defined inside the component.

"},{"location":"learn/separating-events-from-effects/#recap","title":"Recap","text":"
  • Ask when the logic should run. Interaction \u2192 handler. Synchronization \u2192 Effect.
  • State that only a handler sets and only an Effect reads is a smell: call the function from the handler and delete both. Verified symptom: the button works once.
  • Handler logic is deliberately not reactive; that is why you do not need React's Effect Events here.
  • ReactPy stores one handler per target and looks it up at dispatch, so a normal click sees current values \u2014 but events delivered before the next render share one snapshot. Use the functional updater for read-modify-write.
  • An async handler may set state after await; after unmount that update is silently dropped.
  • use_callback pins identity, not freshness, and identity buys you nothing for render cost in ReactPy. Reach for a render-updated ref when you need both.
  • Never list a locally defined function as a dependency of a callback or Effect \u2014 it compares code-equal and freezes the closure.
"},{"location":"learn/separating-events-from-effects/#related","title":"Related","text":"
  • Lifecycle of Reactive Effects \u2014 dependency semantics, strictly_equal, and silent Effect failures
  • You Might Not Need an Effect \u2014 logic that belongs in the render
  • Synchronizing with Effects \u2014 the patterns that genuinely need an Effect
  • Use Callback and Use Ref \u2014 parameter-level reference
"},{"location":"learn/sharing-state-between-components/","title":"Sharing State Between Components","text":""},{"location":"learn/sharing-state-between-components/#overview","title":"Overview","text":"

Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as lifting state up, and it's one of the most common things you will do writing ReactPy code.

You will learn

  • How to share state between two components
  • How to coordinate components by lifting state to their common parent
  • What controlled and uncontrolled components are
"},{"location":"learn/sharing-state-between-components/#lifting-state-up-by-example","title":"Lifting state up by example","text":"

In this example, a parent accordion() component renders two separate panel()s:

  • accordion()
    • panel()
    • panel()

Each panel() component has a boolean is_active state that determines whether its content is visible.

Press the Show button for both panels:

app.pystyles.css Run
@component\ndef panel(title, *children):\n    is_active, set_is_active = use_state(False)\n\n    async def handle_is_active_click(event):\n        set_is_active(True)\n\n    return html.section(\n        {\"className\": \"panel\"},\n        html.h3(title),\n        (\n            html.p(*children)\n            if is_active\n            else html.button({\"onClick\": handle_is_active_click}, \"Show\")\n        ),\n    )\n\n\n@component\ndef accordion():\n    return html(\n        html.h2(\"Almaty, Kazakhstan\"),\n        panel(\n            \"About\",\n            \"With a population of about 2 million, Almaty is Kazakhstan's \"\n            \"largest city. From 1929 to 1997, it was its capital city.\",\n        ),\n        panel(\n            \"Etymology\",\n            \"The name comes from \u0430\u043b\u043c\u0430, the Kazakh word for \u201capple\u201d and is \"\n            \"often translated as \u201cfull of apples\u201d.\",\n        ),\n    )\n
h3,\np {\n    margin: 5px 0;\n}\n\n.panel {\n    padding: 10px;\n    border: 1px solid #aaa;\n}\n
# TODO\n

Notice how pressing one panel's button does not affect the other panel \u2014 they are independent.

But now let's say you want to change it so that only one panel is expanded at any given time. With that design, expanding the second panel should collapse the first one. How would you do that?

To coordinate these two panels, you need to \"lift their state up\" to a parent component in three steps:

  1. Remove state from the child components.
  2. Pass hardcoded data from the common parent.
  3. Add state to the common parent and pass it down together with the event handlers.

This will allow the accordion() component to coordinate both panel()s and only expand one at a time.

"},{"location":"learn/sharing-state-between-components/#step-1-remove-state-from-the-child-components","title":"Step 1: Remove state from the child components","text":"

You will give control of the panel()'s is_active to its parent component. This means that the parent component will pass is_active to panel() as a prop instead. Start by removing this line from the panel() component:

is_active, set_is_active = use_state(False)\n

And instead, add is_active to the panel()'s list of props:

@component\ndef panel(title, is_active, *children):\n

Now the panel()'s parent component can control is_active by passing it down as a prop. Conversely, the panel() component now has no control over the value of is_active \u2014 it's now up to the parent component!

"},{"location":"learn/sharing-state-between-components/#step-2-pass-hardcoded-data-from-the-common-parent","title":"Step 2: Pass hardcoded data from the common parent","text":"

To lift state up, you must locate the closest common parent component of both of the child components that you want to coordinate:

  • accordion() (closest common parent)
    • panel()
    • panel()

In this example, it's the accordion() component. Since it's above both panels and can control their props, it will become the \"source of truth\" for which panel is currently active. Make the accordion() component pass a hardcoded value of is_active (for example, True) to both panels:

app.pystyles.css Run
@component\ndef panel(title, is_active, *children):\n    return html.section(\n        {\"className\": \"panel\"},\n        html.h3(title),\n        html.p(*children) if is_active else html.button(\"Show\"),\n    )\n\n\n@component\ndef accordion():\n    return html(\n        html.h2(\"Almaty, Kazakhstan\"),\n        panel(\"About\", True, \"With a population of about 2 million...\"),\n        panel(\"Etymology\", True, \"The name comes from \u0430\u043b\u043c\u0430...\"),\n    )\n
h3,\np {\n    margin: 5px 0;\n}\n\n.panel {\n    padding: 10px;\n    border: 1px solid #aaa;\n}\n
# TODO\n

Try editing the hardcoded is_active values in the accordion() component and see the result on the screen.

"},{"location":"learn/sharing-state-between-components/#step-3-add-state-to-the-common-parent","title":"Step 3: Add state to the common parent","text":"

Lifting state up often changes the nature of what you're storing as state.

In this case, only one panel should be active at a time. This means that the accordion() common parent component needs to keep track of which panel is the active one. Instead of a bool value, it could use a number as the index of the active panel() for the state variable:

active_index, set_active_index = use_state(0)\n

When the active_index is 0, the first panel is active, and when it's 1, it's the second one.

Clicking the \"Show\" button in either panel() needs to change the active index in accordion(). A panel() can't set the active_index state directly because it's defined inside the accordion(). The accordion() component needs to explicitly allow the panel() component to change its state by passing an event handler down as a prop:

async def show_about(event):\n    set_active_index(0)\n\n\npanel(\n    \"About\",\n    active_index == 0,\n    show_about,\n    ...\n)\n

A handler must be a coroutine, and a coroutine cannot be a lambda, so each panel gets a named handler. That is what the real example below does, twice.

The html.button() inside the panel() will now use the on_show prop as its click event handler:

app.pystyles.css Run
@component\ndef panel(title, is_active, on_show, *children):\n    return html.section(\n        {\"className\": \"panel\"},\n        html.h3(title),\n        (html.p(*children) if is_active else html.button({\"onClick\": on_show}, \"Show\")),\n    )\n\n\n@component\ndef accordion():\n    active_index, set_active_index = use_state(0)\n\n    async def show_about(event):\n        set_active_index(0)\n\n    async def show_etymology(event):\n        set_active_index(1)\n\n    return html(\n        html.h2(\"Almaty, Kazakhstan\"),\n        panel(\n            \"About\",\n            active_index == 0,\n            show_about,\n            \"With a population of about 2 million, Almaty is Kazakhstan's \"\n            \"largest city. From 1929 to 1997, it was its capital city.\",\n        ),\n        panel(\n            \"Etymology\",\n            active_index == 1,\n            show_etymology,\n            \"The name comes from \u0430\u043b\u043c\u0430, the Kazakh word for \u201capple\u201d and is \"\n            \"often translated as \u201cfull of apples\u201d.\",\n        ),\n    )\n
h3,\np {\n    margin: 5px 0;\n}\n\n.panel {\n    padding: 10px;\n    border: 1px solid #aaa;\n}\n
# TODO\n

This completes lifting state up! Moving state into the common parent component allowed you to coordinate the two panels. Using the active index instead of two \"is shown\" flags ensured that only one panel is active at a given time. And passing down the event handler to the child allowed the child to change the parent's state.

"},{"location":"learn/sharing-state-between-components/#controlled-and-uncontrolled-components","title":"Controlled and uncontrolled components","text":"

It is common to call a component with some local state \"uncontrolled\". For example, the original panel() component with an is_active state variable is uncontrolled because its parent cannot influence whether the panel is active or not.

In contrast, you might say a component is \"controlled\" when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final panel() component with the is_active prop is controlled by the accordion() component.

Uncontrolled components are easier to use within their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.

In practice, \"controlled\" and \"uncontrolled\" aren't strict technical terms \u2014 each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.

When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later.

"},{"location":"learn/sharing-state-between-components/#a-single-source-of-truth-for-each-state","title":"A single source of truth for each state","text":"

In a ReactPy application, many components will have their own state. Some state may \"live\" close to the leaf components (components at the bottom of the tree) like inputs. Other state may \"live\" closer to the top of the app. For example, a router implemented in ReactPy would keep the current route in the state of a component near the root, and pass it down by props.

For each unique piece of state, you will choose the component that \"owns\" it. This principle is also known as having a \"single source of truth\". It doesn't mean that all state lives in one place \u2014 but that for each piece of state, there is a specific component that holds that piece of information. Instead of duplicating shared state between components, lift it up to their common shared parent, and pass it down to the children that need it.

Your app will change as you work on it. It is common that you will move state down or back up while you're still figuring out where each piece of the state \"lives\". This is all part of the process!

To see what this feels like in practice with a few more components, read Thinking in React.

"},{"location":"learn/sharing-state-between-components/#summary","title":"Summary","text":"
  • When you want to coordinate two components, move their state to their common parent.
  • Then pass the information down through props from their common parent.
  • Finally, pass the event handlers down so that the children can change the parent's state.
  • It's useful to consider components as \"controlled\" (driven by props) or \"uncontrolled\" (driven by state).
"},{"location":"learn/state-a-components-memory/","title":"State: A Component's Memory","text":""},{"location":"learn/state-a-components-memory/#overview","title":"Overview","text":"

Components often need to change what's on the screen as a result of an interaction. Typing into a form should update the input field, clicking \"next\" on an image carousel should change which image is displayed, clicking \"buy\" should put a product in the shopping cart. Components need to \"remember\" things: the current input value, the current image, the shopping cart. In ReactPy, this kind of component-specific memory is called state.

You will learn

  • How to add a state variable with use_state
  • What pair of values use_state returns
  • How to add more than one state variable
  • Why state is called local
"},{"location":"learn/state-a-components-memory/#when-a-regular-variable-isnt-enough","title":"When a regular variable isn't enough","text":"

Here's a component that renders a sculpture image. Clicking \"Next\" should show the next sculpture by changing index to 1, then 2, and so on. However, this won't work (you can try it!):

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index = 0\n\n    async def handle_next(event):\n        # `nonlocal` is what makes this assignment legal Python at all; without\n        # it the nested function gets its own `index` and the read on the right\n        # hand side raises UnboundLocalError. JS closures need no such keyword.\n        nonlocal index\n        index = index + 1\n\n    sculpture = sculptures[index]\n    return html.div(\n        html.button({\"onClick\": handle_next}, \"Next\"),\n        html.h2(html.i(sculpture[\"name\"]), \" by \", sculpture[\"artist\"]),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n        html.p(sculpture[\"description\"]),\n    )\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n

The handle_next event handler is updating a local variable, index. But two things prevent that change from being visible:

  1. Local variables don't persist between renders. When ReactPy renders this component a second time, it renders it from scratch \u2014 it doesn't consider any changes to the local variables.
  2. Changes to local variables won't trigger renders. ReactPy doesn't realize it needs to render the component again with the new data.

Both were checked against this exact component: clicking \"Next\" three times leaves the served VDOM byte-identical, and a later render caused by real state shows index back at 0 even though the handler had bumped it to 3.

To update a component with new data, two things need to happen:

  1. Retain the data between renders.
  2. Trigger ReactPy to render the component with new data (re-rendering).

use_state provides those two things:

  1. A state variable to retain the data between renders.
  2. A state setter function to update the variable and trigger ReactPy to render the component again.
"},{"location":"learn/state-a-components-memory/#adding-a-state-variable","title":"Adding a state variable","text":"

To add a state variable, import use_state from reactpy at the top of the file:

from reactpy import component, html, use_state\n

Then replace this line:

index = 0\n

with

index, set_index = use_state(0)\n

index is a state variable and set_index is the setter function.

Python gives you the destructuring for free

React's const [index, setIndex] = useState(0) needs array destructuring syntax. In Python this is ordinary tuple unpacking, so there is nothing new to learn here \u2014 and unlike JavaScript, leaving out a name is a syntax error rather than a silent undefined.

This is how they work together in handle_next:

async def handle_next(event):\n    set_index(index + 1)\n

The handler needs nonlocal to read its own variable

If you keep the plain-variable version, the assignment inside the nested function is not legal Python without a declaration:

async def handle_next(event):\n    nonlocal index\n    index = index + 1\n

Without it, Python treats index as a new local of handle_next, and reading it on the right-hand side raises UnboundLocalError. This is a Python scoping rule, not a ReactPy one \u2014 JavaScript closures just work. With use_state you never need it, because you read index and call set_index instead of assigning.

Now clicking the \"Next\" button switches the current sculpture:

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index, set_index = use_state(0)\n\n    async def handle_next(event):\n        set_index(index + 1)\n\n    sculpture = sculptures[index]\n    return html.div(\n        html.button({\"onClick\": handle_next}, \"Next\"),\n        html.h2(html.i(sculpture[\"name\"]), \" by \", sculpture[\"artist\"]),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n        html.p(sculpture[\"description\"]),\n    )\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n
"},{"location":"learn/state-a-components-memory/#meet-your-first-hook","title":"Meet your first Hook","text":"

In ReactPy, use_state, as well as any other function starting with use, is called a Hook.

Hooks are special functions that are only available while ReactPy is rendering (which we'll get into in more detail on the next page). They let you \"hook into\" different ReactPy features.

State is just one of those features, but you will meet the other Hooks later.

Call Hooks only at the top level

Hooks \u2014 functions starting with use \u2014 can only be called at the top level of your components or your own Hooks. You can't call Hooks inside conditions, loops, or other nested functions. Hooks are functions, but it's helpful to think of them as unconditional declarations about your component's needs. You \"use\" ReactPy features at the top of your component similar to how you \"import\" modules at the top of your file.

The check is not just advice: the reactpy-flake8 plugin (the ReactPy analogue of eslint-plugin-react-hooks, and part of this repo's own lint config) reports REACTPY102 for a Hook under a branch or in a loop:

@component\ndef bad(flag):\n    a, _ = use_state(\"A\")\n    if flag:                      # REACTPY102 hook 'use_state' used inside if statement\n        b, _ = use_state(\"B\")\n        return html.p(b)\n    return html.p(a)\n
"},{"location":"learn/state-a-components-memory/#anatomy-of-use_state","title":"Anatomy of use_state","text":"

When you call use_state, you are telling ReactPy that you want this component to remember something:

index, set_index = use_state(0)\n

In this case, you want ReactPy to remember index.

The convention is to name this pair like something, set_something. You could name it anything you like, but conventions make things easier to understand across projects.

The only argument to use_state is the initial value of your state variable. In this example, the index's initial value is set to 0 with use_state(0).

Every time your component renders, use_state gives you a pair containing two values:

  1. The state variable (index) with the value you stored.
  2. The state setter function (set_index) which can update the state variable and trigger ReactPy to render the component again.

Here's how that happens in action:

index, set_index = use_state(0)\n
  1. Your component renders the first time. Because you passed 0 to use_state as the initial value for index, it returns (0, set_index). ReactPy remembers 0 is the latest state value.
  2. You update the state. When a user clicks the button, it calls set_index(index + 1). index is 0, so it's set_index(1). This tells ReactPy to remember index is 1 now and triggers another render.
  3. Your component's second render. ReactPy still sees use_state(0), but because ReactPy remembers that you set index to 1, it returns (1, set_index) instead.
  4. And so on!

Pass a function for an expensive initial value

use_state accepts a callable and calls it only when the state is first created: use_state(lambda: json.loads(big_blob)). Passing the result of an expensive call instead \u2014 use_state(json.loads(big_blob)) \u2014 reparses on every render, because Python evaluates the argument before use_state ever runs.

"},{"location":"learn/state-a-components-memory/#giving-a-component-multiple-state-variables","title":"Giving a component multiple state variables","text":"

You can have as many state variables of as many types as you like in one component. This component has two state variables, a number index and a boolean show_more that's toggled when you click \"Show details\":

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index, set_index = use_state(0)\n    show_more, set_show_more = use_state(False)\n\n    async def handle_next(event):\n        set_index(index + 1)\n\n    async def handle_more(event):\n        set_show_more(not show_more)\n\n    sculpture = sculptures[index]\n    return html.div(\n        html.button({\"onClick\": handle_next}, \"Next\"),\n        html.h2(html.i(sculpture[\"name\"]), \" by \", sculpture[\"artist\"]),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.button(\n            {\"onClick\": handle_more}, \"Hide details\" if show_more else \"Show details\"\n        ),\n        html.p(sculpture[\"description\"]) if show_more else None,\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n    )\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n

It is a good idea to have multiple state variables if their state is unrelated, like index and show_more in this example. But if you find that you often change two state variables together, it might be easier to combine them into one. For example, if you have a form with many fields, it's more convenient to have a single state variable that holds a dict than a state variable per field. Read Choosing the State Structure for more tips.

"},{"location":"learn/state-a-components-memory/#state-is-isolated-and-private","title":"State is isolated and private","text":"

State is local to a particular invocation of a component \u2014 like a local variable. Render the same component twice and each copy gets its own state.

app.pydata.pystyles.css Run
@component\ndef gallery():\n    index, set_index = use_state(0)\n    sculpture = sculptures[index]\n\n    async def handle_index_click(event):\n        set_index(index + 1)\n\n    return html.section(\n        html.button(\n            {\"onClick\": handle_index_click},\n            \"Next\",\n        ),\n        html.h3(f\"({index + 1} of {len(sculptures)})\"),\n        html.img({\"src\": sculpture[\"url\"], \"alt\": sculpture[\"alt\"]}),\n    )\n\n\n@component\ndef page():\n    # Two separate mounts of the same component. Each call site is a different\n    # position in the tree, so each gallery keeps its own index.\n    return html.div(gallery(), gallery())\n
sculptures = [\n    {\n        \"name\": \"Homenaje a la Neurocirug\u00eda\",\n        \"artist\": \"Marta Colvin Andrade\",\n        \"description\": \"Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.\",\n        \"url\": \"https://i.imgur.com/Mx7dA2Y.jpg\",\n        \"alt\": \"A bronze statue of two crossed hands delicately holding a human brain in their fingertips.\",\n    },\n    {\n        \"name\": \"Floralis Gen\u00e9rica\",\n        \"artist\": \"Eduardo Catalano\",\n        \"description\": \"This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.\",\n        \"url\": \"https://i.imgur.com/ZF6s192m.jpg\",\n        \"alt\": \"A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.\",\n    },\n    {\n        \"name\": \"Eternal Presence\",\n        \"artist\": \"John Woodrow Wilson\",\n        \"description\": 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n        \"url\": \"https://i.imgur.com/aTtVpES.jpg\",\n        \"alt\": \"The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.\",\n    },\n    {\n        \"name\": \"Moai\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.\",\n        \"url\": \"https://i.imgur.com/RCwLEoQm.jpg\",\n        \"alt\": \"Three monumental stone busts with the heads that are disproportionately large with somber faces.\",\n    },\n    {\n        \"name\": \"Blue Nana\",\n        \"artist\": \"Niki de Saint Phalle\",\n        \"description\": \"The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.\",\n        \"url\": \"https://i.imgur.com/Sd1AgUOm.jpg\",\n        \"alt\": \"A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.\",\n    },\n    {\n        \"name\": \"Ultimate Form\",\n        \"artist\": \"Barbara Hepworth\",\n        \"description\": \"This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.\",\n        \"url\": \"https://i.imgur.com/2heNQDcm.jpg\",\n        \"alt\": \"A tall sculpture made of three elements stacked on each other reminding of a human figure.\",\n    },\n    {\n        \"name\": \"Cavaliere\",\n        \"artist\": \"Lamidi Olonade Fakeye\",\n        \"description\": \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n        \"url\": \"https://i.imgur.com/wIdGuZwm.png\",\n        \"alt\": \"An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.\",\n    },\n    {\n        \"name\": \"Big Bellies\",\n        \"artist\": \"Alina Szapocznikow\",\n        \"description\": \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n        \"url\": \"https://i.imgur.com/AlHTAdDm.jpg\",\n        \"alt\": \"The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.\",\n    },\n    {\n        \"name\": \"Terracotta Army\",\n        \"artist\": \"Unknown Artist\",\n        \"description\": \"The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.\",\n        \"url\": \"https://i.imgur.com/HMFmH6m.jpg\",\n        \"alt\": \"12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.\",\n    },\n    {\n        \"name\": \"Lunar Landscape\",\n        \"artist\": \"Louise Nevelson\",\n        \"description\": \"Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism's geometric abstraction of space and form.\",\n        \"url\": \"https://i.imgur.com/rN7hY6om.jpg\",\n        \"alt\": \"A black matte sculpture where the individual elements are initially indistinguishable.\",\n    },\n    {\n        \"name\": \"Aureole\",\n        \"artist\": \"Ranjani Shettar\",\n        \"description\": 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n        \"url\": \"https://i.imgur.com/okTpbHhm.jpg\",\n        \"alt\": \"A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.\",\n    },\n    {\n        \"name\": \"Hippos\",\n        \"artist\": \"Taipei Zoo\",\n        \"description\": \"The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.\",\n        \"url\": \"https://i.imgur.com/6o5Vuyu.jpg\",\n        \"alt\": \"A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.\",\n    },\n]\n
h2 {\n    margin-top: 10px;\n    margin-bottom: 0;\n}\n\nh3 {\n    margin-top: 5px;\n    font-weight: normal;\n    font-size: 100%;\n}\n\nimg {\n    width: 120px;\n    height: 120px;\n}\n\nbutton {\n    display: block;\n    margin-top: 10px;\n    margin-bottom: 10px;\n}\n
# TODO\n

Click \"Next\" in one gallery and the other doesn't budge. Each call to gallery() is a separate component instance with its own state.

This is the same mechanism behind Passing Props to a Component: the person prop is local to one invocation of Profile(), so two Profile() calls don't share it. State works the same way.

Because state belongs to the instance and not the function, two galleries can drift apart \u2014 which is sometimes what you want, and sometimes not. Sharing State Between Components covers the case where they must move together.

How does ReactPy know which state to return?

You might have noticed that the use_state call does not receive any information about which state variable it refers to. There is no \"identifier\" passed to use_state, so how does it know which state to return? Does it rely on some magic like parsing your functions? The answer is no.

Instead, to enable their concise syntax, Hooks rely on a stable call order on every render of the same component. ReactPy appends each Hook's state to a list on the component's first render, then walks that list by position on every render after (LifeCycleHook.use_state in core/_life_cycle_hook.py). This works well in practice because if you follow the rule above (\"only call Hooks at the top level\"), Hooks are always called in the same order \u2014 and reactpy-flake8 catches most mistakes.

What happens when the order does change is worth knowing, because one of the two outcomes fails silently:

  • A Hook appears where there was none before \u2192 IndexError: list index out of range, the render aborts, and the error is logged.
  • A Hook stops being called \u2192 the Hooks after it shift up one slot and quietly receive the wrong values. Nothing is logged, and the component keeps rendering.

Measured with two use_state calls that vanish behind a branch, the surviving Hook got the first slot's value while its own was left orphaned. That is far harder to debug than a crash, which is exactly why the top-level rule is enforced rather than merely recommended.

"},{"location":"learn/state-a-components-memory/#recap","title":"Recap","text":"
  • Components need to \"remember\" things, and that memory is called state.
  • A state variable is declared with use_state, which returns the current value and a setter function.
  • Updating a local variable doesn't re-render, and re-renders throw local variables away. State is what survives.
  • Hooks must be called at the top level, in the same order every render; reactpy-flake8 raises REACTPY102 otherwise.
  • State is local to each invocation of a component, so two instances never share it.
"},{"location":"learn/state-as-a-snapshot/","title":"State as a Snapshot","text":""},{"location":"learn/state-as-a-snapshot/#overview","title":"Overview","text":"

State variables might look like regular Python variables that you can read and write to. However, state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render.

You will learn

  • How setting state triggers renders
  • Why variable and prop values are fixed when a render is started
  • Why event handlers see old values of state
"},{"location":"learn/state-as-a-snapshot/#setting-state-triggers-renders","title":"Setting state triggers renders","text":"

You might think of your user interface as changing directly in response to the user event like a click. In ReactPy, it works a little differently from this mental model. You have already seen that setting state requests a re-render. This means that for an interface to react to an event, you need to update the state.

In this example, when you press \"send\", set_is_sent(True) tells ReactPy to re-render the UI:

app.pystyles.css Run
@component\ndef form():\n    is_sent, set_is_sent = use_state(False)\n    message, set_message = use_state(\"Hi!\")\n\n    if is_sent:\n        return html.h1(\"Your message is on its way!\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        set_is_sent(True)\n        send_message(message)\n\n    async def handle_message_change(event):\n        set_message(await event.target.value)\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.textarea(\n            {\n                \"placeholder\": \"Message\",\n                \"value\": message,\n                \"onChange\": handle_message_change,\n            }\n        ),\n        html.button({\"type\": \"submit\"}, \"Send\"),\n    )\n\n\ndef send_message(message):\n    print(f\"Sending: {message}\")\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n\nlabel,\ntextarea {\n    display: block;\n    margin-bottom: 10px;\n}\n
# TODO\n

Here's what happens when you click the button:

  1. The onSubmit event handler executes.
  2. set_is_sent(True) sets is_sent to True and queues a new render.
  3. ReactPy re-renders the component according to the new is_sent value.

Let's take a closer look at the relationship between state and rendering.

"},{"location":"learn/state-as-a-snapshot/#rendering-takes-a-snapshot-in-time","title":"Rendering takes a snapshot in time","text":"

\"Rendering\" means that ReactPy is calling your component, which is a function. The VDOM you return from that function is like a snapshot of the UI in time. Its props, event handlers, and local variables were all calculated using its state at the time of the render.

Unlike a photograph or a movie frame, the UI \"snapshot\" you return is interactive. It includes logic like event handlers that specify what happens in response to inputs. ReactPy updates the screen to match this snapshot and connects the event handlers. As a result, pressing a button triggers the click handler from that snapshot.

When ReactPy re-renders a component:

  1. ReactPy calls your function again.
  2. Your function returns a new VDOM snapshot.
  3. ReactPy then updates the screen to match the snapshot you've returned.

As a component's memory, state is not like a regular variable that disappears after your function returns. State actually \"lives\" in ReactPy itself \u2014 as if on a shelf! \u2014 outside of your function. When ReactPy calls your component, it gives you a snapshot of the state for that particular render. Your component returns a snapshot of the UI with a fresh set of props and event handlers, all calculated using the state values from that render!

Here's a little experiment to show you how this works. Clicking the \"+3\" button calls set_number(number + 1) three times, so you might expect three increments.

app.pystyles.css Run
@component\ndef counter():\n    number, set_number = use_state(0)\n\n    async def handle_click(event):\n        set_number(number + 1)\n        set_number(number + 1)\n        set_number(number + 1)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+3\"),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n\nlabel,\ntextarea {\n    display: block;\n    margin-bottom: 10px;\n}\n
# TODO\n

Notice that number only increments once per click \u2014 verified by dispatching a click and reading the result.

Setting state only changes it for the next render. During the first render, number was 0. This is why, in that render's handle_click, the value of number is still 0 even after set_number(number + 1) was called:

Here is what this button's click handler tells ReactPy to do:

  1. set_number(number + 1): number is 0, so set_number(0 + 1).
    • ReactPy prepares to change number to 1 on the next render.
  2. set_number(number + 1): number is 0, so set_number(0 + 1).
    • ReactPy prepares to change number to 1 on the next render.
  3. set_number(number + 1): number is 0, so set_number(0 + 1).
    • ReactPy prepares to change number to 1 on the next render.

Even though you called set_number(number + 1) three times, in this render's event handler number is always 0, so you set the state to 1 three times. This is why, after your event handler finishes, ReactPy re-renders the component with number equal to 1 rather than 3.

You can also visualize this by mentally substituting state variables with their values in your code. Since number is 0 for this render, its event handler effectively reads:

async def handle_click(event):\n    set_number(0 + 1)\n    set_number(0 + 1)\n    set_number(0 + 1)\n

For the next render number is 1, so that render's click handler reads set_number(1 + 1) three times. This is why clicking again takes the counter to 2, then 3, and so on.

This is Python's own closure rule, not a ReactPy feature

There is no magic here. handle_click is a closure over the local name number created during one specific call to counter(). That name is bound to the value ReactPy handed to that call, and no later assignment elsewhere can change what it refers to. ReactPy's contribution is simply that each render is a fresh call, so each render gets a fresh handle_click closing over fresh values.

It is worth stating plainly, because the usual Python instinct is wrong here: set_number does not write to number. It writes into ReactPy's hook state, on the shelf outside your function.

"},{"location":"learn/state-as-a-snapshot/#state-over-time","title":"State over time","text":"

Try to guess what this button's second column will show.

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n    seen, set_seen = use_state([])\n\n    async def handle_click(event):\n        set_number(number + 5)\n        # `number` here is still the value from THIS render.\n        set_seen([*seen, number])\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+5\"),\n        html.p(\"Handler saw: \", \", \".join(map(str, seen)) or \"-\"),\n    )\n
# TODO\n

Using the substitution method, number is 0, so the first click records 0 \u2014 even though set_number(number + 5) ran one line earlier. Click again and it records 5, because in that render number is 5.

But what if you delay that read, so it only happens after the component re-renders? Would it say 0 or 5? Have a guess!

app.py Run
@component\ndef counter():\n    number, set_number = use_state(0)\n    seen, set_seen = use_state([])\n\n    async def handle_click(event):\n        set_number(number + 5)\n\n        def delayed():\n            # Runs long after this render, but still sees this render's value.\n            set_seen([*seen, number])\n\n        asyncio.get_running_loop().call_later(3, delayed)\n\n    return html.div(\n        html.h1(number),\n        html.button({\"onClick\": handle_click}, \"+5\"),\n        html.p(\"Handler saw (3s later): \", \", \".join(map(str, seen)) or \"-\"),\n    )\n
# TODO\n

Surprised? If you use the substitution method, you can see the \"snapshot\" of the state that was captured.

set_number(0 + 5)\nget_running_loop().call_later(3, delayed)   # delayed() closes over number == 0\n

The state stored in ReactPy may have changed by the time the callback runs, but the callback was scheduled using a snapshot of the state at the time the user interacted with it.

The updater form rescues only the value it updates

The scheduled callback above works because set_seen is stable and the closure captures a value, not a live binding. The trap is the reverse: a callback that reaches for the current value gets the one from whichever render created it. An updater looks like the fix, but it is narrower than it appears. ReactPy applies an updater to the current value of that one state variable the instant you call the setter (core/hooks.py, _CurrentState.dispatch calls new(self.value)), so set_seen(lambda previous: [*previous, number]) hands you the freshest seen \u2014 while number inside it is still the name frozen when the callback was built. Reach for the updater when the value you need is the value you are setting. For anything else, read it before scheduling and pass it in, or keep it in a use_ref box and read box.current when the callback finally runs.

A state variable's value never changes within a render, even if its event handler's code is asynchronous. Inside that render's handle_click, the value of number continues to be 0 even after set_number(number + 5) was called. Its value was \"fixed\" when ReactPy \"took the snapshot\" of the UI by calling your component.

Here is an example of how that makes your event handlers less prone to timing mistakes. Below is a form that sends a message with a five-second delay. Imagine this scenario:

  1. You press the \"Send\" button, sending \"Hello\" to Alice.
  2. Before the five-second delay ends, you change the value of the \"To\" field to \"Bob\".

What do you expect the status line to display? Would it display, \"You said Hello to Alice\"? Or would it display, \"You said Hello to Bob\"? Make a guess based on what you know, and then try it:

app.pystyles.css Run
@component\ndef form():\n    to, set_to = use_state(\"Alice\")\n    message, set_message = use_state(\"Hello\")\n    status, set_status = use_state(\"Not sent yet\")\n\n    async def handle_submit(event):\n        # This handler closes over `to` and `message` from ITS OWN render.\n        set_status(\"Sending...\")\n        await asyncio.sleep(5)\n        set_status(f\"You said {message} to {to}\")\n\n    async def handle_to_change(event):\n        set_to(await event.target.value)\n\n    async def handle_message_change(event):\n        set_message(await event.target.value)\n\n    return html.div(\n        html.label(\n            \"To: \",\n            html.input(\n                {\n                    \"value\": to,\n                    \"onChange\": handle_to_change,\n                }\n            ),\n        ),\n        html.label(\n            \"Message: \",\n            html.input(\n                {\n                    \"value\": message,\n                    \"onChange\": handle_message_change,\n                }\n            ),\n        ),\n        html.button({\"onClick\": handle_submit}, \"Send\"),\n        html.p(status),\n    )\n
button {\n    display: inline-block;\n    margin: 10px;\n    font-size: 20px;\n}\n\nh1 {\n    display: inline-block;\n    margin: 10px;\n    width: 30px;\n    text-align: center;\n}\n\nlabel,\ntextarea {\n    display: block;\n    margin-bottom: 10px;\n}\n
# TODO\n

If you press Send and then change \"To\" before the five seconds elapse, you still get \"You said Hello to Alice\". Verified: pressing Send and changing the recipient to \"Bob\" mid-flight produced exactly that string.

That is the payoff of snapshots. handle_submit was created by the render in which to was \"Alice\", so it keeps saying \"Alice\" no matter what later renders decide. Had the handler read the state through some live reference instead, changing the field mid-request would have sent the message to Alice while reporting that it went to Bob.

React's own version of this demo uses setTimeout and a browser alert. ReactPy has no alert on the server, so the equivalent here is a status paragraph \u2014 and asyncio.sleep rather than a timer callback. If you want the browser to alert, that is the inlineJavaScript escape hatch, not a Python function call.

"},{"location":"learn/state-as-a-snapshot/#recap","title":"Recap","text":"
  • Setting state does not change the state variable you already have; it requests a render.
  • Every render captures its own snapshot of state, props, and handlers. Values are fixed for the duration of that render.
  • This follows Python's ordinary closure rules: each render is a fresh call, so each handler closes over fresh values.
  • Reading state inside a delayed callback or an await gives you the value from the render that created that callback, which is usually what you want.
  • When you genuinely need the latest value inside a callback, use the updater form set_x(lambda previous: ...).
"},{"location":"learn/synchronizing-with-effects/","title":"Synchronizing with Effects","text":""},{"location":"learn/synchronizing-with-effects/#overview","title":"Overview","text":"

Some components need to synchronize with an external system: send an analytics log when a page appears, keep a third-party object in step with state, or hold a connection open while a view is on screen. Effects let you run code after a render so a component can talk to something outside of ReactPy.

You will learn

  • What an Effect is, and how it differs from an event handler
  • How to declare one, and when it re-runs
  • Why ReactPy infers dependencies instead of requiring an array
  • How to write a clean-up function, and what happens if you skip it
  • How to read the newest state from a long-lived Effect

Note

Capitalized \"Effect\" means the ReactPy-specific idea above: a side effect caused by rendering. \"side effect\" keeps its ordinary programming meaning.

"},{"location":"learn/synchronizing-with-effects/#what-are-effects-and-how-are-they-different-from-events","title":"What are Effects and how are they different from events?","text":"

Every ReactPy component contains two kinds of logic:

  • Rendering code (introduced in Your First Component) sits at the top level of the component body. It takes props and state, does the math, and returns VDOM. Rendering code must be pure. It may only calculate; it may not do.

  • Event handlers (introduced in Responding to Events) are functions defined inside the component that ReactPy runs only when the user does something. They contain side effects \u2014 posting a form, updating a field \u2014 triggered by a specific interaction.

That covers most needs. But some work belongs to neither bucket. A chat view must connect to the server whenever it appears on screen, no matter which click or keystroke caused it to appear. There is no event for \"this component was rendered\", and putting the connection in the render body would break purity.

An Effect declares side effects caused by rendering itself, rather than by a particular event. It runs after the render is finished, in the connection's own task.

"},{"location":"learn/synchronizing-with-effects/#you-might-not-need-an-effect","title":"You might not need an Effect","text":"

Do not reach for an Effect first. Effects exist to step outside ReactPy and synchronize with something that is not ReactPy: a network connection, a queue, a third-party library, the filesystem. If your logic only adjusts one piece of state in response to another, or computes a value from state you already have, it belongs in the render \u2014 see You Might Not Need an Effect.

"},{"location":"learn/synchronizing-with-effects/#how-to-write-an-effect","title":"How to write an Effect","text":"

Three steps:

  1. Declare it. ReactPy registers the Effect for the render that created it.
  2. Say when it re-runs. By default ReactPy infers this from your closure, which is not what React does.
  3. Add a clean-up if it needs one. Everything that opens, subscribes, or schedules needs the matching close, unsubscribe, or cancel.
"},{"location":"learn/synchronizing-with-effects/#step-1-declare-an-effect","title":"Step 1: Declare an Effect","text":"

Import the hook and call it at the top level of your component:

from reactpy import use_effect\n

Pass it a plain function that takes no arguments:

@component\ndef my_component():\n    def effect():\n        print(\"on screen\")\n\n    use_effect(effect, dependencies=[])\n    ...\n

Or use it as a decorator, which is equivalent:

@use_effect\ndef effect():\n    print(\"on screen\")\n

Here is a working component. Open the server console and press Re-check: the log line appears once, on mount, and never again \u2014 the Effect does not depend on visit_count:

app.py Run
@component\ndef status_page():\n    visit_count, set_visit_count = use_state(0)\n\n    def effect():\n        # Runs after this component is on screen. ReactPy runs it in the\n        # connection's own task, after the render that scheduled it.\n        print(\"status_page is on screen\")\n\n    use_effect(effect, dependencies=[])\n\n    async def handle_visit_count_click(event):\n        set_visit_count(visit_count + 1)\n\n    return html.div(\n        html.h1(\"Service status\"),\n        html.p(f\"This page has been opened {visit_count} times.\"),\n        html.button(\n            {\"onClick\": handle_visit_count_click},\n            \"Re-check\",\n        ),\n    )\n
# TODO\n

Two things about timing are worth stating precisely, because they differ from what a React tutorial will tell you:

  • An Effect is scheduled after your component's render is complete, not after the browser has painted. The task is created at the end of the render (core/_life_cycle_hook.py, affect_layout_did_render), and the update message has not necessarily reached the socket yet. Measured directly, an Effect's first line ran before layout.render() returned to the backend. So never assume the user can already see the result.
  • The Effect does not delay the render. layout.render() does not await your effect body; the two interleave on the event loop. If you need ordering, express it in state: have the Effect set state and let the next render depend on it.

Effects run on the server

There is no document and no window. An Effect cannot measure an element, focus a field, or attach a scroll listener, because there is no DOM where it runs. For browser-side behaviour see Manipulating the DOM with Refs and Creating VDOM Event Handlers.

use_effect rejects async def

TypeError: `use_effect` does not support async functions. Use use_async_effect instead.\n

The failure is loud with REACTPY_DEBUG on: the model comes back as {\"tagName\": \"\", \"error\": \"TypeError: ...\"} and the region renders empty (with debug off you get the empty region and a traceback in the server log only). Anything await-based belongs in use_async_effect, whose effect takes zero arguments \u2014 the 1.x async def effect(stop) shape fails silently there instead.

"},{"location":"learn/synchronizing-with-effects/#step-2-specify-the-effect-dependencies","title":"Step 2: Specify the Effect dependencies","text":"

Pass the dependencies argument to control when the Effect re-runs \u2014 and note that ReactPy gives this argument four meanings where React has two:

Value When the Effect runs (verified) dependencies=[a, b] On mount, then whenever a or b changes. dependencies=[] Once, on mount. dependencies=None After every render. omitted Inferred from the free variables of your closure.

That last row is the one that bites people porting code from React. In React, omitting the array means \"run after every render\". In ReactPy it means \"re-run when the names my closure reads change\" \u2014 core/hooks.py, _try_to_infer_closure_values reads func.__closure__ and uses those cells as the dependency list. Here the Effect reads count and nothing else, so Rename re-renders the component without re-running the Effect:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    label, set_label = use_state(\"clicks\")\n    runs, set_runs = use_state([])\n\n    def effect():\n        # This closure reads `count` and nothing else, so `count` is the\n        # Effect's only dependency. It never reads `label`, so renaming\n        # re-renders the component WITHOUT re-running this Effect.\n        set_runs(lambda previous: [*previous, f\"effect ran with count={count}\"])\n\n    use_effect(effect)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_label_click(event):\n        set_label(label + \"!\")\n\n    return html.div(\n        html.h1(f\"{label}: {count}\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button({\"onClick\": handle_label_click}, \"Rename\"),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(runs)]),\n    )\n
# TODO\n

Press Rename twice and no new log line appears; press Add 1 and one appears per increment. Verified.

Dependencies are not compared by identity

The docstrings for these hooks say dependencies are compared by id. They are not. core/hooks.py, strictly_equal gates on the type first, then uses ==, and for two locally defined functions it compares their bytecode. In practice: a freshly built [1, 2] does not re-trigger an Effect that listed the previous equal list, and neither does a structurally identical local def \u2014 which means an Effect listing a local function as its dependency keeps the closure it captured first and reads stale state permanently. Both were measured. List the values, not wrappers around them.

Write the list out

Inference is invisible, so an Effect that happens to read one extra local variable quietly gains a dependency, and one that reads nothing re-runs never. If the Effect runs more or less often than you meant, look at which names the body actually reads. An explicit list removes the guesswork, and dependencies=[] and dependencies=None are honest too.

Declare state before the Effects that read it

use_effect(effect)  # effect reads `count`\ncount, set_count = use_state(0)\n

ReactPy reads the closure's cells while resolving dependencies, and count's cell is not filled yet: ValueError: Cell is empty. The component renders as a blank region, with the error in the model (debug on) or in the server log (debug off). State first, Effects after.

"},{"location":"learn/synchronizing-with-effects/#step-3-add-clean-up-if-needed","title":"Step 3: Add clean-up if needed","text":"

Return a function from the Effect. ReactPy runs it before the next run of that same Effect and again when the component unmounts:

app.py Run
@component\ndef chat_room(room):\n    status, set_status = use_state(\"offline\")\n\n    def effect():\n        # ReactPy runs the returned function before the *next* run of this\n        # same Effect, and again when the component unmounts. A connection\n        # without a clean-up is a permanent leak in a long-lived server.\n        close = chat_server.connect(room)\n        set_status(f\"connected to {room}\")\n\n        def cleanup():\n            close()\n            set_status(\"offline\")\n\n        return cleanup\n\n    use_effect(effect, dependencies=[room])\n\n    return html.p(status)\n\n\n@component\ndef app():\n    room, set_room = use_state(\"lobby\")\n    connected, set_connected = use_state(True)\n\n    async def handle_room_click(event):\n        set_room(\"dev\" if room == \"lobby\" else \"lobby\")\n\n    async def handle_connected_click(event):\n        set_connected(not connected)\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_room_click},\n            f\"Switch room (now {room})\",\n        ),\n        html.button(\n            {\"onClick\": handle_connected_click},\n            \"Leave\" if connected else \"Join\",\n        ),\n        *([] if not connected else [chat_room(room)]),\n    )\n
# TODO\n

Switch rooms and the server log reads connect lobby, close lobby, connect dev \u2014 the teardown for the old room runs before the new one starts. Press Leave and the last connection closes. Verified.

This matters more in ReactPy than in React because of process lifetime. A closed browser tab destroys everything with it. A ReactPy server does not: a subscription you forgot to release is still live after the user went home, still holding your component, still firing callbacks into a view nobody can see. The counter-proof, measured in a Layout:

Effect body Subscribers after 4 renders One publish fires After unmount returns nothing 4 4 callbacks 4 still attached returns unsubscribe 1 1 callback 0

Same component, same re-renders. Without the clean-up, one message ran the handler four times.

Clean-up is what makes restarts free

Whenever a dependency changes, ReactPy stops the old Effect and starts a new one. The rule of thumb is that nobody should be able to tell whether the Effect ran once or ran setup \u2192 cleanup \u2192 setup. Write it that way and restarts cost nothing; skip the clean-up and every dependency change leaks.

"},{"location":"learn/synchronizing-with-effects/#reading-the-newest-value-from-a-long-lived-effect","title":"Reading the newest value from a long-lived Effect","text":"

Here is the trap that bites hardest with use_async_effect. An Effect's body closes over the render that created it. If that Effect then lives a long time \u2014 a polling loop, a subscription \u2014 it acts on values that are already stale.

The fix is to keep the value in a ref, assigning it during render. The ref object is the same one on every render, so a reader that wakes up later sees the newest write:

app.py Run
@component\ndef connection_monitor():\n    threshold, set_threshold = use_state(HIGH_THRESHOLD)\n    alerts, set_alerts = use_state([])\n    # Write the newest value during render so a reader that wakes up later\n    # gets this render's number rather than the one captured at mount.\n    latest_threshold = use_ref(threshold)\n    latest_threshold.current = threshold\n\n    async def watch():\n        while True:\n            await asyncio.sleep(0.2)\n            # SLOW_LATENCY stands in for a real measurement.\n            if SLOW_LATENCY > latest_threshold.current:\n                set_alerts(append_alert)\n\n    # `dependencies=[]` keeps ONE task alive for the component's lifetime.\n    # Reading `threshold` directly here would freeze it at its first value.\n    use_async_effect(watch, dependencies=[])\n\n    async def handle_threshold_click(event):\n        set_threshold(LOW_THRESHOLD if threshold == HIGH_THRESHOLD else HIGH_THRESHOLD)\n\n    return html.div(\n        html.p(f\"threshold: {threshold}\"),\n        html.button(\n            {\"onClick\": handle_threshold_click},\n            \"Toggle threshold\",\n        ),\n        html.p(f\"alerts: {len(alerts)}\"),\n    )\n
# TODO\n

Toggle the threshold from 50 to 10 and alerts start appearing; toggle back and they stop. The single long-lived task never restarts and always reads the current number. Verified.

The alternative \u2014 usually the better one \u2014 is to put the value in the dependency list so a change tears the Effect down and builds a new one with fresh values. Use the ref when restarting is expensive: an open socket, a loop with accumulated state.

"},{"location":"learn/synchronizing-with-effects/#common-patterns","title":"Common patterns","text":""},{"location":"learn/synchronizing-with-effects/#synchronizing-with-a-non-reactpy-object","title":"Synchronizing with a non-ReactPy object","text":"

Third-party objects are imperative: you construct them, call setters, destroy them. State is the wrong home for one (constructing it would re-run each render, and setting it would trigger a render), so keep it in a ref and let Effects drive its lifecycle:

app.py Run
@component\ndef report(name):\n    page_size, set_page_size = use_state(\"letter\")\n    # The engine lives in a ref: it has to survive renders, and changing it\n    # must not itself trigger a render.\n    engine_box: Ref[ReportEngine | None] = use_ref(None)\n\n    def create_engine():\n        engine = ReportEngine(name)\n        engine_box.current = engine\n\n        def destroy():\n            engine.destroy()\n            engine_box.current = None\n\n        return destroy\n\n    use_effect(create_engine, dependencies=[name])\n\n    def apply_page_size():\n        if engine_box.current is not None:\n            engine_box.current.set_page_size(page_size)\n\n    use_effect(apply_page_size, dependencies=[page_size])\n\n    async def handle_page_size_click(event):\n        set_page_size(\"a4\" if page_size == \"letter\" else \"letter\")\n\n    return html.div(\n        html.h2(name),\n        html.p(f\"page size: {page_size}\"),\n        html.button(\n            {\"onClick\": handle_page_size_click},\n            \"Toggle page size\",\n        ),\n    )\n
# TODO\n

Two Effects, two dependency lists: one creates and destroys the object when name changes, the other pushes page_size into it. Verified against the object's own log: created q3, set_page_size letter, set_page_size a4, destroyed q3 on unmount \u2014 one instance, kept in step, cleaned up.

Two Effects in the same component run in declaration order, which is why create_engine is registered first: the second Effect can assume the object exists. That ordering holds within a component; across components it does not. Children's Effects run before their parent's, and siblings run in reverse order \u2014 measured ['child effect b', 'child effect a', 'parent effect']. Never write code that needs a parent to have run first.

"},{"location":"learn/synchronizing-with-effects/#subscribing-to-a-stream","title":"Subscribing to a stream","text":"

The shape is always the same: subscribe inside the Effect, unsubscribe in the clean-up, name the topic in the dependency list:

app.py Run
@component\ndef scroll_log(topic):\n    lines, set_lines = use_state([])\n\n    def effect():\n        # The clean-up is what keeps the subscriber count at 1 across\n        # re-subscriptions. Forget it and every render adds another callback.\n        def on_message(message):\n            set_lines(lambda previous: [*previous, message])\n\n        unsubscribe = ticker.subscribe(topic, on_message)\n        return unsubscribe\n\n    use_effect(effect, dependencies=[topic])\n\n    return html.div(\n        html.h2(topic),\n        html.ul([html.li({\"key\": index}, line) for index, line in enumerate(lines)]),\n    )\n\n\n@component\ndef app():\n    topic, set_topic = use_state(\"news\")\n    watching, set_watching = use_state(True)\n\n    async def handle_topic_click(event):\n        set_topic(\"sports\" if topic == \"news\" else \"news\")\n\n    async def handle_watching_click(event):\n        set_watching(not watching)\n\n    async def handle_click(event):\n        ticker.publish(topic, \"ping\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_topic_click},\n            \"Switch topic\",\n        ),\n        html.button(\n            {\"onClick\": handle_watching_click},\n            \"Stop watching\" if watching else \"Watch\",\n        ),\n        html.button({\"onClick\": handle_click}, \"Ping\"),\n        *([] if not watching else [scroll_log(topic)]),\n    )\n
# TODO\n

Verified through the bus's own log: subscribe news, unsubscribe news, subscribe sports, unsubscribe sports, subscribe news, unsubscribe news across a full switch-and-leave cycle \u2014 exactly one subscriber live at any moment.

Note that the callback is defined inside the Effect, so it closes over this Effect's values and the subscription is rebuilt when the dependencies change. Defining it in the component body instead would leave the subscription holding the first version of it.

"},{"location":"learn/synchronizing-with-effects/#fetching-data","title":"Fetching data","text":"

Fetching belongs in use_async_effect, and cancellation is what makes it safe:

app.py Run
@component\ndef report_view(name):\n    rows, set_rows = use_state(\"nothing loaded yet\")\n\n    async def fetch():\n        set_rows(f\"loading {name} ...\")\n        result = await catalog.fetch(name)\n        # This line is reached only if nobody cancelled the task. When `name`\n        # changes, ReactPy cancels the running task before this line runs, so\n        # an answer for the OLD report can never overwrite the new one.\n        set_rows(result)\n\n    use_async_effect(fetch, dependencies=[name])\n\n    return html.div(html.h2(name), html.p(rows))\n\n\n@component\ndef app():\n    name, set_name = use_state(\"orders\")\n\n    async def handle_name_click(event):\n        set_name(\"users\" if name == \"orders\" else \"orders\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_name_click},\n            \"Toggle report\",\n        ),\n        report_view(name),\n    )\n
# TODO\n

The example is deliberately lopsided: orders takes 0.8 s and users takes 0.05 s. Press Toggle report while the slow one is in flight and the screen settles on users: 500 rows and stays there. The data source recorded that it computed only the users answer \u2014 the orders task was cancelled inside its await and never reached its set_rows. Verified.

That is ReactPy's version of React's ignore flag, and it is stronger: the cancelled run's remaining lines never execute at all. Two consequences:

  • Code after an await may never run. Anything that must happen \u2014 closing a handle, releasing a lock \u2014 goes in a try / except asyncio.CancelledError pair, and re-raises so asyncio can finish cancelling.
  • An Effect that sets state it depends on loops forever. If a component appears to spin, read what the Effect awaits, what it depends on, and what it sets.

Good alternatives to fetching in an Effect

Manual fetching in an Effect works, and carries the same drawbacks here as in React: no deduplication, no cache shared between components, and a request that cannot start until the component exists. ReactPy has a better option available by default \u2014 fetch during a parent's render, or at startup, and pass the result down as a prop. The child then renders with data instead of loading it. Keep the Effect for data that genuinely depends on something only the connection knows, such as use_location.

"},{"location":"learn/synchronizing-with-effects/#sending-analytics","title":"Sending analytics","text":"

Report a page view when the page changes, not when the component happens to render:

app.py Run
@component\ndef page_view(path):\n    draft, set_draft = use_state(\"\")\n\n    def effect():\n        # Depends on `path` only: editing the field re-renders the component\n        # but does not report another visit.\n        analytics.log_visit(path)\n\n    use_effect(effect, dependencies=[path])\n\n    async def handle_draft_change(event):\n        set_draft(await event.target.value)\n\n    return html.div(\n        html.h2(path),\n        html.p(f\"total reports sent: {len(analytics.sent)}\"),\n        html.input(\n            {\n                \"placeholder\": \"notes\",\n                \"onChange\": handle_draft_change,\n            }\n        ),\n    )\n\n\n@component\ndef app():\n    path, set_path = use_state(\"/home\")\n\n    async def handle_path_click(event):\n        set_path(\"/about\" if path == \"/home\" else \"/home\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_path_click},\n            \"Navigate\",\n        ),\n        page_view(path),\n    )\n
# TODO\n

The Effect depends on path only. Type into the field and the report count stays put; press Navigate and it climbs by one. Verified: two typing re-renders produced zero reports, two navigations produced two.

"},{"location":"learn/synchronizing-with-effects/#not-an-effect-initializing-the-application","title":"Not an Effect: initializing the application","text":"

Logic that should run once for the whole process does not belong in a component at all \u2014 a component's Effect runs once per connection, and a server serves many:

# module level: runs when the module is imported, once\nconfigure_logging()\nPOOL = create_pool()\n

Put it in the ASGI app's startup, or in the module that builds the ReactPy executor. Reach for a dependencies=[] Effect only when the work is genuinely per-connection. Never guard it with my_component.started = True: the function object is module-scope, so the first visitor flips the flag for everyone (VDOM Mutations).

"},{"location":"learn/synchronizing-with-effects/#not-an-effect-buying-a-product","title":"Not an Effect: buying a product","text":"

An Effect is not the place for something that must happen exactly once per user action, because you cannot promise how many times an Effect runs \u2014 dependencies change, components unmount, processes restart. Charging a card belongs in an event handler, where a click is the only trigger:

async def handle_buy(event):\n    set_status(\"buying...\")\n    order = place_order(sku)\n    set_status(f\"ordered {order.id}\")\n
"},{"location":"learn/synchronizing-with-effects/#deep-dive-each-render-has-its-own-effects","title":"Deep dive: each render has its own Effects","text":"

An Effect belongs to the render that created it. Its closure holds that render's props and state, and it never sees a later one. The dependency list chooses which render's Effect survives; it does not update the one that did:

app.py Run
@component\ndef audit_log(room, message):\n    entries, set_entries = use_state([])\n\n    def effect():\n        # This closure sees the `message` from the render that CREATED this\n        # Effect -- not the newest one. The dependency list says when to make\n        # a new Effect; the values it reads are frozen from that render.\n        set_entries(lambda previous: [*previous, f\"{room} <- {message}\"])\n\n    use_effect(effect, dependencies=[room])\n\n    return html.ul(\n        [html.li({\"key\": index}, line) for index, line in enumerate(entries)]\n    )\n\n\n@component\ndef app():\n    room, set_room = use_state(\"lobby\")\n    message, set_message = use_state(\"hello\")\n\n    async def handle_message_click(event):\n        set_message(message + \"!\")\n\n    async def handle_room_click(event):\n        set_room(\"dev\" if room == \"lobby\" else \"lobby\")\n\n    return html.div(\n        html.p(f\"room={room} message={message!r}\"),\n        html.button({\"onClick\": handle_message_click}, \"Type !\"),\n        html.button(\n            {\"onClick\": handle_room_click},\n            \"Switch room\",\n        ),\n        audit_log(room, message),\n    )\n
# TODO\n

Press Type ! twice, then Switch room. The log gains lobby <- hello from the mount, and then dev <- hello!! \u2014 the Effect that ran for dev was created by the render whose message was already 'hello!!'. Verified. No entry ever records an intermediate message, because only the renders whose dependencies changed created an Effect.

This is why \"my Effect reads an old value\" has exactly two correct answers: add the value to the dependency list, or read it through a ref.

"},{"location":"learn/synchronizing-with-effects/#there-is-no-double-invoke-in-development","title":"There is no double-invoke in development","text":"

React remounts every component twice under Strict Mode to expose Effects that are not restart-safe. ReactPy has no Strict Mode and never calls your component twice \u2014 verified: a component that increments a counter in its own body counted exactly one render per render.

Two consequences:

  • Nothing will find your unclean Effects for you. The restart-safety check React gives away for free is one you have to run yourself: render the component, unmount it, mount it again, and assert the external system is back where it started. A reactpy.core.layout.Layout in a pytest makes that a ten-line test.
  • Do not add artificial guards to compensate. if already_ran.current: return inside an Effect is evidence the dependency list is wrong, not a fix. Write the clean-up so a restart is free and the question stops mattering.
"},{"location":"learn/synchronizing-with-effects/#recap","title":"Recap","text":"
  • An Effect is a side effect caused by rendering; an event handler is one caused by a user action. Prefer the handler.
  • Effects are scheduled after the render completes, in the connection's own task. They do not block the render, and the browser may not have the update yet.
  • They run on the server: no DOM, no window, no element to focus.
  • use_effect takes a plain def; use_async_effect takes a zero-argument async def. One mismatch fails loudly, the other (the old async def effect(stop) shape) fails silently.
  • dependencies has four settings. Omitted means inferred from the closure, not \"every render\" \u2014 and the comparison is ==, not identity.
  • Return a clean-up function. It runs before the Effect's next run and on unmount. Skipping it leaks, and a leak in a long-lived server process leaks forever.
  • A long-lived Effect sees a frozen snapshot; read current values from a ref, or restart the Effect by depending on them.
"},{"location":"learn/synchronizing-with-effects/#related","title":"Related","text":"
  • Use Effect and Use Async Effect \u2014 full parameter reference
  • You Might Not Need an Effect \u2014 the cases that belong back in the render
  • Separating Events from Effects \u2014 reading the newest state without restarting an Effect
  • Removing Effect Dependencies \u2014 when to omit a dependency, safely
  • Referencing Values with Refs \u2014 the box that survives renders
"},{"location":"learn/thinking-in-react/","title":"Thinking in React","text":""},{"location":"learn/thinking-in-react/#overview","title":"Overview","text":"

React can change how you think about the designs you look at and the apps you build. When you build a user interface with React, you will first break it apart into pieces called components. Then, you will describe the different visual states for each of your components. Finally, you will connect your components together so that the data flows through them. In this tutorial, we\u2019ll guide you through the thought process of building a searchable product data table with React.

"},{"location":"learn/thinking-in-react/#start-with-the-mockup","title":"Start with the mockup","text":"

Imagine that you already have a JSON API and a mockup from a designer.

The JSON API returns some data that looks like this:

[\n    {\n        \"category\": \"Fruits\",\n        \"price\": \"$1\",\n        \"stocked\": true,\n        \"name\": \"Apple\"\n    },\n    {\n        \"category\": \"Fruits\",\n        \"price\": \"$1\",\n        \"stocked\": true,\n        \"name\": \"Dragonfruit\"\n    },\n    {\n        \"category\": \"Fruits\",\n        \"price\": \"$2\",\n        \"stocked\": false,\n        \"name\": \"Passionfruit\"\n    },\n    {\n        \"category\": \"Vegetables\",\n        \"price\": \"$2\",\n        \"stocked\": true,\n        \"name\": \"Spinach\"\n    },\n    {\n        \"category\": \"Vegetables\",\n        \"price\": \"$4\",\n        \"stocked\": false,\n        \"name\": \"Pumpkin\"\n    },\n    {\n        \"category\": \"Vegetables\",\n        \"price\": \"$1\",\n        \"stocked\": true,\n        \"name\": \"Peas\"\n    }\n]\n

The mockup looks like this:

To implement a UI in React, you will usually follow the same five steps.

"},{"location":"learn/thinking-in-react/#step-1-break-the-ui-into-a-component-hierarchy","title":"Step 1: Break the UI into a component hierarchy","text":"

Start by drawing boxes around every component and subcomponent in the mockup and naming them. If you work with a designer, they may have already named these components in their design tool. Ask them!

Depending on your background, you can think about splitting up a design into components in different ways:

  • Programming--use the same techniques for deciding if you should create a new function or object. One such technique is the single responsibility principle, that is, a component should ideally only do one thing. If it ends up growing, it should be decomposed into smaller subcomponents.
  • CSS--consider what you would make class selectors for. (However, components are a bit less granular.)
  • Design--consider how you would organize the design's layers.

If your JSON is well-structured, you'll often find that it naturally maps to the component structure of your UI. That's because UI and data models often have the same information architecture--that is, the same shape. Separate your UI into components, where each component matches one piece of your data model.

There are five components on this screen:

  1. filterable_product_table (grey) contains the entire app.
  2. search_bar (blue) receives the user input.
  3. product_table (lavender) displays and filters the list according to the user input.
  4. product_category_row (green) displays a heading for each category.
  5. product_row (yellow) displays a row for each product.

If you look at product_table (lavender), you'll see that the table header (containing the \"Name\" and \"Price\" labels) isn't its own component. This is a matter of preference, and you could go either way. For this example, it is a part of product_table because it appears inside the product_table's list. However, if this header grows to be complex (e.g., if you add sorting), you can move it into its own product_table_header component.

Now that you've identified the components in the mockup, arrange them into a hierarchy. Components that appear within another component in the mockup should appear as a child in the hierarchy:

  • filterable_product_table
    • search_bar
    • product_table
      • product_category_row
      • product_row
"},{"location":"learn/thinking-in-react/#step-2-build-a-static-version-in-react","title":"Step 2: Build a static version in React","text":"

Now that you have your component hierarchy, it's time to implement your app. The most straightforward approach is to build a version that renders the UI from your data model without adding any interactivity... yet! It's often easier to build the static version first and add interactivity later. Building a static version requires a lot of typing and no thinking, but adding interactivity requires a lot of thinking and not a lot of typing.

To build a static version of your app that renders your data model, you'll want to build components that reuse other components and pass data using props. Props are a way of passing data from parent to child. (If you're familiar with the concept of state, don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.)

You can either build \"top down\" by starting with building the components higher up in the hierarchy (like filterable_product_table) or \"bottom up\" by working from components lower down (like product_row). In simpler examples, it\u2019s usually easier to go top-down, and on larger projects, it\u2019s easier to go bottom-up.

app.pystyles.css Run
from reactpy import component, html\n\n\n@component\ndef product_category_row(category):\n    return html.tr(html.th({\"colSpan\": \"2\"}, category))\n\n\n@component\ndef product_row(product):\n    if product[\"stocked\"]:\n        name = product[\"name\"]\n    else:\n        name = html.span({\"style\": {\"color\": \"red\"}}, product[\"name\"])\n    return html.tr(html.td(name), html.td(product[\"price\"]))\n\n\n@component\ndef product_table(products):\n    rows = []\n    last_category = None\n    for product in products:\n        if product[\"category\"] != last_category:\n            rows.append(\n                product_category_row(product[\"category\"], key=product[\"category\"])\n            )\n        rows.append(product_row(product, key=product[\"name\"]))\n        last_category = product[\"category\"]\n\n    return html.table(\n        html.thead(html.tr(html.th(\"Name\"), html.th(\"Price\"))), html.tbody(rows)\n    )\n\n\n@component\ndef search_bar():\n    return html.form(\n        html.input({\"type\": \"text\", \"placeholder\": \"Search...\"}),\n        html.label(html.input({\"type\": \"checkbox\"}), \"Only show products in stock\"),\n    )\n\n\n@component\ndef filterable_product_table(products):\n    return html.div(search_bar(), product_table(products))\n\n\nPRODUCTS = [\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Apple\"},\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Dragonfruit\"},\n    {\"category\": \"Fruits\", \"price\": \"$2\", \"stocked\": False, \"name\": \"Passionfruit\"},\n    {\"category\": \"Vegetables\", \"price\": \"$2\", \"stocked\": True, \"name\": \"Spinach\"},\n    {\"category\": \"Vegetables\", \"price\": \"$4\", \"stocked\": False, \"name\": \"Pumpkin\"},\n    {\"category\": \"Vegetables\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Peas\"},\n]\n\n\n@component\ndef app():\n    return filterable_product_table(PRODUCTS)\n
body {\n    padding: 5px;\n}\nlabel {\n    display: block;\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\nth {\n    padding-top: 10px;\n}\ntd {\n    padding: 2px;\n    padding-right: 40px;\n}\n
# TODO\n

(If this code looks intimidating, go through the Quick Start first!)

After building your components, you'll have a library of reusable components that render your data model. Because this is a static app, the components will only return non-interactive HTML. The component at the top of the hierarchy (filterable_product_table) will take your data model as a prop. This is called one-way data flow because the data flows down from the top-level component to the ones at the bottom of the tree.

Pitfall

At this point, you should not be using any state values. That\u2019s for the next step!

"},{"location":"learn/thinking-in-react/#step-3-find-the-minimal-but-complete-representation-of-ui-state","title":"Step 3: Find the minimal but complete representation of UI state","text":"

To make the UI interactive, you need to let users change your underlying data model. You will use state for this.

Think of state as the minimal set of changing data that your app needs to remember. The most important principle for structuring state is to keep it DRY (Don't Repeat Yourself). Figure out the absolute minimal representation of the state your application needs and compute everything else on-demand. For example, if you're building a shopping list, you can store the items as an array in state. If you want to also display the number of items in the list, don't store the number of items as another state value--instead, read the length of your array.

Now think of all of the pieces of data in this example application:

  1. The original list of products
  2. The search text the user has entered
  3. The value of the checkbox
  4. The filtered list of products

Which of these are state? Identify the ones that are not:

  • Does it remain unchanged over time? If so, it isn't state.
  • Is it passed in from a parent via props? If so, it isn't state.
  • Can you compute it based on existing state or props in your component? If so, it definitely isn't state!

What's left is probably state.

Let's go through them one by one again:

  1. The original list of products is passed in as props, so it's not state.
  2. The search text seems to be state since it changes over time and can't be computed from anything.
  3. The value of the checkbox seems to be state since it changes over time and can't be computed from anything.
  4. The filtered list of products isn't state because it can be computed by taking the original list of products and filtering it according to the search text and value of the checkbox.

This means only the search text and the value of the checkbox are state! Nicely done!

Deep Dive

Props vs State

There are two types of \"model\" data in ReactPy: props and state. The two are very different:

  • Props are like arguments you pass to a function. They let a parent component pass data to a child component and customize its appearance. For example, a search_bar() component can pass a color prop to a badge() component you wrote. This is about your components: html.form and html.button are tag constructors, and what you hand them is an attribute dictionary, not props a component reads.
  • State is like a component\u2019s memory. It lets a component keep track of some information and change it in response to interactions. For example, a badge() component you wrote might keep track of is_hovered state. Tags cannot hold state \u2014 html.button is a function that returns a dictionary, and a dictionary has no memory between renders.

Props and state are different, but they work together. A parent component will often keep some information in state (so that it can change it), and pass it down to child components as their props. It's okay if the difference still feels fuzzy on the first read. It takes a bit of practice for it to really stick!

"},{"location":"learn/thinking-in-react/#step-4-identify-where-your-state-should-live","title":"Step 4: Identify where your state should live","text":"

After identifying your app\u2019s minimal state data, you need to identify which component is responsible for changing this state, or owns the state. Remember: React uses one-way data flow, passing data down the component hierarchy from parent to child component. It may not be immediately clear which component should own what state. This can be challenging if you\u2019re new to this concept, but you can figure it out by following these steps!

For each piece of state in your application:

  1. Identify every component that renders something based on that state.
  2. Find their closest common parent component\u2014a component above them all in the hierarchy.
  3. Decide where the state should live:
    1. Often, you can put the state directly into their common parent.
    2. You can also put the state into some component above their common parent.
    3. If you can't find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common parent component.

In the previous step, you found two pieces of state in this application: the search input text, and the value of the checkbox. In this example, they always appear together, so it makes sense to put them into the same place.

Now let's run through our strategy for them:

  1. Identify components that use state:
    • product_table needs to filter the product list based on that state (search text and checkbox value).
    • search_bar needs to display that state (search text and checkbox value).
  2. Find their common parent: The first parent component both components share is filterable_product_table.
  3. Decide where the state lives: We'll keep the filter text and checked state values in filterable_product_table.

So the state values will live in filterable_product_table.

Add state to the component with the use_state() Hook. Hooks are special functions that let you \"hook into\" React. Add two state variables at the top of filterable_product_table and specify their initial state:

@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = use_state(\"\")\n    in_stock_only, set_in_stock_only = use_state(False)\n

Then, pass filter_text and in_stock_only to product_table and search_bar as props:

html.div(\n    search_bar(filter_text=filter_text, in_stock_only=in_stock_only),\n    product_table(\n        products=products, filter_text=filter_text, in_stock_only=in_stock_only\n    ),\n)\n

You can start seeing how your application will behave. Edit the filter_text initial value from use_state('') to use_state('fruit') in the sandbox code below. You'll see both the search input text and the table update:

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = use_state(\"\")\n    in_stock_only, set_in_stock_only = use_state(False)\n\n    return html.div(\n        search_bar(filter_text=filter_text, in_stock_only=in_stock_only),\n        product_table(\n            products=products, filter_text=filter_text, in_stock_only=in_stock_only\n        ),\n    )\n\n\n@component\ndef product_category_row(category):\n    return html.tr(\n        html.th({\"colspan\": 2}, category),\n    )\n\n\n@component\ndef product_row(product):\n    if product[\"stocked\"]:\n        name = product[\"name\"]\n    else:\n        name = html.span({\"style\": {\"color\": \"red\"}}, product[\"name\"])\n\n    return html.tr(\n        html.td(name),\n        html.td(product[\"price\"]),\n    )\n\n\n@component\ndef product_table(products, filter_text, in_stock_only):\n    rows = []\n    last_category = None\n\n    for product in products:\n        if filter_text.lower() not in product[\"name\"].lower():\n            continue\n        if in_stock_only and not product[\"stocked\"]:\n            continue\n        if product[\"category\"] != last_category:\n            rows.append(\n                product_category_row(product[\"category\"], key=product[\"category\"])\n            )\n        rows.append(product_row(product, key=product[\"name\"]))\n        last_category = product[\"category\"]\n\n    return html.table(\n        html.thead(\n            html.tr(\n                html.th(\"Name\"),\n                html.th(\"Price\"),\n            ),\n        ),\n        html.tbody(rows),\n    )\n\n\n@component\ndef search_bar(filter_text, in_stock_only):\n    return html.form(\n        html.input({\"type\": \"text\", \"value\": filter_text, \"placeholder\": \"Search...\"}),\n        html.label(\n            html.input({\"type\": \"checkbox\", \"checked\": in_stock_only}),\n            \"Only show products in stock\",\n        ),\n    )\n\n\nPRODUCTS = [\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Apple\"},\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Dragonfruit\"},\n    {\"category\": \"Fruits\", \"price\": \"$2\", \"stocked\": False, \"name\": \"Passionfruit\"},\n    {\"category\": \"Vegetables\", \"price\": \"$2\", \"stocked\": True, \"name\": \"Spinach\"},\n    {\"category\": \"Vegetables\", \"price\": \"$4\", \"stocked\": False, \"name\": \"Pumpkin\"},\n    {\"category\": \"Vegetables\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Peas\"},\n]\n\n\n@component\ndef app():\n    return filterable_product_table(PRODUCTS)\n
body {\n    padding: 5px;\n}\nlabel {\n    display: block;\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\nth {\n    padding-top: 5px;\n}\ntd {\n    padding: 2px;\n}\n
# TODO\n

Notice that editing the form doesn't work yet.

In the code above, product_table and search_bar read the filter_text and in_stock_only props to render the table, the input, and the checkbox. For example, here is how search_bar populates the input value:

@component\ndef search_bar(filter_text, in_stock_only):\n    return html.form(\n        html.input(\n            {\n                \"type\": \"text\",\n                \"value\": filter_text,\n                \"placeholder\": \"Search...\",\n            }\n        ),\n        html.p(\n            html.input({\"type\": \"checkbox\", \"checked\": in_stock_only}),\n            \"Only show products in stock\",\n        ),\n    )\n

However, you haven't added any code to respond to the user actions like typing yet. This will be your final step.

"},{"location":"learn/thinking-in-react/#step-5-add-inverse-data-flow","title":"Step 5: Add inverse data flow","text":"

Currently your app renders correctly with props and state flowing down the hierarchy. But to change the state according to user input, you will need to support data flowing the other way: the form components deep in the hierarchy need to update the state in filterable_product_table.

React makes this data flow explicit, but it requires a little more typing than two-way data binding. If you try to type or check the box in the example above, you'll see that React ignores your input. This is intentional. By writing html.input({\"value\": filter_text}), you've set the value attribute of the html.input to always be equal to the filter_text state passed in from filterable_product_table. Since filter_text state is never set, the input never changes.

You want to make it so whenever the user changes the form inputs, the state updates to reflect those changes. The state is owned by filterable_product_table, so only it can call set_filter_text and set_in_stock_only. To let search_bar update the filterable_product_table's state, you need to pass these functions down to search_bar:

@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = hooks.use_state(\"\")\n    in_stock_only, set_in_stock_only = hooks.use_state(False)\n\n    return html.div(\n        search_bar(\n            filter_text=filter_text,\n            in_stock_only=in_stock_only,\n            set_filter_text=set_filter_text,\n            set_in_stock_only=set_in_stock_only,\n        )\n    )\n

Inside the search_bar, you will add the onChange event handlers and set the parent state from them:

async def handle_filter_text_change(event):\n    set_filter_text(await event.target.value)\n\n\nhtml.input(\n    {\n        \"type\": \"text\",\n        \"value\": filter_text,\n        \"placeholder\": \"Search...\",\n        \"onChange\": handle_filter_text_change,\n    }\n)\n

Now the application fully works!

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef filterable_product_table(products):\n    filter_text, set_filter_text = use_state(\"\")\n    in_stock_only, set_in_stock_only = use_state(False)\n\n    return html.div(\n        search_bar(\n            filter_text=filter_text,\n            in_stock_only=in_stock_only,\n            set_filter_text=set_filter_text,\n            set_in_stock_only=set_in_stock_only,\n        ),\n        product_table(\n            products=products, filter_text=filter_text, in_stock_only=in_stock_only\n        ),\n    )\n\n\n@component\ndef product_category_row(category):\n    return html.tr(\n        html.th({\"colspan\": 2}, category),\n    )\n\n\n@component\ndef product_row(product):\n    if product[\"stocked\"]:\n        name = product[\"name\"]\n    else:\n        name = html.span({\"style\": {\"color\": \"red\"}}, product[\"name\"])\n\n    return html.tr(\n        html.td(name),\n        html.td(product[\"price\"]),\n    )\n\n\n@component\ndef product_table(products, filter_text, in_stock_only):\n    rows = []\n    last_category = None\n\n    for product in products:\n        if filter_text.lower() not in product[\"name\"].lower():\n            continue\n        if in_stock_only and not product[\"stocked\"]:\n            continue\n        if product[\"category\"] != last_category:\n            rows.append(\n                product_category_row(product[\"category\"], key=product[\"category\"])\n            )\n        rows.append(product_row(product, key=product[\"name\"]))\n        last_category = product[\"category\"]\n\n    return html.table(\n        html.thead(\n            html.tr(\n                html.th(\"Name\"),\n                html.th(\"Price\"),\n            ),\n        ),\n        html.tbody(rows),\n    )\n\n\n@component\ndef search_bar(filter_text, in_stock_only, set_filter_text, set_in_stock_only):\n    async def handle_filter_text_change(event):\n        set_filter_text(await event.target.value)\n\n    async def handle_in_stock_only_change(event):\n        set_in_stock_only(await event.target.checked)\n\n    return html.form(\n        html.input(\n            {\n                \"type\": \"text\",\n                \"value\": filter_text,\n                \"placeholder\": \"Search...\",\n                \"onChange\": handle_filter_text_change,\n            }\n        ),\n        html.label(\n            html.input(\n                {\n                    \"type\": \"checkbox\",\n                    \"checked\": in_stock_only,\n                    \"onChange\": handle_in_stock_only_change,\n                }\n            ),\n            \"Only show products in stock\",\n        ),\n    )\n\n\nPRODUCTS = [\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Apple\"},\n    {\"category\": \"Fruits\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Dragonfruit\"},\n    {\"category\": \"Fruits\", \"price\": \"$2\", \"stocked\": False, \"name\": \"Passionfruit\"},\n    {\"category\": \"Vegetables\", \"price\": \"$2\", \"stocked\": True, \"name\": \"Spinach\"},\n    {\"category\": \"Vegetables\", \"price\": \"$4\", \"stocked\": False, \"name\": \"Pumpkin\"},\n    {\"category\": \"Vegetables\", \"price\": \"$1\", \"stocked\": True, \"name\": \"Peas\"},\n]\n\n\n@component\ndef app():\n    return filterable_product_table(PRODUCTS)\n
body {\n    padding: 5px;\n}\nlabel {\n    display: block;\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\nth {\n    padding: 4px;\n}\ntd {\n    padding: 2px;\n}\n
# TODO\n

You can learn all about handling events and updating state in the Adding Interactivity section.

"},{"location":"learn/thinking-in-react/#where-to-go-from-here","title":"Where to go from here","text":"

This was a very brief introduction to how to think about building components and applications with React. You can start a React project right now or dive deeper on all the syntax used in this tutorial.

"},{"location":"learn/tools-and-packages/","title":"Tools, Libraries, and Packages","text":"

Use external ReactPy packages and tools to help you build your projects.

"},{"location":"learn/tools-and-packages/#first-party-packages","title":"First party packages","text":"

The Reactive Python organization provides several packages and tools to help you build your projects.

Package Description ReactPy Router URL router for ReactPy to help you build single-page applications. ReactPy Django A Django integration for ReactPy. Comes with several Django-exclusive features."},{"location":"learn/tools-and-packages/#third-party-packages","title":"Third party packages","text":"

There are also a number of third-party packages that can help you build your projects.

You can find a list of these packages on the ReactPy GitHub Discussions.

"},{"location":"learn/tutorial-material-ui/","title":"Tutorial material ui","text":"

Planned / Undeveloped

This tutorial is planned, but is missing a key feature before this page can be written.

See this issue for more details.

"},{"location":"learn/tutorial-react-bootstrap/","title":"Tutorial react bootstrap","text":"

Planned / Undeveloped

This tutorial is planned, but is missing a key feature before this page can be written.

See this issue for more details.

"},{"location":"learn/tutorial-tic-tac-toe/","title":"Tutorial: Tic-Tac-Toe","text":""},{"location":"learn/tutorial-tic-tac-toe/#overview","title":"Overview","text":"

You will build a small tic-tac-toe game. This tutorial assumes no prior ReactPy knowledge, and the techniques it covers \u2014 components, props, state, and lifting state up \u2014 are the ones you will use in every app you write.

Note

This tutorial is for people who prefer to learn by doing. If you would rather meet each concept one at a time, start with Your First Component instead.

The tutorial is divided into four sections:

  • Setup for the tutorial gives you a starting point.
  • The basics teaches you the fundamentals: components, props, and state.
  • Completing the game teaches you the most common techniques.
  • Adding time travel shows why immutability matters.
"},{"location":"learn/tutorial-tic-tac-toe/#what-are-you-building","title":"What are you building?","text":"

You will build an interactive tic-tac-toe game. Here is the finished result:

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef square(value, on_click):\n    return html.button(\n        {\"className\": \"square\", \"onClick\": on_click},\n        value,\n    )\n\n\n@component\ndef board(x_is_next, squares, on_play):\n    def handle_click(i):\n        async def inner(event):\n            \"\"\"\n            Due to a quirk of Python, if your event handler needs args other than\n            `event`, you will need to create a wrapper function as seen above.\n            Ref: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/cell-var-from-loop.html\n            \"\"\"\n            if calculate_winner(squares) or squares[i]:\n                return\n\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            on_play(next_squares)\n\n        return inner\n\n    winner = calculate_winner(squares)\n    status = (\n        f\"Winner: {winner}\" if winner else \"Next player: \" + (\"X\" if x_is_next else \"O\")\n    )\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n\n\n@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n    current_move, set_current_move = use_state(0)\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    def handle_play(next_squares):\n        next_history = history[: current_move + 1]\n        next_history.append(next_squares)\n        set_history(next_history)\n        set_current_move(len(next_history) - 1)\n\n    def jump_to(next_move):\n        async def go_to_move(event):\n            set_current_move(next_move)\n\n        return go_to_move\n\n    moves = []\n    for move, _squares in enumerate(history):\n        description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n\n        moves.append(\n            html.li(\n                {\"key\": move},\n                html.button({\"onClick\": jump_to(move)}, description),\n            )\n        )\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol(moves)),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for line in lines:\n        a, b, c = line\n        if not squares:\n            continue\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

If the code does not make sense yet, do not worry \u2014 that is the point of the tutorial. Play the game before you read it. You will notice a list to the right of the board that records every move and lets you jump back to any of them. That list is what the last section builds toward.

Note

Throughout this tutorial, app.py is the file you edit, and the styles.css tab shows the stylesheet it relies on. ReactPy serves your components, not files on your disk, so the stylesheet is loaded into the page in your ASGI entry module. Setup for the tutorial shows how.

"},{"location":"learn/tutorial-tic-tac-toe/#setup-for-the-tutorial","title":"Setup for the tutorial","text":"

The code below is your starting point: a single square, with no interactivity.

app.pystyles.css Run
@component\ndef square():\n    return html.button({\"className\": \"square\"}, \"X\")\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

It should render one empty-looking square containing an X.

Note

You can follow along in your own editor instead. You will need:

  1. Python 3.11 or newer, with pip install reactpy[asgi] uvicorn[standard]
  2. A file app.py containing the example above
  3. A file styles.css containing the stylesheet above
  4. A file main.py that mounts app.py and injects the stylesheet:

    from pathlib import Path\n\nfrom app import square\n\nfrom reactpy import html\nfrom reactpy.executors.asgi import ReactPy\n\n# ReactPy serves the components, not the files on your disk, so the stylesheet\n# is read here and injected into the page's <head>.\nCSS = Path(\"styles.css\").read_text()\n\nmy_app = ReactPy(square, html_head=html.head(html.style(CSS)))\n
  5. uvicorn main:my_app, then open the URL it prints

If the setup fights you, do not let it stop you \u2014 follow along online and try the local setup again later. See Creating a Standalone React App for the details.

"},{"location":"learn/tutorial-tic-tac-toe/#the-basics-components-props-and-state","title":"The basics: components, props, and state","text":""},{"location":"learn/tutorial-tic-tac-toe/#inspecting-the-starter-code","title":"Inspecting the starter code","text":"

The example defines a component. In ReactPy, a component is a function that returns what part of the interface should look like. Read it line by line:

@component\ndef square():\n    return html.button({\"className\": \"square\"}, \"X\")\n

The first two lines are boilerplate: @component turns the function below it into a component, and html gives you a function for every HTML tag.

The def square(): line defines a function called square. Unlike React, ReactPy component names are snake_case and lowercase \u2014 there is no capital-letter rule, because ReactPy tells a component from an HTML tag by whether you call it, not by how it is spelled.

The return line gives back a button. html.button(...) is a call to a function, not an element literal: children come after the attribute dictionary as positional arguments, so \"X\" is the text inside the button. {\"className\": \"square\"} is that attribute dictionary, and className is the DOM attribute name \u2014 attribute keys are forwarded to the browser verbatim, so they keep React's camelCase spelling.

"},{"location":"learn/tutorial-tic-tac-toe/#building-the-board","title":"Building the board","text":"

The board needs nine squares, not one. Copying the button twice is tempting:

@component\ndef square():\n    return html.button({\"className\": \"square\"}, \"X\"), html.button({\"className\": \"square\"}, \"X\")\n

That does not fail the way React's \"adjacent JSX elements\" error does. The comma builds a two-item tuple; ReactPy does not know you meant two children, coerces it with str(), and the page shows the VDOM dictionaries as literal text:

({'children': ['X'], 'attributes': {'className': 'square'}, 'tagName': 'button'}, ...)\n

Verified through Layout.render(): the model's only child is that string. If the page ever shows you a Python repr, a missing call or a stray comma is the usual cause. A component returns exactly one thing. To return several children, wrap them in a fragment with html.fragment(...), which groups children without adding an element of its own:

@component\ndef square():\n    return html.fragment(\n        html.button({\"className\": \"square\"}, \"X\"),\n        html.button({\"className\": \"square\"}, \"X\"),\n    )\n

Now copy-paste your way to nine squares and... they come out in a single line, not a grid. Group them into rows with divs carrying the className the stylesheet already defines, and number the squares so you can tell them apart:

app.pystyles.css Run
@component\ndef square():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"1\"),\n            html.button({\"className\": \"square\"}, \"2\"),\n            html.button({\"className\": \"square\"}, \"3\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"4\"),\n            html.button({\"className\": \"square\"}, \"5\"),\n            html.button({\"className\": \"square\"}, \"6\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"7\"),\n            html.button({\"className\": \"square\"}, \"8\"),\n            html.button({\"className\": \"square\"}, \"9\"),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

You now have a tic-tac-toe board. But you have a naming problem: the component is called square and renders a whole board. Rename it to board.

At this point your code should look like this:

app.pystyles.css Run
@component\ndef board():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"1\"),\n            html.button({\"className\": \"square\"}, \"2\"),\n            html.button({\"className\": \"square\"}, \"3\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"4\"),\n            html.button({\"className\": \"square\"}, \"5\"),\n            html.button({\"className\": \"square\"}, \"6\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            html.button({\"className\": \"square\"}, \"7\"),\n            html.button({\"className\": \"square\"}, \"8\"),\n            html.button({\"className\": \"square\"}, \"9\"),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Note

That is a lot of typing. Copying code from this page is fine, but you will learn more by typing each snippet at least once yourself.

"},{"location":"learn/tutorial-tic-tac-toe/#passing-data-through-props","title":"Passing data through props","text":"

Nine hardcoded buttons means nine places to change when a square's content becomes dynamic. Instead, extract a square component and give it the value to display \u2014 a prop:

@component\ndef square(value):\n    return html.button({\"className\": \"square\"}, value)\n

There is no JSX, so props are simply Python parameters. Call the component and pass the argument:

square(\"A\")\n

Update board to call square nine times, passing a different letter each time so you can see the values arrive:

app.pystyles.css Run
@component\ndef square(value):\n    return html.button({\"className\": \"square\"}, value)\n\n\n@component\ndef board():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(\"A\"),\n            square(\"B\"),\n            square(\"C\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(\"D\"),\n            square(\"E\"),\n            square(\"F\"),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(\"G\"),\n            square(\"H\"),\n            square(\"I\"),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Notice how board and square are both called the same way, with no ceremony, while html.div and html.button are calls into the html object. Components are just functions you wrote; tags are functions ReactPy provides.

Props are read-only. square cannot assign to value to change what its parent passed, and ReactPy has no mechanism to help it try. Data flows down; to change it, the component that owns it must change it.

"},{"location":"learn/tutorial-tic-tac-toe/#making-an-interactive-component","title":"Making an interactive component","text":"

Now make a square fill itself when clicked. Add a handler inside square and register it under onClick:

@component\ndef square():\n    async def handle_click(event):\n        print(\"clicked!\")\n\n    return html.button({\"className\": \"square\", \"onClick\": handle_click})\n

A handler is a coroutine, and ReactPy calls it with exactly one argument \u2014 the event. Name it event, even if you ignore it.

Unlike React, there is no browser console to open: ReactPy components run on the server, so print lands in the terminal running your app, next to the URL. Click a square several times and you will see the line repeat there.

Pitfall

The key must be camelCase onClick. The React-looking on_click is accepted and shipped to the browser like any other on* key \u2014 and then nothing listens for it, because no browser event is named on_click. Verified: the server-side model holds a registered on_click handler while clicking the button leaves the component's state at 0. The button renders, looks normal, and never fires, with no error and no log line anywhere. If a control does nothing, check the spelling of the key first. See Responding to Events.

Next, make the square remember that it was clicked. ReactPy gives components memory with the use_state hook, which returns the current value and the function that changes it:

app.pystyles.css Run
# None is what an unfilled square holds; ReactPy renders None as nothing.\nEMPTY_SQUARE: str | None = None\n\n\n@component\ndef square():\n    value, set_value = use_state(EMPTY_SQUARE)\n\n    async def handle_click(event):\n        set_value(\"X\")\n\n    return html.button({\"className\": \"square\", \"onClick\": handle_click}, value)\n\n\n@component\ndef board():\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(),\n            square(),\n            square(),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(),\n            square(),\n            square(),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(),\n            square(),\n            square(),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

value, set_value = use_state(EMPTY_SQUARE) unpacks a pair: the state you read and the setter you call. Its argument is the starting value, used only on the component's first render. Calling set_value asks ReactPy to render the component again, and that render sees the new value.

The value is None, meaning \"empty\", and ReactPy renders None as nothing, so a fresh board is blank. It lives in a module-level constant annotated str | None, which is what lets a type checker accept both None and \"X\" in that square \u2014 without it, the type is inferred from None alone and assigning a mark is flagged. See Use State.

Note that ReactPy renders True and False as the words True and False, so None is the only value that disappears \u2014 React's habit of writing cond && <X/> has no working Python equivalent. See Conditional Rendering.

Click each square and watch them fill independently. Nine square() calls contain nine separate use_state calls, and each one keeps its own value.

That is a defect, not a feature. Each square's value lives inside that square, so no other part of the program can see the whole board \u2014 which means nothing can answer \"has someone won?\". The next section fixes this by moving the state to where the whole board is visible.

"},{"location":"learn/tutorial-tic-tac-toe/#completing-the-game","title":"Completing the game","text":"

Right now the game has a serious limitation: nobody can win.

"},{"location":"learn/tutorial-tic-tac-toe/#lifting-state-up","title":"Lifting state up","text":"

To detect a winner you need the whole board in one place. The fix is to move the state out of square and into board, which already renders all nine squares \u2014 a move called lifting state up.

board owns a list of nine values and passes each one down:

app.pystyles.css Run
@component\ndef square(value, on_click):\n    return html.button({\"className\": \"square\", \"onClick\": on_click}, value)\n\n\n@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n\n    def handle_click(i):\n        async def inner(event):\n            if squares[i]:\n                return\n            next_squares = squares.copy()\n            next_squares[i] = \"X\"\n            set_squares(next_squares)\n\n        return inner\n\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

square is now stateless: it takes value to display and on_click to call, and remembers nothing. That is the shape of most components you will write \u2014 they take data in and report events out, and the state lives above them.

Note the spelling distinction, because both forms are correct in different places. on_click above is a Python parameter \u2014 a prop name you invented, so it is snake_case like every other identifier you define. onClick is the attribute dictionary key the browser needs, so it keeps its camelCase spelling. Naming a prop onClick or writing the attribute key on_click are both mistakes, in opposite directions.

Two details are worth pausing on.

First, the handler needs to know which square was clicked, but ReactPy hands a handler exactly one argument: the event. The answer is a factory, as handle_click(i) does here \u2014 it returns the handler that closes over i:

def handle_click(i):\n    async def inner(event):\n        ...\n\n    return inner\n\n\nhtml.button({\"onClick\": handle_click(i)}, squares[i])\n

A lambda is not available for this. It cannot be a coroutine, so ReactPy refuses it as a handler; functools.partial(handle_click, i) over a two-parameter handler is the other shape which works \u2014 but prefer the factory. ReactPy unwraps a partial before reading your code, yet it then looks for preventDefault() and friends on your function's first parameter, and a partial has already filled that one in. So a preventDefault() inside a partial-bound handler goes undetected and has to be declared with @event(prevent_default=True). The factory has no such problem: its inner async def inner(event) still leads with the event, so detection finds what you wrote.

The factory has a sharp edge inside a loop \u2014 the closure captures the variable, not the value \u2014 which Picking a key demonstrates.

Second, board renders three rows by hand, so square(...) appears nine times with nine different indices. A loop is shorter, and once you write one you will need keys \u2014 see Rendering Lists.

"},{"location":"learn/tutorial-tic-tac-toe/#why-immutability-is-important","title":"Why immutability is important","text":"

Look closely at what the handler does before it calls the setter:

next_squares = squares.copy()\nnext_squares[i] = \"X\"\nset_squares(next_squares)\n

It copies the list, changes the copy, and hands the copy to the setter. There are two ways to change data: mutate it in place, or replace it with a modified copy. The obvious Python code takes the first route, and it fails in a way that is hard to diagnose:

app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n\n    def handle_click(i):\n        # Mutating in place and handing back the same object: ReactPy compares\n        # the new value with the old one, sees the same object, and schedules no\n        # render at all. The button looks fine and never responds.\n        async def inner(event):\n            squares[i] = \"X\"\n            set_squares(squares)\n\n        return inner\n\n    return html.fragment(\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Click a square. Nothing happens \u2014 no error, no log line, no warning. The handler runs; the board just never changes.

Verified against this example: after three clicks on that square, the board is still nine empty cells. The cause is that set_squares(squares) passes back the very same object that is already in state. ReactPy compares the new value against the current one, finds them equal, and skips the render \u2014 an optimisation that turns an in-place mutation into a silent no-op.

Pitfall

Mutate-then-set-the-same-object is the most common ReactPy bug and the quietest one. squares[i] = \"X\" followed by set_squares(squares) schedules no render at all. Neither does mutating without calling the setter. Always build a new list \u2014 squares.copy(), a list comprehension, or slicing \u2014 and pass that. See Updating Arrays in State.

Immutability earns its keep twice here. It is what makes the board update at all, and it is what lets the last section keep every past move around: because old lists are never modified, they stay valid and can be re-displayed later.

Deep Dive

React's version of this section argues that immutability lets React skip re-rendering parts of the tree that did not change. That argument does not apply to ReactPy: there is no memo and no render bailout, so a child component's body re-runs whenever its parent renders, even with identical props. Immutable data is still required \u2014 for the reason above, and so equality checks mean what you expect \u2014 but do not expect it to save you renders. See You Might Not Need an Effect.

"},{"location":"learn/tutorial-tic-tac-toe/#taking-turns","title":"Taking turns","text":"

Players can fill squares, but only with X. Add a second piece of state to board for whose turn it is, and flip it after every move:

app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n    x_is_next, set_x_is_next = use_state(True)\n\n    def handle_click(i):\n        async def inner(event):\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            set_squares(next_squares)\n            set_x_is_next(not x_is_next)\n\n        return inner\n\n    status = \"Next player: \" + (\"X\" if x_is_next else \"O\")\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Squares now alternate X and O, and the status line above the board tracks the next player.

One defect remains: click a filled square and the other player overwrites it. That happens because the handler never checks whether the square is already taken. Return early instead of playing over it:

async def inner(event):\n    if squares[i]:\n        return\n    next_squares = squares.copy()\n    ...\n
app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n    x_is_next, set_x_is_next = use_state(True)\n\n    def handle_click(i):\n        async def inner(event):\n            if squares[i]:\n                return\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            set_squares(next_squares)\n            set_x_is_next(not x_is_next)\n\n        return inner\n\n    status = \"Next player: \" + (\"X\" if x_is_next else \"O\")\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Verified: clicking square 0 twice leaves X in place rather than replacing it with O, and the other squares still play.

The guard is the first line of the handler, before any state is built or set. Putting it after set_squares would let the invalid move through and try to undo it \u2014 an expensive, error-prone shape. Return early, then proceed.

"},{"location":"learn/tutorial-tic-tac-toe/#declaring-a-winner","title":"Declaring a winner","text":"

Add a helper that checks the eight winning lines. It knows nothing about ReactPy, so read it as plain Python:

def calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n

It returns \"X\", \"O\", or None. Where you put it in the file does not matter; keeping it at the bottom means you do not scroll past it every time.

Call it in two places \u2014 to stop play after a win, and to write the status line:

app.pystyles.css Run
@component\ndef board():\n    initial_board: list[str | None] = [None] * 9\n    squares, set_squares = use_state(initial_board)\n    x_is_next, set_x_is_next = use_state(True)\n\n    def handle_click(i):\n        async def inner(event):\n            if calculate_winner(squares) or squares[i]:\n                return\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            set_squares(next_squares)\n            set_x_is_next(not x_is_next)\n\n        return inner\n\n    winner = calculate_winner(squares)\n    status = (\n        f\"Winner: {winner}\" if winner else \"Next player: \" + (\"X\" if x_is_next else \"O\")\n    )\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Get three in a row and the status changes to Winner: X, after which no square responds \u2014 the guard returns early because calculate_winner(squares) is truthy.

Notice that the winner is not stored in state. It is computed from squares during every render, which is both simpler and impossible to get out of sync. If a value can be derived from state you are already storing, do not add a second piece of state for it: two sources of truth drift, and one number cannot. See Reacting to Input with State.

The game is playable. What it cannot do is show you how it got here \u2014 which is where immutability finally pays off.

"},{"location":"learn/tutorial-tic-tac-toe/#adding-time-travel","title":"Adding time travel","text":"

Because every move produced a new list instead of modifying the old one, the previous boards still exist. All that is left is to keep them.

The history is a list of boards, one per move:

[\n    # before the first move\n    [None] * 9,\n    # after the first move\n    [None, None, None, None, \"X\", None, None, None, None],\n    # after the second move\n    [None, None, None, None, \"X\", None, None, None, \"O\"],\n]\n
"},{"location":"learn/tutorial-tic-tac-toe/#lifting-state-up-again","title":"Lifting state up, again","text":"

Add a game component above board to hold that history. It will also decide which move you are looking at, so board must stop owning state and start receiving what to draw.

Start by having game render board inside the layout the stylesheet expects, and keep the history and the current move as its own state. board keeps its own handle_click, but now reports the finished move upward through an on_play prop instead of storing it:

@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n\n    def handle_play(next_squares):\n        set_history([*history, next_squares])\n\n    current_move = len(history) - 1\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol()),\n    )\n

x_is_next and current_squares are not state. They are computed from history during the render, which is why they cannot drift out of sync with it.

app.pystyles.css Run
@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n\n    def handle_play(next_squares):\n        set_history([*history, next_squares])\n\n    current_move = len(history) - 1\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol()),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

The game plays exactly as it did before \u2014 only now the whole history lives in one component that can see all of it.

"},{"location":"learn/tutorial-tic-tac-toe/#showing-the-past-moves","title":"Showing the past moves","text":"

You have a list of moves in state; render it as a list of buttons. Where React reaches for map, Python reaches for a comprehension or a loop:

moves = []\nfor move in range(len(history)):\n    description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n    moves.append(html.li(html.button({\"onClick\": jump_to(move)}, description)))\n

Then render html.ol(moves) inside the game-info div. Add a current_move state variable and an empty jump_to(move) that does nothing yet, so the list has somewhere to go:

app.pystyles.css Run
@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n    current_move, set_current_move = use_state(0)\n\n    def handle_play(next_squares):\n        next_history = deepcopy(history[: current_move + 1])\n        next_history.append(next_squares)\n        set_history(next_history)\n        set_current_move(len(next_history) - 1)\n\n    def jump_to(move):\n        async def inner(event):\n            # TODO: make the board show move\n            pass\n\n        return inner\n\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    moves = []\n    for move in range(len(history)):\n        description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n        moves.append(html.li(html.button({\"onClick\": jump_to(move)}, description)))\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol(moves)),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for a, b, c in lines:\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Make a few moves and the list grows: Go to game start, Go to move #1, Go to move #2. Clicking one does nothing, because jump_to is still the placeholder \u2014 that is the next step.

Pitfall

With REACTPY_DEBUG on, the server logs Key not specified for child in list ... for every row. ReactPy is telling you the same thing React does: a list you will re-render needs a key on each item. It is a warning, not an error, so a list can ship without one \u2014 and misbehave later in ways that are hard to trace back. The fix is Picking a key.

A related trap: pass a list, not a generator. html.ol(html.li(...) for ... in ...) skips the key check entirely, with the warning Did not verify key-path integrity of children in generator ... - pass a sequence, and an iterator stored in state is worse still \u2014 it is single-use, so the first render shows the rows and every later render shows nothing.

"},{"location":"learn/tutorial-tic-tac-toe/#picking-a-key","title":"Picking a key","text":"

When you re-render a list, ReactPy has to work out which item is which. It cannot read your intent, so you tell it: give each item a key.

html.li({\"key\": move}, html.button({\"onClick\": jump_to(move)}, description))\n

key is a reserved name, and it is spelled two different ways depending on what you are creating \u2014 mixing them up is a hard error:

You are creating Spell it Passing it the other way raises an element inside the attribute dict: html.li({\"key\": move}, ...) TypeError: Vdom.__call__() got an unexpected keyword argument 'key' a component as a keyword: row(item, key=item[\"id\"]) silently meaningless, since the dict is the attribute dict

A key identifies an item across renders. Change a component's key and ReactPy destroys it and builds a fresh one, discarding its state \u2014 which is a useful trick on its own, and the subject of Preserving and Resetting State. Keys need only be unique among siblings, not globally.

For the move list, the move's index is a legitimate key: moves are never reordered, inserted in the middle, or deleted, so index and identity agree. That is the exception. When items can be added or removed, the array index stops identifying anything, and state quietly lands on the wrong row \u2014 use a real identifier instead. See Rendering Lists.

"},{"location":"learn/tutorial-tic-tac-toe/#implementing-time-travel","title":"Implementing time travel","text":"

jump_to(move) only has to say which move you are looking at:

def jump_to(move):\n    async def inner(event):\n        set_current_move(move)\n\n    return inner\n

And the values derived during render must follow current_move rather than the end of the list:

x_is_next = current_move % 2 == 0\ncurrent_squares = history[current_move]\n

Playing a move now has one more rule. If you jump back and play from there, the moves after that point are no longer the future \u2014 they are discarded:

def handle_play(next_squares):\n    next_history = history[: current_move + 1]\n    next_history.append(next_squares)\n    set_history(next_history)\n    set_current_move(len(next_history) - 1)\n

The slice history[: current_move + 1] keeps everything up to and including the move you jumped back to, dropping the rest, and set_current_move puts you at the new end.

app.pystyles.css Run
from reactpy import component, html, use_state\n\n\n@component\ndef square(value, on_click):\n    return html.button(\n        {\"className\": \"square\", \"onClick\": on_click},\n        value,\n    )\n\n\n@component\ndef board(x_is_next, squares, on_play):\n    def handle_click(i):\n        async def inner(event):\n            \"\"\"\n            Due to a quirk of Python, if your event handler needs args other than\n            `event`, you will need to create a wrapper function as seen above.\n            Ref: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/cell-var-from-loop.html\n            \"\"\"\n            if calculate_winner(squares) or squares[i]:\n                return\n\n            next_squares = squares.copy()\n            next_squares[i] = \"X\" if x_is_next else \"O\"\n            on_play(next_squares)\n\n        return inner\n\n    winner = calculate_winner(squares)\n    status = (\n        f\"Winner: {winner}\" if winner else \"Next player: \" + (\"X\" if x_is_next else \"O\")\n    )\n\n    return html.fragment(\n        html.div({\"className\": \"status\"}, status),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[0], handle_click(0)),\n            square(squares[1], handle_click(1)),\n            square(squares[2], handle_click(2)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[3], handle_click(3)),\n            square(squares[4], handle_click(4)),\n            square(squares[5], handle_click(5)),\n        ),\n        html.div(\n            {\"className\": \"board-row\"},\n            square(squares[6], handle_click(6)),\n            square(squares[7], handle_click(7)),\n            square(squares[8], handle_click(8)),\n        ),\n    )\n\n\n@component\ndef game():\n    history, set_history = use_state([[None] * 9])\n    current_move, set_current_move = use_state(0)\n    x_is_next = current_move % 2 == 0\n    current_squares = history[current_move]\n\n    def handle_play(next_squares):\n        next_history = history[: current_move + 1]\n        next_history.append(next_squares)\n        set_history(next_history)\n        set_current_move(len(next_history) - 1)\n\n    def jump_to(next_move):\n        async def go_to_move(event):\n            set_current_move(next_move)\n\n        return go_to_move\n\n    moves = []\n    for move, _squares in enumerate(history):\n        description = f\"Go to move #{move}\" if move > 0 else \"Go to game start\"\n\n        moves.append(\n            html.li(\n                {\"key\": move},\n                html.button({\"onClick\": jump_to(move)}, description),\n            )\n        )\n\n    return html.div(\n        {\"className\": \"game\"},\n        html.div(\n            {\"className\": \"game-board\"},\n            board(x_is_next, current_squares, handle_play),\n        ),\n        html.div({\"className\": \"game-info\"}, html.ol(moves)),\n    )\n\n\ndef calculate_winner(squares):\n    lines = [\n        [0, 1, 2],\n        [3, 4, 5],\n        [6, 7, 8],\n        [0, 3, 6],\n        [1, 4, 7],\n        [2, 5, 8],\n        [0, 4, 8],\n        [2, 4, 6],\n    ]\n    for line in lines:\n        a, b, c = line\n        if not squares:\n            continue\n        if squares[a] and squares[a] == squares[b] and squares[a] == squares[c]:\n            return squares[a]\n\n    return None\n
* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: sans-serif;\n    margin: 20px;\n    padding: 0;\n}\n\n.square {\n    background: #fff;\n    border: 1px solid #999;\n    float: left;\n    font-size: 24px;\n    font-weight: bold;\n    line-height: 34px;\n    height: 34px;\n    margin-right: -1px;\n    margin-top: -1px;\n    padding: 0;\n    text-align: center;\n    width: 34px;\n}\n\n.board-row:after {\n    clear: both;\n    content: \"\";\n    display: table;\n}\n\n.status {\n    margin-bottom: 10px;\n}\n.game {\n    display: flex;\n    flex-direction: row;\n}\n\n.game-info {\n    margin-left: 20px;\n}\n
# TODO\n

Verified against this file: play three moves to put X in the corners, click Go to game start, and the board is empty again with Next player: X. Jump to Go to move #1, play a different square, and the list retracts to two entries \u2014 the abandoned branch is gone.

None of that would work if the handlers mutated squares in place. Every stored board would be the same list, and rewinding would show the current game. This is the payoff for the rule from Why immutability is important.

Pitfall

Do not store one board and a separate list of moves that points into it. The history must own the boards, and the boards must never change once stored. If you find yourself reaching for deepcopy here, that is a sign something is still being mutated \u2014 with the code above, the boards are never modified after they are created, so a plain slice is enough.

"},{"location":"learn/tutorial-tic-tac-toe/#final-cleanup","title":"Final cleanup","text":"

The finished game holds its state in three places, each as low as it can be while still seeing what it needs:

  • game owns history and current_move, because it renders both the board and the move list.
  • board owns nothing. It receives x_is_next, squares, and on_play.
  • square owns nothing. It receives value and on_click.

That is the pattern this tutorial was built around. State belongs to the component that needs it and to every component above it \u2014 no lower, because then a sibling cannot see it, and no higher, because then unrelated renders redo the work. When two components need the same data, lift it to their nearest common ancestor.

"},{"location":"learn/tutorial-tic-tac-toe/#wrapping-up","title":"Wrapping up","text":"
  • A component is a @component function returning one thing; several children go inside html.fragment(...) or a list.
  • Props are ordinary Python parameters, and they are read-only.
  • use_state gives a component memory; calling the setter re-renders it.
  • Handlers take exactly one argument, the event. Extra context is bound on the Python side, usually with a factory function.
  • Never mutate a list you have put in state \u2014 copy it, or the setter silently does nothing.
  • Derive what you can during render instead of storing it: winner, x_is_next, current_squares.
  • Lift state to the lowest component that can see everything that needs it.
  • Give list items a key \u2014 in the attribute dict for elements, as a keyword for components.
  • Time travel is free once your data is immutable, and impossible when it is not.
"},{"location":"learn/tutorial-tic-tac-toe/#related","title":"Related","text":"
  • Quick Start \u2014 the same building blocks at a faster pace
  • Your First Component \u2014 components and props in more depth
  • State: A Component's Memory \u2014 how use_state behaves
  • Adding Interactivity \u2014 events, handlers, and @event options
  • Updating Arrays in State \u2014 replace, never mutate
  • Sharing State Between Components \u2014 lifting state up
  • Rendering Lists \u2014 keys, and when the index is the wrong choice
  • Extracting State Logic into a Reducer \u2014 the next step once a component has too many state variables
"},{"location":"learn/updating-arrays-in-state/","title":"Updating Arrays in State","text":""},{"location":"learn/updating-arrays-in-state/#overview","title":"Overview","text":"

Lists are mutable in Python, but you should treat them as immutable when you store them in state. Just like with objects, when you want to update a list stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new list.

You will learn

  • How to add, remove, or change items in a list in ReactPy state
  • How to update an object inside of a list
  • Why a mutating update is dropped without any error
"},{"location":"learn/updating-arrays-in-state/#updating-lists-without-mutation","title":"Updating lists without mutation","text":"

In JavaScript you copy arrays with the spread syntax, and mutating methods like push() and pop() are easy to reach for by mistake. Python's equivalent methods \u2014 append(), extend(), insert(), remove(), pop(), sort(), reverse() \u2014 are in-place and return None, so the mistake looks different and fails harder:

async def handle_add(event):\n    items.append({\"id\": next_id, \"text\": text})   # mutates, returns None\n    set_items(items)                              # same object -> ignored\n

Nothing happens. ReactPy compares the new value against the old one with == (core/hooks.py, strictly_equal), sees nothing changed, and schedules no render. There is no error, no warning, and nothing in the browser console. Verified directly: mutating then setting the same list produces no render at all. Nor does setting a new list that happens to compare equal \u2014 which is why set_rows([*rows, 4]) works while mutating rows and passing it back does not.

So whenever you want to update a list, pass a new list to the setter. Python gives you two tools for that:

Instead of (mutates) Use (new list) items.append(x) [*items, x] items.remove(x) [i for i in items if i is not x] items[i] = x [*items[:i], x, *items[i + 1:]] items.pop(i) [*items[:i], *items[i + 1:]] items.insert(i, x) [*items[:i], x, *items[i:]] items.reverse() list(reversed(items)) items.sort(key=f) sorted(items, key=f)

Never store an iterator in state

This one has no JavaScript analogue and will bite you. Python's filter() and map() return lazy, single-use iterators, not lists:

set_items(filter(lambda i: i[\"id\"] != id_to_delete, items))   # \ud83d\udea9\n

That renders correctly once, which is exactly what makes it so hard to diagnose. The iterator is consumed while ReactPy walks the children, so the next render of that same state sees an exhausted iterator and displays nothing. Verified: a list of three items filtered to \"a,c\" on the first render, then an unrelated state change rendered an empty line.

Always build a list \u2014 a comprehension is clearest, list(filter(...)) if you must:

set_items([i for i in items if i[\"id\"] != id_to_delete])   # correct\n
"},{"location":"learn/updating-arrays-in-state/#adding-to-a-list","title":"Adding to a list","text":"

The common case is adding an item. The handler creates a new list ending with the new item:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state([{\"id\": 0, \"text\": \"Walk the dog\"}])\n    text, set_text = use_state(\"\")\n\n    async def handle_add(event):\n        event.preventDefault()\n        if not text:\n            return\n        # A new list; `items.append(...)` would be silently ignored.\n        next_id = max((item[\"id\"] for item in items), default=-1) + 1\n        set_items([*items, {\"id\": next_id, \"text\": text}])\n        set_text(\"\")\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.form(\n            {\"onSubmit\": handle_add},\n            html.input(\n                {\n                    \"value\": text,\n                    \"placeholder\": \"To do...\",\n                    \"onChange\": handle_text_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Add\"),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

Type a task and submit; it appends while the existing ones stay put. Note that the new id is computed from the existing items rather than from a module-level counter. A global counter is shared by every visitor to the server, so two users would collide on the same key \u2014 derive ids from the state you already have, or use uuid4().

"},{"location":"learn/updating-arrays-in-state/#removing-from-a-list","title":"Removing from a list","text":"

The most common removal is filtering out one item:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n            {\"id\": 2, \"text\": \"Teach the fish to juggle\"},\n        ]\n    )\n\n    def handle_delete(id_to_keep):\n        async def handler(event):\n            # filter() gives a new list; items.remove(...) would mutate it.\n            set_items([item for item in items if item[\"id\"] != id_to_keep])\n\n        return handler\n\n    return html.ul(\n        [\n            html.li(\n                {\"key\": item[\"id\"]},\n                item[\"text\"],\n                html.button({\"onClick\": handle_delete(item[\"id\"])}, \"Delete\"),\n            )\n            for item in items\n        ]\n    )\n
# TODO\n

[item for item in items if item[\"id\"] != id_to_keep] produces a brand-new list containing everything except the deleted item, and the original is left alone.

"},{"location":"learn/updating-arrays-in-state/#transforming-every-item","title":"Transforming every item","text":"

To apply a change to every item, build a new list by transforming each element:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n        ]\n    )\n\n    async def handle_click(event):\n        # map() becomes a comprehension; the old items stay untouched.\n        set_items([{\"id\": item[\"id\"], \"text\": item[\"text\"] + \"!\"} for item in items])\n\n    return html.div(\n        html.button({\"onClick\": handle_click}, \"Add !\"),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

Each item becomes a new dict, because mutating item[\"text\"] in place would leave the previous state pointing at the same mutated object.

Comprehensions do not modify the original

React's docs point out that filter() doesn't touch the original array, and a Python comprehension has the same property \u2014 it reads the old list and returns a fresh one. The catch, stated above, is that the built-in filter() does not return a list at all.

"},{"location":"learn/updating-arrays-in-state/#replacing-an-item","title":"Replacing an item","text":"

Unlike most languages, Python has no setAt(i, x) method, and the obvious items[i] = x mutates. Build the list from slices instead:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n        ]\n    )\n    text, set_text = use_state(\"\")\n\n    async def handle_replace(event):\n        event.preventDefault()\n        if not text:\n            return\n        # Replace one item by building a whole new list. Mutating\n        # items[0][\"text\"] would be invisible to ReactPy.\n        set_items([{\"id\": 0, \"text\": text}, *items[1:]])\n        set_text(\"\")\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.form(\n            {\"onSubmit\": handle_replace},\n            html.input(\n                {\n                    \"value\": text,\n                    \"placeholder\": \"Replace the first item\",\n                    \"onChange\": handle_text_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Replace\"),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

[{\"id\": 0, \"text\": text}, *items[1:]] replaces the first item and copies the rest. For an arbitrary index, [*items[:i], new_item, *items[i + 1:]] is the general form.

"},{"location":"learn/updating-arrays-in-state/#inserting-into-a-list","title":"Inserting into a list","text":"

To insert an item somewhere other than the end, slice around the insertion point:

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n        ]\n    )\n    text, set_text = use_state(\"\")\n\n    async def handle_insert(event):\n        event.preventDefault()\n        if not text:\n            return\n        # Slicing builds the new list; list.insert() would mutate in place.\n        set_items([*items[:1], {\"id\": 3, \"text\": text}, *items[1:]])\n        set_text(\"\")\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.form(\n            {\"onSubmit\": handle_insert},\n            html.input(\n                {\n                    \"value\": text,\n                    \"placeholder\": \"Insert at index 1\",\n                    \"onChange\": handle_text_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Insert\"),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n    )\n
# TODO\n

[*items[:1], new_item, *items[1:]] puts the new item at index 1. This is how React's \"insert into an array\" example translates; list.insert() mutates and is unusable here.

"},{"location":"learn/updating-arrays-in-state/#making-other-changes-to-a-list","title":"Making other changes to a list","text":"

The rule is uniform: copy first, then operate on the copy, or use a function that already returns a new list.

app.py Run
@component\ndef todo_list():\n    items, set_items = use_state(\n        [\n            {\"id\": 0, \"text\": \"Walk the dog\"},\n            {\"id\": 1, \"text\": \"Brush the cat\"},\n            {\"id\": 2, \"text\": \"Feed the fish\"},\n        ]\n    )\n    log, set_log = use_state([])\n\n    def record(label, next_items):\n        set_items(next_items)\n        set_log([*log, f\"{label}: {', '.join(i['text'] for i in next_items)}\"])\n\n    async def handle_reversed_click(event):\n        record(\"reversed\", list(reversed(items)))\n\n    async def handle_sorted_click(event):\n        record(\"sorted\", sorted(items, key=lambda i: i[\"text\"]))\n\n    async def handle_filtered_click(event):\n        record(\"filtered\", items[:2])\n\n    return html.div(\n        html.div(\n            html.button(\n                {\"onClick\": handle_reversed_click},\n                \"Reverse\",\n            ),\n            html.button(\n                {\"onClick\": handle_sorted_click},\n                \"Sort\",\n            ),\n            html.button(\n                {\"onClick\": handle_filtered_click},\n                \"Keep first two\",\n            ),\n        ),\n        html.ul([html.li({\"key\": item[\"id\"]}, item[\"text\"]) for item in items]),\n        html.ul([html.li({\"key\": i}, line) for i, line in enumerate(log)]),\n    )\n
# TODO\n

reversed() returns an iterator, so wrap it in list(...); sorted() already returns a new list; items[:2] is a slice, which is already a new list.

list.sort() and list.reverse() return None

These two are the most expensive traps because the failure is invisible. items.sort() sorts in place and returns None, so set_items(items.sort()) stores None in state. Use sorted(items) and list(reversed(items)).

"},{"location":"learn/updating-arrays-in-state/#updating-objects-inside-lists","title":"Updating objects inside lists","text":"

The examples above replaced whole dicts, which is the safe default. When an item is a dict whose fields you want to change one at a time, you need two copies: a new list, and a new dict for the item that changed.

app.py Run
@component\ndef form():\n    artworks, set_artworks = use_state(initial_artworks())\n\n    def handle_toggle(id_to_toggle):\n        async def handler(event):\n            # Copy the LIST, and copy the one dict inside it that changed.\n            # A shallow copy would leave the dict shared with the old state.\n            set_artworks(\n                [\n                    (\n                        {**artwork, \"seen\": not artwork[\"seen\"]}\n                        if artwork[\"id\"] == id_to_toggle\n                        else artwork\n                    )\n                    for artwork in artworks\n                ]\n            )\n\n        return handler\n\n    return html.ul(\n        [\n            html.li(\n                {\"key\": artwork[\"id\"]},\n                html.label(\n                    html.input(\n                        {\n                            \"type\": \"checkbox\",\n                            \"checked\": artwork[\"seen\"],\n                            \"onChange\": handle_toggle(artwork[\"id\"]),\n                        }\n                    ),\n                    f\"{artwork['title']} - {artwork['artist']}\",\n                ),\n            )\n            for artwork in artworks\n        ]\n    )\n
# TODO\n

Tick one checkbox and only that artwork's \"seen\" flips \u2014 verified, the pair goes from [False, False] to [True, False]. The comprehension copies every item, and only the targeted one is rebuilt with the changed field.

A shallow copy of the list is not enough

It is tempting to write:

next_items = list(artworks)          # new list...\nnext_items[0][\"seen\"] = True         # ...but the SAME dicts inside\nset_items(next_items)\n

That is a new list containing the same dict objects. Two problems follow. The assignment mutates a dict the previous state still references, so the change is invisible to ReactPy's comparison. And anything else holding that dict \u2014 another component seeded from the same fixture, a cached render \u2014 sees it flip too.

Copying is shallow at every level in Python; there is no deep copy on assignment or unpacking. Build the changed dict explicitly, as the example does. See Updating a nested object for the object-side version of this.

Keep identity stable for unchanged items

Notice the else artwork branch: unchanged items are passed through as the same objects, not rebuilt. That is both cheaper and useful \u2014 if you rebuild everything on each keystroke, nothing can be recognised as unchanged later.

"},{"location":"learn/updating-arrays-in-state/#writing-concise-update-logic","title":"Writing concise update logic","text":"

React's docs finish this page with Immer, which lets you write draft.push(...) behind a proxy. ReactPy has no Immer and no equivalent. There is no proxy layer to record your mutations, and no use_immer to import.

What you have instead is what this page already used:

  • List unpacking and comprehensions \u2014 [*items, x], [f(i) for i in items], [*items[:i], x, *items[i:]]. Verbose but explicit, and it fails loudly rather than silently.
  • copy.deepcopy \u2014 for a structure too nested to copy by hand.
  • use_reducer \u2014 moves the update logic out of the component, which is where the verbosity actually starts to hurt. See Extracting State Logic into a Reducer.

A homegrown Immer is worth avoiding specifically because it would paper over the failure mode this page is about: mutations are more dangerous here than in the browser, because ReactPy decides whether to render by comparing the old and new values on the server \u2014 so a mutation that hands back the same object, or an equal one, is dropped without a trace.

"},{"location":"learn/updating-arrays-in-state/#recap","title":"Recap","text":"
  • Treat lists in state as read-only: never call append(), sort(), insert(), remove(), and friends on them.
  • Build a new list with [*items, x] or a comprehension, and pass that to the setter.
  • Mutating and passing the same list back is silently dropped \u2014 no render, no error.
  • Never store filter()/map() results in state; they are single-use iterators and the second render will be empty.
  • To change an object inside a list, copy the list and copy the changed dict.
  • There is no Immer in ReactPy; use comprehensions, deepcopy, or use_reducer.
"},{"location":"learn/updating-objects-in-state/","title":"Updating Objects in State","text":""},{"location":"learn/updating-objects-in-state/#overview","title":"Overview","text":"

State can hold any kind of value, including dicts and lists. But you shouldn't change objects that you hold in ReactPy state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.

You will learn

  • How to correctly update an object in ReactPy state
  • How to update a nested object without mutating it
  • What immutability is, and how not to break it
  • Why a mutating update can be dropped without any error
"},{"location":"learn/updating-objects-in-state/#whats-a-mutation","title":"What's a mutation?","text":"

To mutate something means to change it after it was created.

Objects and lists in Python are mutable: you can change their contents in place. The mutating operations are usually visible in the code you write \u2014 obj[\"key\"] = value, list.append(x), list.pop(), del list[i], d.update(other) \u2014 but there are more hiding behind method names that don't announce themselves (list.sort(), list.extend(), list.reverse(), dict.setdefault()).

In ReactPy, treat state as read-only. Every time your code renders, you work with the state that already exists, and you want to cause changes over time based on user input. Leaving all mutation to the setter keeps your code easy to understand. This applies to state in general, not just object state.

Mutating state doesn't just risk a bug \u2014 it can silently do nothing

In React, mutating state is a \"may render stale UI\" hazard. In ReactPy it is sharper, because the setter compares the new value against the old one before scheduling anything \u2014 and that comparison accepts equality, not just identity (core/hooks.py, strictly_equal). Anything that compares equal, including a brand-new object, schedules no render.

Measured behaviour, each case dispatched through reactpy.core.layout.Layout:

What the handler does Result Mutates a list in place, then set_x(same_list) no render at all Mutates in place, calls no setter no render set_x([*old, item]) \u2014 a new object renders

The failure is completely silent: no error, no warning, nothing in the browser console. Your handler ran, ReactPy agreed that nothing changed, and the UI never updates. This is why the read-only rule is not a style preference here.

"},{"location":"learn/updating-objects-in-state/#treat-state-as-read-only","title":"Treat state as read-only","text":"

Consider a form that lets the user change their profile. This handler reaches into the state object and assigns a field directly:

async def handle_first_name(event):\n    person[\"firstName\"] = await event.target.value   # \ud83d\udea9 Mutate!\n    set_person(person)                                # same object -> ignored\n

person is the very object ReactPy already holds, so the assignment changes it in place and then hands the same object back. ReactPy compares it against what it already has, finds nothing changed, and skips the render \u2014 the field appears frozen while your dict quietly holds the new value.

The fix is to always create a new object for the next state:

async def handle_first_name(event):\n    set_person({**person, \"firstName\": await event.target.value})\n

That is the pattern for every state update in ReactPy: build a new value, pass it to the setter.

A pointer-tracking dot shows the same rule with no form in sight. The handler builds a brand-new position dict on every move:

app.py Run
@component\ndef moving_dot():\n    position, set_position = use_state({\"x\": 0, \"y\": 0})\n\n    async def handle_move(event):\n        # Replace the whole object: ReactPy never merges a partial update.\n        set_position({\"x\": await event.clientX, \"y\": await event.clientY})\n\n    return html.div(\n        {\n            \"onPointerMove\": handle_move,\n            \"style\": {\n                \"position\": \"relative\",\n                \"width\": \"100%\",\n                \"height\": \"180px\",\n                \"background\": \"#f0f0f0\",\n            },\n        },\n        html.div(\n            {\n                \"style\": {\n                    \"position\": \"absolute\",\n                    \"background\": \"black\",\n                    \"borderRadius\": \"50%\",\n                    \"width\": \"20px\",\n                    \"height\": \"20px\",\n                    \"left\": \"-10px\",\n                    \"top\": \"-10px\",\n                    \"transform\": f\"translate({position['x']}px, {position['y']}px)\",\n                }\n            }\n        ),\n    )\n
# TODO\n

The \"onPointerMove\" handler is on the grey container, and the dot is positioned from the coordinates it reads. Move the pointer anywhere inside the box and the dot follows. await event.clientX and await event.clientY are ordinary proxy reads \u2014 the value is fetched from the browser at the await, not shipped up front \u2014 so React's pointer and drag demos port across unchanged.

There is no automatic merge

set_x(...) replaces the state. It does not merge a partial object into what was there. Verified: setting {\"a\": 9} over {\"a\": 1, \"b\": 2} leaves {'a': 9} \u2014 \"b\" is simply gone.

If you have read React tutorials, you may remember setState merging objects. That was the class API. React's own useState replaces, and so does ReactPy (core/hooks.py, _CurrentState.dispatch assigns self.value = new). Any ReactJS text saying \"React merges the object into your current state\" is describing an API that no longer applies \u2014 port those examples with an explicit {**old, ...}.

Local mutation is fine

Mutating a value you just created inside the current render is safe, because nothing else has ever seen it. The same applies to the rows = [] plus rows.append(...) loop you will write constantly when building children:

@component\ndef list_of_parts(parts):\n    # Built during this render, never handed out before.\n    sorted_parts = sorted(parts, key=lambda p: p[\"importance\"])\n    return html.ul(\n        [html.li({\"key\": p[\"id\"]}, p[\"title\"]) for p in sorted_parts]\n    )\n

What must not be mutated is state ReactPy gave you, and props you received. See Local mutation: Your component's little secret for the rendering-side version of this rule.

"},{"location":"learn/updating-objects-in-state/#copying-objects-with-the-spread-syntax","title":"Copying objects with the spread syntax","text":"

To update an object, create a new one from the old one so you can change any of its properties. Python has no ... spread operator, but {**obj, \"key\": value} is the same operation on dicts, and [*items, x] is its twin for lists.

Here the form keeps the other fields intact while replacing one:

app.py Run
@component\ndef form():\n    person, set_person = use_state(\n        {\n            \"firstName\": \"Jane\",\n            \"lastName\": \"Jacobs\",\n            \"email\": \"jane@example.com\",\n        }\n    )\n\n    async def handle_first_name(event):\n        # Copy everything, then override just this one field.\n        set_person({**person, \"firstName\": await event.target.value})\n\n    async def handle_last_name(event):\n        set_person({**person, \"lastName\": await event.target.value})\n\n    async def handle_email(event):\n        set_person({**person, \"email\": await event.target.value})\n\n    return html.div(\n        html.input({\"value\": person[\"firstName\"], \"onChange\": handle_first_name}),\n        html.input({\"value\": person[\"lastName\"], \"onChange\": handle_last_name}),\n        html.input({\"value\": person[\"email\"], \"onChange\": handle_email}),\n        html.hr(),\n        html.h1(f\"Hello, {person['firstName']} {person['lastName']}!\"),\n        html.a({\"href\": f\"mailto:{person['email']}\"}, person[\"email\"]),\n    )\n
# TODO\n

Type in any field and watch the heading update. Each handler spreads the current person into a fresh dict and overrides exactly one key.

An unpacked dict is a shallow copy

{**person} copies the top level only. Any nested dict or list inside it is still shared with the previous state \u2014 see Updating a nested object.

The destructuring equivalents

Where React's docs reach for const { a, ...rest } = obj, Python reads with indexing and writes with unpacking. For the \"everything but one key\" shape, the direct translation is a comprehension:

rest = {k: v for k, v in obj.items() if k != \"drop\"}\n
"},{"location":"learn/updating-objects-in-state/#using-a-single-event-handler-for-multiple-fields","title":"Using a single event handler for multiple fields","text":"

Repeating a near-identical handler per field gets tedious. One handler can take the field name:

app.py Run
@component\ndef form():\n    person, set_person = use_state(\n        {\"firstName\": \"Jane\", \"lastName\": \"Jacobs\", \"email\": \"jane@example.com\"}\n    )\n\n    # One handler for every field. ReactPy always calls a handler with the\n    # event as its single positional argument, so the field name is bound by\n    # the thin handlers below rather than delivered by the client.\n    async def handle_change(field, event):\n        set_person({**person, field: await event.target.value})\n\n    async def handle_first_name_change(event):\n        await handle_change(\"firstName\", event)\n\n    async def handle_last_name_change(event):\n        await handle_change(\"lastName\", event)\n\n    async def handle_email_change(event):\n        await handle_change(\"email\", event)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": person[\"firstName\"],\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": person[\"lastName\"],\n                \"onChange\": handle_last_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": person[\"email\"],\n                \"onChange\": handle_email_change,\n            }\n        ),\n        html.hr(),\n        html.h1(f\"Hello, {person['firstName']} {person['lastName']}!\"),\n    )\n
# TODO\n

Note how the field name is bound by the thin per-field handler, not delivered by the client. ReactPy invokes every event handler with the event as its single positional argument \u2014 verified, a handler declaring two required parameters fails with TypeError: ... missing 1 required positional argument: 'event'. There is no e.target.name shortcut either: name is readable, but reading it costs a round trip to the page, and it has to be read from the element which fired the event. Any extra context which is already known on the server is cheaper bound in Python \u2014 by a named handler, a closure, or a functools.partial.

Reach for use_reducer when handlers multiply

Several handlers that all edit one object is a sign the update logic wants its own home. See Extracting State Logic into a Reducer.

"},{"location":"learn/updating-objects-in-state/#updating-a-nested-object","title":"Updating a nested object","text":"

State often contains nested objects. To update a deeply nested value, copy the object all the way up from where the change happens.

app.py Run
@component\ndef form():\n    person, set_person = use_state(\n        {\n            \"firstName\": \"Jane\",\n            \"lastName\": \"Jacobs\",\n            \"email\": \"jane@example.com\",\n            \"shippingAddress\": {\n                \"street\": \"123 Maple Street\",\n                \"city\": \"Pretendville\",\n            },\n        }\n    )\n\n    async def handle_city(event):\n        address = {**person[\"shippingAddress\"], \"city\": await event.target.value}\n        set_person({**person, \"shippingAddress\": address})\n\n    return html.div(\n        html.label(\n            \"City: \",\n            html.input(\n                {\n                    \"value\": person[\"shippingAddress\"][\"city\"],\n                    \"onChange\": handle_city,\n                }\n            ),\n        ),\n        html.h1(person[\"firstName\"]),\n        html.p(person[\"shippingAddress\"][\"street\"]),\n        html.p(person[\"shippingAddress\"][\"city\"]),\n    )\n
# TODO\n

Two copies are required:

  1. Copy person[\"shippingAddress\"] with the new \"city\".
  2. Copy person, replacing \"shippingAddress\" with that new dict.

Writing person[\"shippingAddress\"][\"city\"] = ... would mutate the nested dict in place. Because that nested dict is the same object the previous state still refers to, the change is invisible to ReactPy's comparison and the update is dropped.

Objects are not really nested

Python makes this point sharper than JavaScript does. Given:

person = {\"name\": \"Jane\", \"theme\": {\"background\": \"white\"}}\ncopy = {**person}\n

copy[\"theme\"] and person[\"theme\"] are the same dict. Python never deep-copies on assignment or on unpacking \u2014 copying is shallow for dicts, lists, and everything else. To get a genuinely independent copy you either copy each level explicitly, as the example does, or use copy.deepcopy:

import copy\n\nset_people(copy.deepcopy(people))\n

deepcopy is the blunt instrument: correct, but slower, and it copies everything reachable, including objects you did not mean to duplicate. Prefer explicit copies for the paths you actually change, and keep state to plain dicts, lists, and primitives so deepcopy remains a safe fallback.

Shallow sharing is also why two components seeded from the same module-level fixture end up aliasing each other's data \u2014 see Updating Arrays in State for the list version of that bug.

"},{"location":"learn/updating-objects-in-state/#writing-concise-update-logic","title":"Writing concise update logic","text":"

React's docs reach for Immer here, which records your mutations through a proxy and turns them into copies. ReactPy has no Immer and no equivalent. There is no proxy layer, so nothing could observe your mutations, and there is no use_immer to import.

What you have instead:

  • Dict and list unpacking \u2014 {**obj, \"k\": v}, [*items, x]. This covers the overwhelming majority of updates and is what every example above uses.
  • copy.deepcopy \u2014 for the rare shape too deep to copy by hand.
  • use_reducer \u2014 does not shorten updates, but moves them out of the component so their verbosity costs you less. See Extracting State Logic into a Reducer.

Resist building your own Immer. A proxy that logs mutations would have to run on the server, after a websocket round trip, which is not the problem Immer solves in the browser. If update logic feels genuinely painful, that usually means the state shape is wrong \u2014 see Choosing the State Structure.

"},{"location":"learn/updating-objects-in-state/#recap","title":"Recap","text":"
  • Treat everything ReactPy hands you as state or props as read-only: never assign into it, never call a mutating method on it.
  • Build a new object with {**old, ...} and pass that to the setter.
  • Mutating in place and passing the same object back is silently dropped \u2014 ReactPy compares the new value with the old and schedules no render when they are equal. Nothing in the browser will tell you.
  • Copying is shallow at every level; copy all the way up from the field that changed, or use copy.deepcopy.
  • Mutating a value you created during this render is fine.
  • There is no Immer in ReactPy; use dict and list unpacking, or restructure the state.
"},{"location":"learn/vdom-mutations/","title":"VDOM Mutations","text":""},{"location":"learn/vdom-mutations/#overview","title":"Overview","text":"

A ReactPy element is a dict, and Python lets you assign into it. That is the whole topic of this page: what is safe to change, what is merely pointless, and what quietly corrupts the model that the server is holding on behalf of every connected browser. The rule that decides all of it is that a model is owned by exactly one render \u2014 before it, yours to shape; after it, ReactPy's.

You will learn

  • Why mutation before a render is fine and after it is not
  • Which parts of a built element are copies and which are shared
  • The module-level element that leaks one user's change into everyone's page
  • Safe shapes: build fresh, deepcopy, and transform at build time
"},{"location":"learn/vdom-mutations/#the-rule-a-model-belongs-to-its-render","title":"The rule: a model belongs to its render","text":"

ReactPy serializes your element to JSON and sends it. Once that has happened, the dictionary is no longer purely yours \u2014 the layout keeps live references to the same objects so it can patch a subtree later (core/layout.py, Layout._create_layout_update reuses model.current rather than rebuilding it).

Split your code on that line:

  • Before the element is returned \u2014 mutate freely. Building a model incrementally out of helper functions is normal Python and nothing is watching.
  • After it has been returned from a render \u2014 treat it as frozen. Changes now affect a model the client has already received, and the server has no idea they happened.

Nothing in ReactPy enforces this. There is no freeze, no copy-on-render, no warning. That is why the failure modes below are worth memorizing.

"},{"location":"learn/vdom-mutations/#what-is-safe-before-the-render","title":"What is safe before the render","text":"

VdomDict is a light dict subclass, and it is strict about its top-level keys \u2014 exactly tagName, children, attributes, eventHandlers, inlineJavaScript, importSource:

d = html.div({\"id\": \"pre\"}, \"x\")\n\nd[\"tagName\"] = \"section\"       # fine\nd[\"attributes\"][\"id\"] = \"a\"    # fine \u2014 but only because \"attributes\" exists\nd[\"children\"].append(\"tail\")   # fine\nd[\"key\"] = 1                   # KeyError: 'Invalid key: key'\nd[\"ref\"] = object()            # KeyError: 'Invalid key: ref'\n

Note the first line passes an attributes dict. html.div(\"x\") omits the attributes key entirely when there are none \u2014 verified, it returns {'children': ['x'], 'tagName': 'div'} \u2014 so d[\"attributes\"][\"id\"] = \"a\" on such an element raises KeyError: 'attributes' before any guard is reached. Use d.setdefault(\"attributes\", {})[\"id\"] = \"a\" when the element may not have had one.

The guard only covers the six top-level names. d[\"attributes\"][\"anything\"] is unvalidated, and so is every nested value \u2014 the validation happens at serialization, and only in debug mode.

Keys and refs are not top-level keys

The React shapes {\"key\": ...} and {\"ref\": ...} raise, because a VdomDict has no such slot. A key goes inside attributes for an element (html.li({\"key\": 1}, ...)) or is passed as a keyword to a component (Item(x, key=1)). A ref does not exist at all \u2014 see Manipulating the DOM with Refs.

"},{"location":"learn/vdom-mutations/#shallow-copy-deep-sharing","title":"Shallow copy, deep sharing","text":"

The constructors copy the top level, so the containers you passed in are not aliased into the element \u2014 but the copy is shallow, and nested values still point at your objects:

app.py Run
# The tag constructors copy the top level, so the element's own attributes and\n# children containers are separate objects from the ones you passed in.\nstyle = {\"color\": \"red\"}\nattributes = {\"id\": \"card\", \"style\": style}\nchildren = [\"hello\"]\n\nelement = html.div(attributes, *children)\n\nprint(element[\"attributes\"] is attributes)  # False -- a copy\nprint(element[\"children\"] is children)  # False -- a fresh list\n\n# But the copy is shallow: nested values are still the objects you handed over.\nprint(element[\"attributes\"].get(\"style\") is style)  # True\n\nstyle[\"color\"] = \"blue\"\nprint(element[\"attributes\"].get(\"style\"))  # {'color': 'blue'} -- changed after the fact\n
# TODO\n

The same applies to child elements: pass a model you built earlier and the parent stores that object, not a copy. Mutating the child later changes the parent. This is convenient for builders that assemble a tree in stages, and dangerous for anything that outlives the render \u2014 which is the subject of the next section.

"},{"location":"learn/vdom-mutations/#mutating-after-the-render","title":"Mutating after the render","text":"

There are two ways this goes wrong, and they look nothing alike.

"},{"location":"learn/vdom-mutations/#case-1-the-mutation-is-never-sent","title":"Case 1: the mutation is never sent","text":"

Change a rendered element and ask for no re-render, and the browser never learns. This is the same class of bug as mutating state in place, and it shares the cause: ReactPy decides what to send from state changes, not from the shape of your dictionaries.

There is no error, no log line, and no console message. The element in the browser keeps its old text and you keep staring at the assignment that \"obviously\" should have worked.

A mutation you make while handling an event is invisible until something else renders

This is the version that costs an afternoon. In a real browser, against the shared-element example below: click mutate and the page does not change; click bump, which changes unrelated state, and the mutation appears \u2014 as if it had been waiting. Because the re-render walks the model again, it re-reads the dictionary you edited and ships the change with the next patch. A user's action becomes visible on the next action, which is a bug that looks like a timing problem and is not one.

"},{"location":"learn/vdom-mutations/#case-2-the-mutation-escapes-to-other-users","title":"Case 2: the mutation escapes to other users","text":"

The more damaging case, and the one unique to a server-side framework.

A model built at module level is one object shared by every connection to the server. Each connection gets its own Layout and its own hook state, but they all render the same dictionary. One visitor's handler mutates the object that everyone else's next render reads:

app.py Run
# Built once, at module scope. Every visitor's Layout renders this same dict.\nSTATUS = html.p({\"id\": \"status\"}, \"idle\")\n\n\n@component\ndef panel():\n    n, set_n = use_state(0)\n\n    async def mark_dirty(event):\n        # Mutates the shared element and asks for no render: nothing changes\n        # on screen, and the server's model now disagrees with every client.\n        STATUS[\"children\"] = [f\"mutated by visitor {n}\"]\n\n    async def bump(event):\n        # The next render of ANY state in this component re-sends STATUS --\n        # including the mutation, whoever made it.\n        set_n(n + 1)\n\n    return html.div(\n        html.h1({\"id\": \"counter\"}, f\"n={n}\"),\n        html.button({\"id\": \"dirty\", \"onClick\": mark_dirty}, \"mutate\"),\n        html.button({\"id\": \"bump\", \"onClick\": bump}, \"bump\"),\n        STATUS,\n    )\n
# TODO\n

Verified in a real browser with two separate tabs and two separate websockets against one server: tab A clicks mutate, then bump; tab B, which never clicked anything, then clicks bump and sees mutated by visitor 0 in its own page. Neither user can make it go away, because the mutated module-level dict is now the source of truth for every future render.

Module-level elements are shared across all connections

This has no browser-side analogue \u2014 in React, module scope is per-tab. Here module scope is per-process, and the process is shared. The rule follows directly: build elements inside the component. A function that returns html.p(...) costs one dictionary construction and removes the whole class of bug:

app.py Run
# A function that builds a fresh element on every call: the safe shape.\ndef status_banner(text, tone):\n    return html.p(\n        {\"id\": \"status\", \"className\": f\"status-{tone}\"},\n        text,\n    )\n\n\n@component\ndef panel():\n    n, set_n = use_state(0)\n    tone = \"ok\" if n == 0 else \"warn\"\n\n    async def handle_n_click(event):\n        set_n(n + 1)\n\n    return html.div(\n        html.h1(f\"n={n}\"),\n        html.button({\"onClick\": handle_n_click}, \"bump\"),\n        status_banner(f\"{n} items\", tone),  # a new dict every render\n    )\n
# TODO\n

Module-level constants are still fine when they are genuinely inert \u2014 a style dict, a list of option labels, an html.head() passed once to ReactPy(html_head=...) \u2014 because nothing writes to them. The bug needs a write. If nothing in your program assigns into it, module scope is safe.

"},{"location":"learn/vdom-mutations/#the-two-mutation-habits-that-also-defeat-state","title":"The two mutation habits that also defeat state","text":"

These belong to Updating Objects in State and Updating Arrays in State, but they are VDOM mutation seen from the other side, so they are repeated here because the symptom \u2014 a control that stops responding with no error \u2014 is the one people hunt for longest.

use_state's setter compares the new value against the old with == (core/hooks.py, strictly_equal). So:

rows.append(x)\nset_rows(rows)          # same object -> compares equal -> no render, no error\n

And the corollary that surprises people: a new object that merely compares equal is dropped too, so set_rows([*rows]) when rows is unchanged also renders nothing. Replace state with new, non-equal objects; never edit in place.

The same comparison is what makes use_memo fail to recompute after a mutate-then-set \u2014 the dependency compares equal to itself.

"},{"location":"learn/vdom-mutations/#safe-ways-to-shape-a-model","title":"Safe ways to shape a model","text":"

Reach for these in order.

"},{"location":"learn/vdom-mutations/#build-it-do-not-edit-it","title":"Build it, do not edit it","text":"

The default. Compose with functions, keep elements as return values, and pass data in as arguments. Conditional structure is an expression, not a post-construction edit:

def row(item):\n    return html.li(\n        {\"key\": item[\"id\"], \"className\": \"row\" if item[\"done\"] else \"row pending\"},\n        item[\"text\"],\n    )\n
"},{"location":"learn/vdom-mutations/#deepcopy-for-a-genuine-fork","title":"deepcopy for a genuine fork","text":"

When you must derive a variant from a model you do not own \u2014 typically something that came back from string_to_reactpy() or a cached template \u2014 copy it first:

from copy import deepcopy\n\nTEMPLATE = html.div({\"className\": \"card\"}, html.p(\"placeholder\"))\n\n\ndef card(body_text):\n    model = deepcopy(TEMPLATE)\n    model[\"children\"][0][\"children\"] = [body_text]\n    return model\n

deepcopy preserves the VdomDict class and its key guard, and leaves the original intact \u2014 verified. It is the right tool for a template you parse or build once at module level; it is not a licence to mutate a model that has already been rendered.

Deep-copying a model with handlers duplicates the handler objects

deepcopy succeeds on a model that has eventHandlers. It builds a new EventHandler but shares the wrapped function, so both copies still invoke the same Python code. That is fine for a fork you intend to render; it is not a way to hand two elements independent handlers \u2014 build those separately.

"},{"location":"learn/vdom-mutations/#transform-at-parse-time","title":"Transform at parse time","text":"

For HTML that arrives as a string, pass *transforms to string_to_reactpy() and edit nodes before anyone has a reference to them \u2014 see Convert Between VDOM and HTML.

"},{"location":"learn/vdom-mutations/#turn-the-check-on-while-developing","title":"Turn the check on while developing","text":"

REACTPY_CHECK_VDOM_SPEC validates each rendered model against the JSON schema in core/vdom.py \u2014 but only catches structural damage (a child that is not a VDOM dict, a missing tagName), and it is off unless REACTPY_DEBUG is on:

from reactpy import config\n\nconfig.REACTPY_DEBUG.current = True   # parents REACTPY_CHECK_VDOM_SPEC on\n

It will not catch a className you changed to the wrong string, or a text node you rewrote \u2014 those are valid VDOM. Treat it as a schema check, not a mutation detector.

"},{"location":"learn/vdom-mutations/#what-you-may-still-touch-the-dom-briefly","title":"What you may still touch: the DOM, briefly","text":"

Nothing here applies to the browser's DOM, which ReactPy does not share with you. The one sanctioned route is an inline-JavaScript handler, and the same discipline applies there \u2014 do not mutate nodes ReactPy owns, since the next patch against a node you removed breaks the diff. That is covered in Manipulating the DOM with Refs.

"},{"location":"learn/vdom-mutations/#summary","title":"Summary","text":"
  • A model is yours until it is returned from a render; after that the layout holds the same objects.
  • VdomDict guards its six top-level keys \u2014 key belongs inside attributes, and there is no ref.
  • Constructors copy the top level but share nested values, so a child element you built is the object the parent holds.
  • Mutating a rendered element sends nothing; the change surfaces on the next unrelated render, which makes it look like a timing bug.
  • A module-level element is shared by every connection, so one user's mutation appears in everyone's page. Build elements inside components.
  • Mutate-then-set-the-same-object is dropped by use_state's equality check, with no error.
  • Prefer building fresh; use deepcopy for a real fork; use parse-time transforms for parsed HTML.
"},{"location":"learn/vdom-mutations/#related","title":"Related","text":"
  • Updating Objects in State and Updating Arrays in State \u2014 the state-side version of the same hazard
  • Creating HTML Tags \u2014 what the constructors do to your arguments
  • Convert Between VDOM and HTML \u2014 where deepcopy-and-edit is the natural pattern
  • Preserving and Resetting State \u2014 how identity actually drives re-mounting
  • Protocol Structure \u2014 why the server keeps holding on to those dictionaries
"},{"location":"learn/writing-markup-with-psx/","title":"Writing markup with psx","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

See this issue for more details.

"},{"location":"learn/you-might-not-need-an-effect/","title":"You Might Not Need an Effect","text":""},{"location":"learn/you-might-not-need-an-effect/#overview","title":"Overview","text":"

Most Effects are unnecessary. If your logic only adjusts one thing based on another thing you already have, it belongs in the render \u2014 not in an Effect. This page walks through the common cases where an Effect looks right and isn't, with the render counts each one costs.

You will learn

  • How to recognize an Effect that is doing a render's job
  • What to write instead, case by case
  • Why ReactPy cannot use React's \"set state during render\" escape hatch
  • Which Effects genuinely are necessary
"},{"location":"learn/you-might-not-need-an-effect/#how-to-remove-unnecessary-effects","title":"How to remove unnecessary Effects","text":"

Work through each Effect in your code and ask: does this code need to run after a render, or could it run while rendering?

An Effect is genuinely necessary when the component must reach something outside ReactPy \u2014 subscribing to a queue, opening a socket, starting a task. Everything below is the opposite case: the \"external system\" is a value your component already has.

If you're not sure what Effects are for in the first place, start with Synchronizing with Effects.

"},{"location":"learn/you-might-not-need-an-effect/#updating-state-based-on-props-or-state","title":"Updating state based on props or state","text":"

A component shows a name built from two fields. The tempting version stores the combination and keeps it in step with an Effect:

app.py Run
@component\ndef form():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n    # Wrong: this value is not state, it is a calculation of other state.\n    full_name, set_full_name = use_state(\"\")\n\n    def effect():\n        set_full_name(f\"{first_name} {last_name}\")\n\n    use_effect(effect, dependencies=[first_name, last_name])\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.h1(full_name or \"(nothing yet)\"),\n        html.input(\n            {\n                \"value\": first_name,\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": last_name,\n                \"onChange\": handle_last_name_change,\n            }\n        ),\n    )\n
# TODO\n

Remove the state variable and the Effect, and calculate it during render:

app.py Run
@component\ndef form():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n    # Right: calculated during rendering, so it is always in sync.\n    full_name = f\"{first_name} {last_name}\"\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.h1(full_name),\n        html.input(\n            {\n                \"value\": first_name,\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.input(\n            {\n                \"value\": last_name,\n                \"onChange\": handle_last_name_change,\n            }\n        ),\n    )\n
# TODO\n

The difference is measurable, and the first row of each is the part users actually see:

First paint Renders for two keystrokes Effect + stored state \"(nothing yet)\" 6 Derived during render \"Ada Lovelace\" 3

Verified in a Layout. The Effect version renders once with an empty name, sets state from the Effect, renders again \u2014 every keystroke costs two renders instead of one, and the very first paint is wrong. Nothing downstream can rescue that first frame, because it is a real render that a real client receives.

When something can be calculated from existing props or state, don't put it in state. Calculate it during render. Fewer state variables means less code, no way for two of them to drift apart, and half the renders. If this feels unfamiliar, Thinking in React covers what belongs in state.

How expensive is too expensive?

If you're not sure whether a calculation is costly enough to worry about, write it without caching first, then measure it: time the component body, or count how often it runs. In practice the work that is genuinely slow is usually obvious \u2014 parsing, sorting or filtering thousands of rows, regex over a large string, cryptographic hashes. String concatenation, len(), and a loop over a page's worth of items are not worth a cache, and a cache costs more than it saves when its dependency list is wrong.

"},{"location":"learn/you-might-not-need-an-effect/#caching-expensive-calculations","title":"Caching expensive calculations","text":"

When a calculation genuinely is expensive, the answer is still not state plus an Effect \u2014 it is use_memo, which caches a value without adding a render:

app.py Run
@component\ndef todo_list():\n    filter_name, set_filter_name = use_state(\"all\")\n    new_todo, set_new_todo = use_state(\"\")\n\n    def visible_todos():\n        # A real filter over a real dataset. It records every run so the\n        # effect of use_memo is countable.\n        filter_runs.append(filter_name)\n        if filter_name == \"all\":\n            return todos\n        if filter_name == \"active\":\n            return [todo for todo in todos if not todo[\"completed\"]]\n        return [todo for todo in todos if todo[\"completed\"]]\n\n    shown = use_memo(visible_todos)\n\n    async def handle_filter_name_click(event):\n        set_filter_name(\n            {\"all\": \"active\", \"active\": \"completed\", \"completed\": \"all\"}[filter_name]\n        )\n\n    async def handle_new_todo_change(event):\n        set_new_todo(await event.target.value)\n\n    return html.div(\n        html.p(f\"{len(shown)} of {len(todos)} shown (filter: {filter_name})\"),\n        html.ul([html.li({\"key\": todo[\"id\"]}, todo[\"text\"]) for todo in shown]),\n        html.button(\n            {\"onClick\": handle_filter_name_click},\n            \"Change filter\",\n        ),\n        html.input(\n            {\n                \"placeholder\": \"new todo\",\n                \"value\": new_todo,\n                \"onChange\": handle_new_todo_change,\n            }\n        ),\n    )\n
# TODO\n

The filter records each run so the result is countable. Verified: after two keystrokes the component has rendered three times and the filter has run once; pressing Change filter adds a render and a second run. The value is recomputed exactly when the inputs it reads change, and never on a render that didn't change them.

Two properties worth knowing, both verified:

  • use_memo infers its dependencies the same way use_effect does \u2014 from the names the function reads. Pass dependencies=[] to pin a value forever, or dependencies=None to recompute on every render. See Use Memo.
  • It caches the value, not the render. The component body still runs on every render; ReactPy has no component-level memo, so a child re-runs whenever its parent does. use_memo saves the computation, never the render \u2014 see Render and Commit.

Mutating a value in place and \"setting\" it back defeats use_memo too, for the same reason it defeats use_state: the new value compares equal to the old one. Build a new object instead (Updating Arrays in State).

"},{"location":"learn/you-might-not-need-an-effect/#resetting-all-state-when-a-prop-changes","title":"Resetting all state when a prop changes","text":"

A profile page holds a comment draft. When the user navigates to a different profile, the draft should not follow them:

app.py Run
@component\ndef profile(user_id):\n    # Any state declared here is thrown away whenever the key changes, along\n    # with the state of every component below it. No Effect needed.\n    comment, set_comment = use_state(\"\")\n\n    async def handle_comment_change(event):\n        set_comment(await event.target.value)\n\n    return html.div(\n        html.h2(f\"profile {user_id}\"),\n        html.textarea(\n            {\n                \"value\": comment,\n                \"placeholder\": \"leave a comment\",\n                \"onChange\": handle_comment_change,\n            }\n        ),\n        html.p(f\"draft: {comment or '(empty)'}\"),\n    )\n\n\n@component\ndef profile_page():\n    user_id, set_user_id = use_state(\"ada\")\n\n    async def handle_user_id_click(event):\n        set_user_id(\"grace\" if user_id == \"ada\" else \"ada\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_user_id_click},\n            \"Next profile\",\n        ),\n        # The key is what makes ReactPy treat this as a different profile.\n        profile(user_id, key=user_id),\n    )\n
# TODO\n

Verified: type a draft, press Next profile, and the draft is empty on the new profile \u2014 in a single extra render.

Passing key=user_id tells ReactPy that a different user_id means a different component instance. It tears down profile() and everything below it and builds them again, so all of their state starts fresh. One line, and it covers every piece of state in the whole subtree \u2014 no Effect, and no remembering to add one to each nested component.

The Effect version (set_comment(\"\") in an Effect depending on user_id) is worse in three ways: the subtree renders once with the previous user's draft still in it, every nested component needs its own copy, and a piece of state added later silently fails to reset. See Preserving and Resetting State for the full mechanism.

key is spelled differently for elements and components

Here it is a keyword argument to the component call: profile(user_id, key=user_id). On an element it goes in the attribute dict instead: html.li({\"key\": 1}, ...). Put it in the wrong place and you get TypeError: Vdom.__call__() got an unexpected keyword argument 'key', or a literal key=\"...\" attribute in the page.

"},{"location":"learn/you-might-not-need-an-effect/#adjusting-some-state-when-a-prop-changes","title":"Adjusting some state when a prop changes","text":"

Sometimes you don't want to throw state away \u2014 just invalidate part of it when an input changes. A list keeps a selection, and a new items prop should clear it. React's advice is to call the setter during render, and that escape hatch does not work in ReactPy. Measured, the render-phase-setter version takes four renders and the client receives this sequence:

<root>                     items: [a,b,c]     selected: None\n/children/0/children/1     items: [a,b,c]     selected: a\n<root>                     items: [a,b,c,i4]  selected: a     <-- stale, and delivered\n/children/0/children/1     items: [a,b,c,i4]  selected: None\n

React throws the discarded render away; ReactPy hands the model to the socket as soon as it exists, so the stale frame ships. Use a derived read instead. Store the value alongside what it belonged to, and let the read decide whether it is still valid:

app.py Run
@component\ndef list_box(items):\n    is_reverse, set_is_reverse = use_state(False)\n    # One state value that remembers which list the choice belonged to. The\n    # selection is *derived*: if the items changed, the old choice is not\n    # valid for the new list, so it reads back as None. No Effect, no\n    # intermediate render with a stale selection.\n    chosen, set_chosen = use_state({\"items\": None, \"value\": None})\n    selection = chosen[\"value\"] if chosen[\"items\"] == items else None\n\n    shown = items[::-1] if is_reverse else items\n\n    async def handle_is_reverse_click(event):\n        set_is_reverse(not is_reverse)\n\n    async def handle_chosen_click(event):\n        set_chosen({\"items\": items, \"value\": shown[0]})\n\n    return html.div(\n        html.p(f\"items: {shown}  selected: {selection}\"),\n        html.button({\"onClick\": handle_is_reverse_click}, \"Reverse\"),\n        html.button(\n            {\"onClick\": handle_chosen_click},\n            \"Select\",\n        ),\n    )\n\n\n@component\ndef app():\n    items, set_items = use_state([\"a\", \"b\", \"c\"])\n\n    async def handle_items_click(event):\n        set_items([*items, f\"item {len(items) + 1}\"])\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_items_click},\n            \"Add item\",\n        ),\n        list_box(items),\n    )\n
# TODO\n

Verified: Select costs one render and shows the selection; Add item costs one render and the selection is already gone in that same patch \u2014 three renders total, no stale frame.

The general shape is a guard on the read rather than a call to the setter:

is_valid = chosen[\"items\"] == items\nselection = chosen[\"value\"] if is_valid else None\n

Do not set state from an Effect that depends on it

The reflexive alternative \u2014 an Effect calling set_selection(None) with dependencies=[items] \u2014 is not merely slow. When the value it sets is one it also reads, it is a loop, and ReactPy has no detector for one. Measured: an Effect that set a counter it depended on rendered 86 times before the probe gave up. If a component visibly spins, read what the Effect awaits, what it depends on, and what it sets.

"},{"location":"learn/you-might-not-need-an-effect/#sharing-logic-between-event-handlers","title":"Sharing logic between event handlers","text":"

Two handlers should do the same thing on input. There is no reason for this to involve an Effect:

app.py Run
@component\ndef shipping_form():\n    shipping_city, set_shipping_city = use_state(\"\")\n    billing_city, set_billing_city = use_state(\"\")\n\n    async def handle_shipping_change(event):\n        city = await event.target.value\n        set_shipping_city(city)\n        # Shared logic: just call the other setter. Handlers are ordinary\n        # functions; there is nothing to schedule and nothing to re-sync.\n        set_billing_city(city)\n\n    async def handle_billing_city_change(event):\n        set_billing_city(await event.target.value)\n\n    return html.div(\n        html.p(f\"shipping: {shipping_city or '(empty)'}\"),\n        html.p(f\"billing:  {billing_city or '(empty)'}\"),\n        html.input(\n            {\"value\": shipping_city, \"onChange\": handle_shipping_change},\n        ),\n        html.input(\n            {\n                \"value\": billing_city,\n                \"onChange\": handle_billing_city_change,\n            }\n        ),\n    )\n
# TODO\n

Verified: type in the shipping field and the billing readout changes with it, in one render, because both setters were called from one handler.

Event handlers are ordinary functions \u2014 ordinary async defs, which is to say the coroutine ReactPy requires of anything in a handler slot. Call one from the other (with await, since it is a coroutine), extract the shared part into a plain function, or pass the other setter as a parameter. Whatever React's docs tell you here transfers unchanged, because none of it is rendering work.

"},{"location":"learn/you-might-not-need-an-effect/#sending-a-post-request","title":"Sending a POST request","text":"

Submitting a form is an event, not a consequence of rendering. Do the work in the handler:

app.py Run
@component\ndef signup():\n    email, set_email = use_state(\"\")\n    status, set_status = use_state(\"idle\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        # Wrong: set a \"pending\" flag and let an Effect notice it. That costs\n        # a state variable, a render, and a way to submit the same value twice.\n        #     set_pending(True)\n        #\n        # Right: do the work from the handler. It runs once per submission.\n        set_status(\"sending...\")\n        await send_email(email)\n        set_status(f\"sent to {email}\")\n\n    async def handle_email_change(event):\n        set_email(await event.target.value)\n\n    return html.div(\n        html.p(status),\n        html.form(\n            {\"onSubmit\": handle_submit},\n            html.input(\n                {\n                    \"value\": email,\n                    \"placeholder\": \"you@example.com\",\n                    \"onChange\": handle_email_change,\n                }\n            ),\n            html.button({\"type\": \"submit\"}, \"Sign up\"),\n        ),\n    )\n
# TODO\n

Verified: fill the field, submit, and the transport recorded exactly one send \u2014 ['ada@example.com'].

The version to avoid is a flag plus an Effect: set_pending(True), and an Effect that watches pending. It costs a state variable, an extra render per submission, and \u2014 because Effects re-run on dependency changes and remounts \u2014 a way to send twice. event.preventDefault() stops the page navigating, and must appear literally in the handler's body, since ReactPy detects it by bytecode inspection (Creating VDOM Event Handlers).

"},{"location":"learn/you-might-not-need-an-effect/#notifying-parent-components-about-state-changes","title":"Notifying parent components about state changes","text":"

A child should tell its parent something happened. Pass a callback and call it from the child's event handler \u2014 this works exactly as it does in React:

app.py Run
@component\ndef search_box(on_search):\n    query, set_query = use_state(\"\")\n\n    async def handle_submit(event):\n        event.preventDefault()\n        # Not an Effect: the search happens because the user submitted.\n        on_search(query)\n\n    async def handle_query_change(event):\n        set_query(await event.target.value)\n\n    return html.form(\n        {\"onSubmit\": handle_submit},\n        html.input(\n            {\n                \"value\": query,\n                \"placeholder\": \"search\",\n                \"onChange\": handle_query_change,\n            }\n        ),\n        html.button({\"type\": \"submit\"}, \"Search\"),\n    )\n\n\n@component\ndef app():\n    results, set_results = use_state(\"Type something and submit.\")\n\n    def handle_search(query):\n        set_results(f\"searching for {query!r}...\" if query else \"empty query\")\n\n    return html.div(html.p(results), search_box(handle_search))\n
# TODO\n

Verified: type ada, submit, and the parent's paragraph reads searching for 'ada'....

What you must not do is call the parent's function from an Effect that watches a value. Handlers run once per user action; Effects run once per dependency change, which includes renders the user never caused. There is also no use_imperative_handle for the reverse direction \u2014 pass data down as props and callbacks up as arguments (Sharing State Between Components).

"},{"location":"learn/you-might-not-need-an-effect/#chains-of-computations","title":"Chains of computations","text":"

When several values feed each other, keep the whole chain in render. Each intermediate is a local variable, not a state variable:

@component\ndef report(rows, currency):\n    priced = [(*row, row[1] * RATES[currency]) for row in rows]\n    total = sum(item[-1] for item in priced)\n    heading = f\"{currency.upper()}: {total:,.2f}\"\n    return html.div(html.h1(heading))\n

An Effect at any link in that chain means every upstream change costs an extra full render \u2014 and, as above, each of those renders is visible to the client. If one link is genuinely expensive, wrap that one in use_memo rather than putting the whole chain in state.

"},{"location":"learn/you-might-not-need-an-effect/#initializing-the-application","title":"Initializing the application","text":"

Code that should run once per process does not belong in a component at all \u2014 a component's Effect runs once per connection, and a server serves many connections:

# module level, or the ASGI app's startup: runs once\nconfigure_logging()\nPOOL = create_pool()\n

Keep a dependencies=[] Effect for work that is genuinely per-connection, such as a subscription or a session-scoped task. Never guard a process-level side effect with my_component.started = True: the function object is module-scope, so the first visitor flips the flag for everyone (VDOM Mutations).

"},{"location":"learn/you-might-not-need-an-effect/#subscribing-to-an-external-store","title":"Subscribing to an external store","text":"

Here is the case where the Effect is correct. The value does not live in ReactPy at all, so there is nothing to derive it from \u2014 ReactPy has to be told when it changes:

app.py Run
@component\ndef clock_view():\n    # The value is owned by `clock`, not by this component, so it cannot be\n    # derived during render -- ReactPy has to be told when it changes.\n    ticks, set_ticks = use_state(clock.get())\n\n    def effect():\n        # Subscribe, and hand back the unsubscribe. This is what an Effect\n        # is for: the source of truth lives outside ReactPy.\n        def on_change():\n            set_ticks(clock.get())\n\n        return clock.subscribe(on_change)\n\n    use_effect(effect, dependencies=[])\n\n    return html.div(html.h1(f\"{ticks} ticks\"))\n\n\n@component\ndef app():\n    watching, set_watching = use_state(True)\n\n    async def handle_click(event):\n        clock.tick()\n\n    async def handle_watching_click(event):\n        set_watching(not watching)\n\n    return html.div(\n        html.button({\"onClick\": handle_click}, \"Tick\"),\n        html.button(\n            {\"onClick\": handle_watching_click},\n            \"Stop watching\" if watching else \"Watch\",\n        ),\n        *([] if not watching else [clock_view()]),\n    )\n
# TODO\n

Verified: pressing Tick once shows 1 ticks; pressing Stop watching unmounts the view and leaves 0 listeners registered on the store.

Three things make this one legitimate where the others were not:

  1. The source of truth is outside ReactPy. Deriving would mean reading clock.get() during render, which has no way to know when to run again.
  2. The subscription needs a lifetime, and the clean-up is what returns the store to zero listeners.
  3. The state exists to trigger a render \u2014 that is its whole purpose.

This is also where use_async_effect earns its place, when the external system is an async stream rather than a callback. See Synchronizing with Effects for cancellation and the four-way dependency table.

"},{"location":"learn/you-might-not-need-an-effect/#fetching-data-without-the-extra-effects","title":"Fetching data, without the extra Effects","text":"

Fetching genuinely needs an Effect \u2014 the network is external. Everything you would otherwise stack on top of it does not. Store the response, and shape it in render:

app.py Run
@component\ndef report_view(name):\n    # Store the response *with the name it belongs to*, so a render for a new\n    # report cannot display the old one's rows. The empty read is the\n    # \"loading\" state -- derived, not a second state variable.\n    loaded, set_loaded = use_state({\"name\": None, \"data\": {}})\n\n    async def load():\n        data = await catalog.fetch(name)\n        set_loaded({\"name\": name, \"data\": data})\n\n    use_async_effect(load, dependencies=[name])\n\n    raw = loaded[\"data\"] if loaded[\"name\"] == name else {}\n\n    if not raw:\n        return html.p(\"loading...\")\n\n    # Right: the shape the page needs is computed here, from the data that\n    # arrived. There is no second state variable to keep in step, so there is\n    # nothing for an Effect to synchronise.\n    rows = [{\"label\": key.title(), \"value\": value} for key, value in raw.items()]\n    total = sum(row[\"value\"] for row in rows)\n    lines = [\n        html.li({\"key\": row[\"label\"]}, f\"{row['label']}: {row['value']}\")\n        for row in rows\n    ]\n\n    return html.div(\n        html.h2(f\"{name}: {total} total\"),\n        html.ul(lines),\n    )\n\n\n@component\ndef app():\n    name, set_name = use_state(\"orders\")\n\n    async def handle_name_click(event):\n        set_name(\"users\" if name == \"orders\" else \"orders\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_name_click},\n            \"Toggle report\",\n        ),\n        # No `key` here on purpose: the derived read in report_view is what\n        # keeps the old report off the screen, so nothing has to be remounted.\n        report_view(name),\n    )\n
# TODO\n

Verified: the Effect stores the raw response; the labels, the per-row formatting, and the total are computed during render. One request per report name (['orders', 'users']), and no rows or total state variables that could fall out of step with the response.

The example carries one more lesson in its state shape. It stores the response together with the name that produced it and derives the read:

raw = loaded[\"data\"] if loaded[\"name\"] == name else {}\n

Storing the response alone looks fine and is wrong: switch from orders to users and the component re-renders immediately, before the new response arrives, so the client receives users: 55 total above the orders rows \u2014 55 being orders' total. Measured, and the derived read above is what fixes it, without remounting the component.

The anti-pattern is a second Effect that watches the response and sets three more state variables from it. Each of those is a value you can calculate, and each adds a render in which the page is half-updated.

Because ReactPy already runs on the server, there is usually a better option than fetching in the child at all: fetch in a parent during render, or at startup, and pass the result down as a prop. Then the child has no Effect and no loading state, and the response is shared rather than re-requested. Keep the Effect for data that depends on something only the connection knows, like use_location.

"},{"location":"learn/you-might-not-need-an-effect/#how-to-tell-whether-an-effect-is-necessary","title":"How to tell whether an Effect is necessary","text":"The Effect... Verdict reads state or props and sets other state unnecessary \u2014 compute it during render caches a calculation unnecessary \u2014 use_memo resets state when a prop changes unnecessary \u2014 pass a key invalidates state when a prop changes unnecessary \u2014 derive the read from what it belonged to runs logic shared by two handlers unnecessary \u2014 call a function reacts to a user action unnecessary \u2014 put it in the event handler calls a parent's callback when a value changes unnecessary \u2014 call it from the handler subscribes to a queue, socket, or store necessary, with a clean-up starts async work that outlives the render necessary, with use_async_effect starts per-connection work on mount necessary, with dependencies=[]

If you cannot name what the Effect is synchronizing with, it is probably doing a render's job.

Two failure modes that cost hours

  • An Effect that sets state it depends on loops silently. ReactPy has no loop detector. Measured: 86 renders and counting before the probe stopped.
  • Hooks do not work inside an effect body. use_context, use_state, and friends all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 effects run in their own task, with no hook on the stack. Read what you need during render and let the closure carry it in. The traceback appears only in the server log; the page just goes quiet.
"},{"location":"learn/you-might-not-need-an-effect/#recap","title":"Recap","text":"
  • An Effect runs after a render. If the work could happen during the render, it should.
  • Values calculated from props or state are not state. Compute them; do not store and re-sync them. Measured: the Effect version costs twice the renders and ships a wrong first frame.
  • use_memo caches expensive calculations without adding a render \u2014 but it caches values, not component renders.
  • Reset state with a key, not with an Effect.
  • ReactPy cannot use React's render-phase setter trick: the stale patch reaches the client. Derive the value instead.
  • Sharing logic between handlers, notifying a parent, and submitting a form are all handler work.
  • Subscriptions and async streams are the real use for Effects \u2014 with a clean-up, always.
"},{"location":"learn/you-might-not-need-an-effect/#related","title":"Related","text":"
  • Synchronizing with Effects \u2014 how Effects work, and their four-way dependency table
  • Choosing the State Structure \u2014 avoid redundant state in the first place
  • Preserving and Resetting State \u2014 resetting with key
  • Use Memo and Use Callback
  • Separating Events from Effects \u2014 the remaining case where reading fresh state matters
"},{"location":"learn/your-first-component/","title":"Your First Component","text":""},{"location":"learn/your-first-component/#overview","title":"Overview","text":"

Components are one of the core concepts of ReactPy. They are the foundation upon which you build user interfaces (UI), which makes them the perfect place to start your ReactPy journey!

You will learn

  • What a component is
  • What role components play in a ReactPy application
  • How to write your first ReactPy component
"},{"location":"learn/your-first-component/#components-ui-building-blocks","title":"Components: UI building blocks","text":"

On the Web, HTML lets us create rich structured documents with its built-in set of tags like <h1> and <li>:

<article>\n    <h1>My First Component</h1>\n    <ol>\n        <li>Components: UI Building Blocks</li>\n        <li>Defining a Component</li>\n        <li>Using a Component</li>\n    </ol>\n</article>\n

This markup represents this article <article>, its heading <h1>, and an (abbreviated) table of contents as an ordered list <ol>. Markup like this, combined with CSS for style, and Python for interactivity, lies behind every sidebar, avatar, modal, dropdown\u2014every piece of UI you see on the Web.

ReactPy lets you combine your markup, CSS, and Python into custom \"components\", reusable UI elements for your app. The table of contents code you saw above could be turned into a table_of_contents() component you could render on every page. Under the hood, it still uses the same HTML tags like html.article, html.h1, etc.

Just like with HTML tags, you can compose, order and nest components to design whole pages. For example, the documentation page you're reading is made out of ReactPy components:

html.div(\n    html.header(\n        search_bar(),\n        html.a({\"href\": \"/docs\"}, \"Docs\"),\n    ),\n    sidebar(),\n    html.main(\n        table_of_contents(),\n        documentation_text(),\n    ),\n)\n

As your project grows, you will notice that many of your designs can be composed by reusing components you already wrote, speeding up your development. Our table of contents above could be added to any screen with table_of_contents()! You can even jumpstart your project with components shared by the community.

"},{"location":"learn/your-first-component/#defining-a-component","title":"Defining a component","text":"

Traditionally when creating web pages, web developers marked up their content and then added interaction by sprinkling on some JavaScript. This worked great when interaction was a nice-to-have on the web. Now it is expected for many sites and all apps. ReactPy puts interactivity first while still using the same technology: a ReactPy component is a Python function that you can sprinkle with markup. Here's what that looks like:

app.pystyles.css Run
@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3Am.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n
img {\n    height: 200px;\n}\n
# TODO\n

And here's how to build a component:

"},{"location":"learn/your-first-component/#step-1-import-what-you-need","title":"Step 1: Import what you need","text":"

Every component needs the @component decorator and the html tag constructor:

from reactpy import component, html\n

Unlike ReactJS, there is no export statement. To use a component from another file you import it like any other Python object, which you will learn about in Importing and Exporting Components.

"},{"location":"learn/your-first-component/#step-2-define-the-function","title":"Step 2: Define the function","text":"

With def profile(): you define a Python function with the name profile. The @component decorator turns that function into something ReactPy can render.

Pitfall

ReactPy components are ordinary Python functions, so follow Python's naming conventions and write them in snake_case.

The ReactJS rule that \"component names must start with a capital letter\" does not apply. ReactJS needs that rule to tell components apart from HTML tags in its markup syntax. ReactPy has no markup syntax: HTML tags are reached through html. and components are called directly, so the difference is already unambiguous.

"},{"location":"learn/your-first-component/#step-3-add-markup","title":"Step 3: Add markup","text":"

The component returns an html.img element with src and alt attributes. There is no JSX or PSX in ReactPy \u2014 a tag is a normal Python call whose first argument is a dictionary of attributes and whose remaining arguments are its children.

Return statements can be written all on one line when the element is short:

return html.h1(\"Amazing scientists\")\n

For anything longer, wrap the element in parentheses rather than relying on a backslash continuation:

return html.img(\n    {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n)\n
"},{"location":"learn/your-first-component/#using-a-component","title":"Using a component","text":"

Now that you've defined your profile component, you can nest it inside other components. For example, you can define a gallery component that uses multiple profile components:

app.pystyles.css Run
@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n
img {\n    margin: 0 10px 10px 0;\n    height: 90px;\n}\n
# TODO\n
"},{"location":"learn/your-first-component/#what-the-browser-sees","title":"What the browser sees","text":"

Notice the difference in how the two kinds of calls are made:

  • html.section(...) and html.h1(...) are attributes on the html object, so ReactPy knows they are HTML tags.
  • profile() is a function you wrote and decorated with @component, so ReactPy knows it is a component.

And profile contains even more HTML: html.img. In the end, this is what the browser sees:

<section>\n    <h1>Amazing scientists</h1>\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n</section>\n
"},{"location":"learn/your-first-component/#nesting-and-organizing-components","title":"Nesting and organizing components","text":"

Components are regular Python functions, so you can keep multiple components in the same file. This is convenient when components are relatively small or tightly related to each other. If this file gets crowded, you can always move profile to a separate file. You will learn how to do this shortly on the page about imports.

Because the profile components are rendered inside gallery\u2014even several times!\u2014we can say that gallery is a parent component, rendering each profile as a \"child\". This is part of the magic of ReactPy: you can define a component once, and then use it in as many places and as many times as you like.

Pitfall

Components can render other components, but you must never nest their definitions:

from reactpy import component, html\n\n\n# Do not do this: a component defined inside another component.\n@component\ndef gallery():\n    @component\n    def profile():\n        return html.img({\"src\": \"https://i.imgur.com/MK3eW3As.jpg\"})\n\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n

Instead, define every component at the top level of your module:

@component\ndef profile():\n    return html.img(\n        {\"src\": \"https://i.imgur.com/MK3eW3As.jpg\", \"alt\": \"Katherine Johnson\"}\n    )\n\n\n@component\ndef gallery():\n    return html.section(\n        html.h1(\"Amazing scientists\"),\n        profile(),\n        profile(),\n        profile(),\n    )\n

Here is why. When ReactPy re-renders gallery, it matches each child against the child from the previous render by the identity of its function. A component defined inside another function is a brand new function object every time the parent runs, so ReactPy concludes the old child is gone: it unmounts the previous profile, throws away its state and Effects, and mounts a fresh one. Rendering still looks correct, which makes this bug easy to miss and hard to trace.

When a child component needs some data from a parent, pass it by props instead of nesting definitions.

Deep Dive

"},{"location":"learn/your-first-component/#components-all-the-way-down","title":"Components all the way down","text":"

Your ReactPy application begins at a \"root\" component. This is the component you hand to an executor \u2014 for example the hello_world component passed to ReactPy in Creating a Standalone React App.

Most ReactPy apps use components all the way down. This means that you won't only use components for reusable pieces like buttons, but also for larger pieces like sidebars, lists, and ultimately, complete pages! Components are a handy way to organize UI code and markup, even if some of them are only used once.

Because your components run on the server, ReactPy can also render them into the initial HTML response instead of waiting for a browser to do the work. See Add React to an Existing Project to render components inside pages you already have.

Still, many websites only use ReactPy to add interactivity to existing HTML pages. They have many root components instead of a single one for the entire page. You can use as much\u2014or as little\u2014ReactPy as you need.

"},{"location":"learn/your-first-component/#recap","title":"Recap","text":"

You've just gotten your first taste of ReactPy! Let's recap some key points.

  • ReactPy lets you create components, reusable UI elements for your app.
  • In a ReactPy app, every piece of UI is a component.
  • ReactPy components are regular Python functions except:

    1. They are decorated with @component.
    2. They return elements built with html.
  • Attributes are a dict, children are the remaining arguments, and both are ordinary Python \u2014 which means for loops, if statements, and variables work exactly as you would expect.

"},{"location":"learn/your-first-component/#next-steps","title":"Next Steps","text":"

Check out Importing and Exporting Components to organize components across files, or jump straight to Passing Props to a Component to make yours configurable.

"},{"location":"reference/client-api/","title":"Client API","text":""},{"location":"reference/client-api/#overview","title":"Overview","text":"

mountReactPy is the browser-side entry point: one exported function, one websocket, no build step. Everything else in the browser is the server's business.

import { mountReactPy } from \"/reactpy/static/index.js\";\n\nmountReactPy({\n  mountElement: document.getElementById(\"app\"),\n  pathPrefix: \"/reactpy/\",\n  componentPath: \"main.counter/\",\n});\n

The executors emit this call for you \u2014 see ReactPy and ReactPyMiddleware. You need to write it yourself only when you serve the document from your own route, or when the defaults do not fit.

"},{"location":"reference/client-api/#reference","title":"Reference","text":""},{"location":"reference/client-api/#mountreactpyoptions","title":"mountReactPy(options)","text":"
import { mountReactPy } from \"{pathPrefix}static/index.js\";\n

The bundle is served by ReactPy itself, under {pathPrefix}static/, alongside two chunks it imports by relative path. There is nothing to install and nothing to build: it is preact and the client, pre-bundled.

"},{"location":"reference/client-api/#parameters","title":"Parameters","text":"

A single object.

  • mountElement: The DOM node to render into. Not optional in effect \u2014 passing null, which is what getElementById returns for a missing id, dies with TypeError: Cannot read properties of null (reading '__k') before anything is logged.

  • pathPrefix: string. Must start and end with /. Concatenated verbatim with componentPath, so a missing slash on either side produces a URL that is wrong rather than invalid \u2014 which is worse, because nothing rejects it. A value with no leading slash (\"reactpy/\") or no pathPrefix at all may fail early with TypeError: Failed to construct 'URL': Invalid URL, but only when the page's host includes a port. See The URL is built by concatenation.

  • componentPath: string, optional. The dotted path of the root component, with a trailing slash: \"main.counter/\". ReactPyMiddleware requires it. ReactPy takes the opposite view and requires it be omitted \u2014 see The executors route differently.

  • reconnectInterval: number of ms. 750.

  • reconnectMaxInterval: number of ms. 60000.

  • reconnectMaxRetries: number. 150.

  • reconnectBackoffMultiplier: number. 1.25.

"},{"location":"reference/client-api/#returns","title":"Returns","text":"

Nothing.

There is no unmountReactPy

The bundle exports exactly one name. Verified by importing it and listing the keys: mountReactPy, nothing else. mountReactPy returns nothing and takes no callback, so you get no handle to keep and no supported way to stop a component.

Removing the mount element from the DOM does not stop it either. The subtree is detached, not unmounted: mountReactPy calls preact's render(...) into the node and never the matching unmount. Verified in a browser \u2014 after removing the node, a click dispatched into the detached subtree still reached the server and incremented its state, and the websocket stayed OPEN in both directions. The component keeps reconciling into an orphan nobody can see.

The only clean teardown is to navigate or reload. If you are building a single-page app that swaps components on the client, the closest available tool is onMessage's return value, which unsubscribes a handler \u2014 that stops the client applying updates, but leaves the socket and the server's layout alive.

Reconnect options reject zero, not just wrong types

The defaults are applied with JavaScript's ||, not ??, so any falsy value is replaced by the default. Passing reconnectMaxRetries: 0 to mean \"do not retry\" gets you the default 150 instead, and reconnectInterval: 0 gets you 750 ms.

To actually stop retrying, pass a negative reconnectMaxRetries. The retry test is retries >= maxRetries and negatives survive ||, so reconnectMaxRetries: -1 connects once and then gives up \u2014 measured at exactly one attempt, by running the client's own createReconnectingWebSocket against a stub socket.

The retry counter is also not what it appears to be. It counts consecutive failures, because a successful open resets it to zero along with the backoff interval. Measured against a stub that accepts each connection and closes it 2 ms later, reconnectMaxRetries: 2 with a 1 ms interval produced 342 attempts in 1.5 s: every reconnect succeeds, so the counter never reaches the limit. Only attempts that fail to open at all accumulate.

Which leads to the sharper edge: a handshake that is refused is never retried at all. If the socket never reaches OPEN, the close handler logs Failed to connect! and returns without scheduling anything. So ReactPyMiddleware answering 403 \u2014 a wrong componentPath, a wrong path_prefix \u2014 gives you one attempt, one line in the console, and a permanently empty element. Nothing recovers without a reload. Retries exist only for connections that opened and then dropped.

"},{"location":"reference/client-api/#what-mountreactpy-does","title":"What mountReactPy does","text":"

mountReactPy does five things (static/index.js):

  1. Picks the websocket scheme from the page: wss: under https:, otherwise ws:.
  2. Builds the URL as scheme // host + pathPrefix + componentPath, then appends path=window.location.pathname and, only if the page has a query string, qs=window.location.search.
  3. Sets jsModulesPath to origin + pathPrefix + \"modules/\", the base from which JavaScript modules named by an element's importSource are imported.
  4. Constructs the client, which wires up a reconnecting websocket.
  5. Renders the layout into mountElement.

Steps 2 and 3 are the only places the browser's own URL is read. Once the socket is open, the server's view of the URL is whatever those two parameters said \u2014 see Use Location.

"},{"location":"reference/client-api/#the-url-is-built-by-concatenation","title":"The URL is built by concatenation","text":"

Nothing inserts a slash for you, so both halves have to arrive correctly formed. The four ways to get it wrong, each verified from a page with the resulting socket URL (against ReactPyMiddleware on a host with a port):

pathPrefix componentPath Socket path Result \"/reactpy/\" \"main.counter/\" /reactpy/main.counter/ Connects. \"/reactpy\" \"main.counter/\" /reactpymain.counter/ 403, then Failed to connect! \"/reactpy/\" \"main.counter\" /reactpy/main.counter 403, then Failed to connect! \"/reactpy/\" omitted /reactpy/ 403, then Failed to connect!

ReactPyMiddleware matches the websocket path against ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$. Rows two through four fail that pattern, so they fall through to your own ASGI app, which answers 403.

Read the browser's own error line, not ReactPy's. The client emits a bare Failed to connect!; the URL that caused it is on the line above, in the browser's message, without the [ReactPy] prefix.

The || defaults and the URL constructor make this host-dependent

Two of these fail earlier than the table suggests, and only sometimes. new URL(...) throws TypeError: Failed to construct 'URL': Invalid URL for a missing or slashless pathPrefix \u2014 but only when the host carries a port, because ws://example.comreactpy/main.counter/ parses happily as host example.comreactpy. Verified with Node's URL, the same implementation Chrome uses:

Host pathPrefix Outcome 127.0.0.1:8842 \"\" TypeError: Invalid URL 127.0.0.1:8842 \"reactpy/\" TypeError: Invalid URL example.com \"\" Connects to host example.commain.counter example.com \"reactpy/\" Connects to host example.comreactpy

So the mistake that fails loudly in development fails silently in production, sending the socket to a hostname that does not exist. Check the host, not just the path.

"},{"location":"reference/client-api/#the-executors-route-differently","title":"The executors route differently","text":"

The last table row is not a universal truth, and this is where the two executors part company. ReactPy \u2014 the standalone app \u2014 overrides the matcher:

def match_dispatch_path(self, scope):\n    \"\"\"Method override to remove `dotted_path` from the dispatcher URL.\"\"\"\n    return str(scope[\"path\"]) == self.dispatcher_path\n

So the two want opposite things:

componentPath Unmatched dotted path ReactPyMiddleware required, with trailing slash Falls through to your app (with Starlette's websocket handler, a 403 handshake rejection) ReactPy must be omitted \u2014 the bare {pathPrefix} is the route socket error; the server logs NotImplementedError (see below)

Verified against both executors on the same build: the middleware accepts /reactpy/main.counter/ and refuses /reactpy/; the standalone app does exactly the reverse. A mount snippet copied from one executor to the other therefore produces a socket that never opens.

The standalone rejection does not read like a normal exception. executors/asgi/standalone.py, ReactPyApp.__call__ builds msg as a tuple of a %-format template and its arguments and raises NotImplementedError(msg) without ever formatting it, so the server logs it verbatim \u2014 the placeholders survive and the path is appended:

NotImplementedError: (\"ReactPy app received unsupported request of type '%s' at path '%s'\", 'websocket', '/reactpy//')\n

Do not search the log for ...type 'websocket'; search for unsupported request of type. The same tuple is written by _logger.warning immediately before the raise.

"},{"location":"reference/client-api/#the-socket-does-not-open-immediately","title":"The socket does not open immediately","text":"

Constructing the client does not connect. The websocket waits on a ready promise, and that promise is resolved by the first onMessage registration \u2014 nothing else in the codebase resolves it. The layout registers its handler from an effect, so the client connects only after that effect runs, which is why [ReactPy] Starting client... always precedes Connected! and never follows it.

Because onMessage is the sole resolver, a run where the layout's effect never fires produces a fourth console state: no Starting client..., no socket attempt, and no error. Treat that as \"the bundle ran but the component never mounted\" rather than a network problem \u2014 the other four messages below are all post-connect.

Two consequences of the gating:

  • Reconnection refreshes path and qs from the live page on every attempt, but never re-reads componentPath \u2014 that was baked into the URL at mount time.
  • Messages sent before the socket reports OPEN are buffered rather than dropped, and flushed on the next open. They survive a reconnect \u2014 but once retries are exhausted the buffer is never drained.

Note that path and qs are query parameters, not path segments. The full form is ws://127.0.0.1:8000/reactpy/main.counter/?path=%2F&qs=%3Ftab%3D1, and the server reads them with strict_parsing=True. When building a test client by hand, copy a real frame from the browser rather than assembling one from the tables on this page.

"},{"location":"reference/client-api/#the-import-map","title":"The import map","text":"

For a server-rendered component, no import map is needed. The bundle imports its own two chunks by relative path and has preact inlined, so nothing it loads statically is a bare specifier. Verified: a page with no map at all mounted, connected, and the server never received a single request for preact.js.

The map becomes load-bearing only when the model contains importSource and the resolved module does not export its own bind. Rendering such an element goes through loadImportSource, which uses the module's bind when it is a function and only otherwise calls infer_bind_from_environment() \u2014 the function that runs await import(\"react\") and await import(\"react-dom/client\"). Those are dynamic imports, which are exactly what an import map resolves. If they succeed, real React renders the subtree; if they fail, the client falls back to its own preact bindings and says so:

ReactPy will render JavaScript components using internal bindings for 'react'.\n

Note the level and the prefix: that line is console.debug with no [ReactPy] prefix, so it is invisible in a console filtered to warnings, and it is not caught by filtering on [ReactPy].

The executors' bootstrap injects a map only if none is present \u2014 and the check is keyed on one specific id:

if (!document.querySelector(\"#reactpy-importmap\")) { /* inject ReactPy's map */ }\n

The id is the trigger, not the content. Verified across three pages, all served through the same executor bootstrap:

Page ReactPy injects What you see No map Yes, as <script type=\"importmap\" id=\"reactpy-importmap\"> ReactPy did not detect a suitable JavaScript import map. Falling back to ReactPy's internal framework (Preact). Your own map, any other id Yes \u2014 a second map appears, alongside yours The same did not detect line, plus the browser's own An import map rule for specifier 'react' was removed, as it conflicted with an existing rule. Your own map with id=\"reactpy-importmap\" No Nothing at all.

The two messages have different authors, which matters when you search a minified bundle. The did not detect line is written by the Python side of the bootstrap \u2014 executors/utils.py, server_side_component_html emits it inside the inline <script> it generates, so it is in the served HTML, not in the bundle. The internal bindings for 'react' line comes from the client \u2014 infer_bind_from_environment in bind.tsx, reached from loadImportSource in vdom.tsx. Both are unprefixed console.debug, so neither shows up under a [ReactPy] filter.

To control resolution yourself, use that exact id. Two maps is worse than untidy: the browser keeps the first rule it sees for a specifier and discards the later one, so with two maps the winner is decided by document order \u2014 which is why the warning above names a rule being removed.

The map ReactPy writes maps all four React specifiers onto its own preact files under {pathPrefix}static/. Save the page and the app under those exact names \u2014 the dotted path in componentPath is a Python module path, and main.counter only resolves when this file is called main.py:

page.htmlmain.py Run
<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Counter</title>\n    <script type=\"importmap\" id=\"reactpy-importmap\">\n      {\n        \"imports\": {\n          \"react\": \"/reactpy/static/preact.js\",\n          \"react-dom\": \"/reactpy/static/preact-dom.js\",\n          \"react-dom/client\": \"/reactpy/static/preact-dom.js\",\n          \"react/jsx-runtime\": \"/reactpy/static/preact-jsx-runtime.js\"\n        }\n      }\n    </script>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" crossorigin=\"anonymous\">\n      import { mountReactPy } from \"/reactpy/static/index.js\";\n\n      mountReactPy({\n        mountElement: document.getElementById(\"app\"),\n        pathPrefix: \"/reactpy/\",\n        componentPath: \"main.counter/\",\n      });\n    </script>\n  </body>\n</html>\n
from pathlib import Path\n\nfrom starlette.applications import Starlette\nfrom starlette.responses import FileResponse\nfrom starlette.routing import Route\n\nfrom reactpy import component, html, use_state\nfrom reactpy.executors.asgi import ReactPyMiddleware\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def increment(_event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"count is {count}\"),\n        html.button({\"onClick\": increment}, \"Add\"),\n    )\n\n\nasync def home(request):\n    return FileResponse(Path(__file__).parent / \"page.html\")\n\n\nmy_app = ReactPyMiddleware(\n    Starlette(routes=[Route(\"/\", home)]),\n    root_components=[\"main.counter\"],\n)\n
# TODO\n

Serve it with uvicorn main:my_app. Verified in a browser: the counter rendered and logged [ReactPy] Connected!, and no request for preact.js reached the server \u2014 confirming the client never consulted the map.

"},{"location":"reference/client-api/#the-client-object","title":"The client object","text":"

mountReactPy builds on two classes from the @reactpy/client source package. Neither is reachable from a page \u2014 the shipped bundle exports mountReactPy and nothing else \u2014 but you meet them reading the bundle, and they are what a custom binding's sendMessage and onMessage come from.

BaseReactPyClient is the transport-independent half:

Member Purpose onMessage(type, handler) Register a handler for a message type. Returns an unsubscribe function. handleIncoming(message) Dispatch to the handlers for message.type \u2014 except live event reads, which it answers itself. eventProxy The EventProxyRegistry created with the client, which holds the live objects behind event handles. ready Promise, resolved by the first onMessage call. Gates the connection.

One type never reaches a registered handler. handleIncoming routes \"event-proxy-request\" straight to the client's own eventProxy registry (client.ts), which is why live event reads work even in a binding that registered no handler for them \u2014 and why Received message without handler is never logged for one. Build a client that does not extend BaseReactPyClient and you must answer those requests yourself, or every await on an event ends in EventProxyTimeout \u2014 see Protocol Structure.

Messages with no type, or with one nobody registered for, are logged and dropped \u2014 the connection is never closed over a bad message:

Received message without type {\u2026}\nReceived message without handler {\u2026}\n

ReactPyClient adds the websocket, plus sendMessage(message) and loadModule(name). They are not peers. sendMessage is handed to a JavaScript binding as its way to talk back; loadModule is called for the binding, by ReactPy, to pull in a web module named by an importSource whose sourceType is \"NAME\" \u2014 it resolves against jsModulesPath, which is what the server's web modules directory is mounted under.

"},{"location":"reference/client-api/#the-console","title":"The console","text":"

Every message ReactPy writes is prefixed [ReactPy], so filtering the console by that string shows the whole story of a mount. The set is finite:

Message Meaning Starting client... The layout registered its handler; a connection attempt is about to start. Connected! The handshake completed. Resets the retry counter and the backoff interval. Failed to connect! The socket closed without ever having opened. Usually a routing miss \u2014 see The URL is built by concatenation. Disconnected! The socket closed after at least one successful open. Reconnecting in 0.7500 seconds... A retry is scheduled. The number is the current backoff interval. Connection max retries exhausted! Giving up. The page needs a reload; nothing recovers after this. did not detect a suitable JavaScript import map... <script type=\"importmap\" id=\"reactpy-importmap\"> was absent. Informational \u2014 see The import map.

Failed to connect! and Disconnected! are distinguished by whether the socket ever opened, which makes them useful as a diagnosis: the first means the server never routed the request, the second means it did and then the link dropped. Both are logged at console.info level, so a console set to warnings-only shows nothing at all \u2014 check the level before concluding the client is silent.

"},{"location":"reference/client-api/#a-hand-written-mount","title":"A hand-written mount","text":"

Nothing is required beyond an element and the call. Both files below were run to produce the [ReactPy] Connected! and the incremented counter quoted on this page.

main.py Run
from starlette.applications import Starlette\nfrom starlette.responses import HTMLResponse\nfrom starlette.routing import Route\n\nfrom reactpy import component, html, use_state\nfrom reactpy.executors.asgi import ReactPyMiddleware\n\nPATH_PREFIX = \"/reactpy/\"\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def increment(_event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"count is {count}\"),\n        html.button({\"onClick\": increment}, \"Add\"),\n    )\n\n\nasync def home(request):\n    # pathPrefix and the bundle's URL both have to agree with the server's,\n    # so both are interpolated from one value rather than typed twice.\n    return HTMLResponse(f\"\"\"<!doctype html>\n        <html>\n          <body>\n            <div id=\"app\"></div>\n            <script type=\"module\" crossorigin=\"anonymous\">\n              import {{ mountReactPy }} from \"{PATH_PREFIX}static/index.js\";\n\n              mountReactPy({{\n                mountElement: document.getElementById(\"app\"),\n                pathPrefix: \"{PATH_PREFIX}\",\n                componentPath: \"main.counter/\",\n              }});\n            </script>\n          </body>\n        </html>\"\"\")\n\n\nmy_app = ReactPyMiddleware(\n    Starlette(routes=[Route(\"/\", home)]),\n    root_components=[\"main.counter\"],\n)\n
# TODO\n

The component is registered with root_components, and the componentPath in the page \u2014 \"main.counter/\" \u2014 is that dotted path with a slash appended.

The 404 in the console is usually not yours

A page like this logs one Failed to load resource: 404 on every load. It is /favicon.ico, which no browser should be told about by a component framework. Verified: it appears with everything else working. Ignore it; a second 404 naming a /reactpy/... path means the bundle could not be fetched, which does mean the mount will not happen.

"},{"location":"reference/client-api/#failure-modes","title":"Failure modes","text":"Symptom Cause TypeError: Cannot read properties of null (reading '__k') mountElement is None. The id does not exist, or the script ran before the element was parsed. TypeError: Failed to construct 'URL': Invalid URL pathPrefix missing, or missing a leading or trailing slash. No request is ever made. mountReactPy() throws Cannot read properties of undefined No options object at all. Failed to connect!, nothing rendered The socket URL did not match the server's route. Read the URL in the browser's own error line. [ReactPy] Connected!, empty element, nothing in the browser console The component path is unknown to the server. The failure is server-side: RuntimeError: Attempting to use an unregistered root component <path>. Check the server log, not the browser. Renders once, never updates Handlers are not firing \u2014 usually a snake_case event name such as on_click. See Responding to Events. Component vanishes when you remove it from the DOM, socket still open Expected. There is no unmount that closes the socket. Two import maps in the document, React warnings about a removed rule Your map does not use id=\"reactpy-importmap\"."},{"location":"reference/client-api/#related","title":"Related","text":"
  • Protocol Structure \u2014 what travels over the socket this function opens.
  • ReactPy and ReactPyMiddleware \u2014 the executors that generate this call.
  • Jinja \u2014 emitting mounts from templates, including {% pyscript_component %} for running Python in the page.
  • Use Location \u2014 reading back the path and qs the client sends.
"},{"location":"reference/common-props/","title":"Common Props","text":""},{"location":"reference/common-props/#overview","title":"Overview","text":"

Every element a ReactPy component renders is a plain dict with a fixed set of keys, and the attributes you pass to a tag end up on the DOM node nearly untouched. This page is the reference for what you may put in there \u2014 and for the handful of places ReactPy behaves differently from React.

{\n    \"tagName\": \"div\",\n    \"children\": [...],\n    \"attributes\": {...},\n    \"eventHandlers\": {...},\n    \"inlineJavaScript\": {...},\n    \"importSource\": {...},\n}\n

Only \"tagName\" is required. You almost never write this by hand \u2014 reactpy.html builds it for you \u2014 but everything below is what a tag call can carry.

"},{"location":"reference/common-props/#attributes","title":"Attributes","text":"

An attribute is any key/value pair in the first argument to a tag:

html.input({\"type\": \"search\", \"placeholder\": \"Filter\", \"autoFocus\": True})\n

ReactPy forwards the name and the value to the browser verbatim. There is no translation layer, which is the main difference from React, where JSX rewrites props before they reach the DOM.

The value must be JSON-serializable, because the model is sent to the browser as JSON. Nothing stops you from putting something else in there: with debug mode off, the value is accepted at the tag call and only fails when the layout tries to send the model, deep inside the connection:

TypeError: Type is not JSON serializable: object\n

Run with REACTPY_DEBUG=1 and the same mistake fails at the tag call, where you can see which element caused it:

TypeError: Object of type object is not JSON serializable\n

Write the name the browser expects

Attribute names are not validated against any allow-list. If the browser understands an attribute, ReactPy delivers it \u2014 including data-*, aria-*, and attributes ReactPy has never heard of:

html.div({\"aria-live\": \"polite\", \"data-row-id\": 7, \"spellcheck\": False})\n
"},{"location":"reference/common-props/#class-and-classname","title":"class and className","text":"

Both work, and both produce class=\"...\" in the DOM:

html.div({\"class\": \"one two\"}, \"...\")\nhtml.div({\"className\": \"one two\"}, \"...\")\n

There is no JSX in Python, so nothing forces you toward className. Pick one and stay consistent \u2014 class reads better beside the HTML it mirrors, className is familiar to anyone arriving from React.

"},{"location":"reference/common-props/#for-and-htmlfor","title":"for and htmlFor","text":"

Both work on <label>, and both produce for=\"...\". Prefer for; htmlFor exists only for readers who expect it.

"},{"location":"reference/common-props/#style","title":"style","text":"

Pass a dict. Keys may be camelCase or kebab-case \u2014 both are accepted, and both reach the browser in a form it understands:

html.div(\n    {\n        \"style\": {\n            \"backgroundColor\": \"rgb(255, 0, 0)\",  # camelCase\n            \"font-size\": \"20px\",  # kebab-case\n        }\n    }\n)\n

Always write the unit as part of the string

Do not rely on a bare number being given a unit. React appends px to numeric style values; ReactPy's executors do not agree with each other on this. Measured in Chrome, the same component rendered through each executor:

style value ReactPy / ReactPyMiddleware ReactPyCsr {\"fontSize\": \"30px\"} font-size: 30px font-size: 30px {\"font-size\": \"30px\"} font-size: 30px font-size: 30px {\"fontSize\": 30} font-size: 30px nothing at all {\"fontSize\": \"30\"} nothing nothing {\"zIndex\": 5} z-index: 5 z-index: 5 {\"opacity\": 0.5} opacity: 0.5 opacity: 0.5

A number that needs a unit is dropped in silence \u2014 no warning, no log line, and the layout model shows the value untouched, so it is nowhere to be found by reading the server side. Unitless properties like z-index and opacity are the exception: a number is already a valid value there.

The portable rule: put units in the string, everywhere.

{\"style\": {\"font-size\": 20}}  # works by accident in some executors\n{\"style\": {\"font-size\": \"20px\"}}  # works everywhere\n
"},{"location":"reference/common-props/#all-together","title":"All together","text":"

A table of rows is where most of these rules meet at once \u2014 key in attributes, data-* and aria-* passed straight through, and units inside style strings:

app.py Run
ROWS = [\n    {\"id\": 1, \"name\": \"Dana\", \"role\": \"admin\", \"active\": True},\n    {\"id\": 2, \"name\": \"Sam\", \"role\": \"viewer\", \"active\": False},\n]\n\n\n@component\ndef row(person):\n    # `key` lives in attributes, `data-*` and `aria-*` are forwarded verbatim,\n    # and `class` works exactly like `className`. Style values carry their own\n    # units, because a bare number is not portable across executors.\n    return html.tr(\n        {\n            \"key\": person[\"id\"],\n            \"class\": \"is-active\" if person[\"active\"] else \"is-inactive\",\n            \"data-role\": person[\"role\"],\n            \"aria-label\": f\"{person['name']}, {person['role']}\",\n            \"style\": {\"opacity\": \"1\" if person[\"active\"] else \"0.55\"},\n        },\n        html.td(person[\"name\"]),\n        html.td(person[\"role\"]),\n        html.td(\n            {\"style\": {\"text-align\": \"right\", \"font-variant-numeric\": \"tabular-nums\"}},\n            \"yes\" if person[\"active\"] else \"no\",\n        ),\n    )\n\n\n@component\ndef people_table():\n    return html.table(\n        None,\n        html.thead(\n            html.tr(\n                None,\n                html.th(\"Name\"),\n                html.th(\"Role\"),\n                html.th({\"scope\": \"col\"}, \"Active\"),\n            )\n        ),\n        # A list of children: every row needs a key or state will follow the\n        # row's position rather than the person.\n        html.tbody(None, [row(person) for person in ROWS]),\n    )\n
# TODO\n

Rendering this through Layout gives rows whose attributes are exactly what was written:

{\n    \"tagName\": \"tr\",\n    \"attributes\": {\n        \"key\": 1,\n        \"class\": \"is-active\",\n        \"data-role\": \"admin\",\n        \"aria-label\": \"Dana, admin\",\n        \"style\": {\"opacity\": \"1\"},\n    },\n    \"children\": [...],\n}\n

Nothing was renamed, and nothing was dropped. In the DOM the row arrives as:

<tr data-role=\"admin\" aria-label=\"Dana, admin\" class=\"is-active\" style=\"opacity: 1;\">\n

The one attribute that does not reach the DOM is key itself: ReactPy and ReactPyMiddleware consume it for reconciliation and never set it on the element. ReactPyCsr does leak it, so a key=\"1\" attribute shows up in the page \u2014 harmless, but do not rely on the attribute being absent or present. Every other name you write arrives as written.

"},{"location":"reference/common-props/#booleans-and-numbers","title":"Booleans and numbers","text":"

Values are serialized as JSON and coerced by the browser:

You write The browser receives {\"hidden\": True} hidden=\"\" \u2014 present, therefore true {\"hidden\": False} no attribute at all {\"readonly\": True} readonly=\"true\" {\"data-x\": \"1\"} data-x=\"1\", readable as element.dataset.x {\"tabindex\": 2} tabindex=\"2\"

React's attribute quirks do not carry over. There is no htmlFor requirement, no auto-px, and no special-casing of value versus defaultValue \u2014 what you write is what arrives.

"},{"location":"reference/common-props/#dangerouslysetinnerhtml","title":"dangerouslySetInnerHTML","text":"

Injects a raw HTML string, exactly as in React:

html.div({\"dangerouslySetInnerHTML\": {\"__html\": \"<b>bold</b>\"}})\n

The key is the plain string \"dangerouslySetInnerHTML\" \u2014 not a class to import \u2014 and its only field is \"__html\".

This is the XSS hole

ReactPy does nothing to sanitize the string. Never put user input in it.

A dangerouslySetInnerHTML attribute also suppresses children. Nothing enforces the combination \u2014 no error, no warning, at the tag call or at render time \u2014 so the children you passed are simply never displayed. Verified in the browser: for html.div({\"dangerouslySetInnerHTML\": {\"__html\": \"<b>RAW</b>\"}}, \"CHILDTEXT\") the server sends both the attribute and \"children\": [\"CHILDTEXT\"], and the rendered element contains only <b>RAW</b>. Pass the children or the raw HTML, never both, because the silent one is your children.

"},{"location":"reference/common-props/#children","title":"Children","text":"

Children are the arguments that follow the attribute dict. ReactPy accepts a wide range of types and normalizes each one:

Child Becomes str a text node, verbatim int / float its str(), so 7 renders as \"7\" None dropped \u2014 renders nothing True / False the literal text True / False list / tuple flattened into the children another dict an element a @component instance that component's rendered output

Nested lists are flattened, so [items, \"more\"] is the same as one flat list.

Only None renders nothing

False renders the word False. React-style short-circuits therefore misfire:

html.div(None, items or False)  # renders the word \"False\" when items is empty\nhtml.div(None, banner if is_visible else None)  # renders nothing\n

Replace every x and <element> habit with a conditional expression ending in else None.

"},{"location":"reference/common-props/#void-elements","title":"Void elements","text":"

html.br, html.img, html.input, html.hr, html.meta and the rest of the void-element set reject children:

html.br(\"oops\")\n
TypeError: 'br' nodes cannot have children.\n

Nothing special is needed for self-closing tags \u2014 a tag with no children is simply:

html.img({\"src\": \"/logo.png\", \"alt\": \"Logo\"})\n
"},{"location":"reference/common-props/#fragments","title":"Fragments","text":"

html.fragment groups children without adding an element to the DOM. It renders as an element with an empty tagName, which the client renders as its children:

html.fragment(\n    html.h1(\"Title\"),\n    html.p(\"Body\"),\n)\n

A fragment accepts key and nothing else:

html.fragment({\"id\": \"x\"}, html.p(\"...\"))\n
TypeError: Fragments cannot have attributes besides 'key'\n

html(...) \u2014 calling html directly \u2014 is the same fragment constructor.

"},{"location":"reference/common-props/#keys","title":"Keys","text":"

ReactPy matches children between renders to decide what to update versus what to rebuild. It reads the key out of attributes[\"key\"]:

html.ul(\n    None,\n    [html.li({\"key\": person[\"id\"]}, person[\"name\"]) for person in people],\n)\n

key is an attribute, not a sibling of tagName

React treats key as a top-level prop. ReactPy reads it from attributes, so this has no effect \u2014 the key is silently ignored:

{\"tagName\": \"li\", \"key\": \"row-1\", \"children\": [\"...\"]}\n

Write {\"tagName\": \"li\", \"attributes\": {\"key\": \"row-1\"}, \"children\": [\"...\"]}, or simply html.li({\"key\": \"row-1\"}, \"...\"). Passing key= to VdomDict directly raises ValueError: Invalid keys: {'key'}.

Without a key, a child's position in the list becomes its key. That is why reordering a list makes state stick to the wrong row: nothing moved as far as ReactPy is concerned. Keys must be unique among siblings.

When debug mode is on, ReactPy warns for each unkeyed child that came out of a list:

UserWarning: Key not specified for child in list {'children': ..., 'tagName': 'li'}\n

Run your app with REACTPY_DEBUG=1 to see them \u2014 see Rendering Lists for the same warning in context. The check only fires for children inside a list; the direct children of a tag are never warned about, and a generator cannot be inspected at all:

UserWarning: Did not verify key-path integrity of children in generator <generator object ...> - pass a sequence (i.e. list of finite length) in order to verify\n

Pass a list \u2014 a comprehension is usually the smallest change.

"},{"location":"reference/common-props/#event-handlers","title":"Event handlers","text":"

Any callable value under any attribute name is treated as an event handler \u2014 and the coroutine check runs at that same moment, so a callable which is not one raises there rather than being quietly routed to the attributes:

async def save(event):\n    await store.save(await event.target.value)\n\n\nhtml.button({\"onClick\": save}, \"Save\")\n

The name is forwarded as the DOM event to listen for, so the names that work are the DOM event names in camelCase \u2014 onClick, onInput, onKeyDown. The handler receives one argument: a reactpy.types.Event, a live proxy onto the browser's event object rather than a serialized copy of it. await event.target.value asks the page for the property at that moment; the proxy has no __getitem__, so event[\"target\"][\"value\"] raises TypeError: 'Event' object is not subscriptable. There is no \"data\" key to unwrap \u2014 the browser sends a list of arguments, and ReactPy hands you its single element.

The coroutine requirement is not a style rule and async is not optional. The event lives in the browser, so every read is a round trip which must be awaited; a handler that cannot be awaited cannot make one. core/events.py, check_is_async_handler therefore rejects a def and a lambda outright, at element-build time, with a TypeError naming the function. What is accepted: a coroutine function, a callable object whose __call__ is a coroutine, and a functools.partial or functools.wraps wrapper around either. See Responding to Events.

A handler wrapped in EventHandler receives the raw list

A plain coroutine is called with the browser's argument list unpacked, so async def f(event) gets the event. An EventHandler does the opposite: it is called with the list itself \u2014 an EventArguments, a list subclass \u2014 so async def f(data) must index into it. The difference stays invisible until a handler is converted, and the two directions fail differently \u2014 AttributeError: 'EventArguments' object has no attribute 'target' one way, TypeError: 'Event' object is not subscriptable the other:

from reactpy.core.events import EventHandler\n\nasync def save(event):        # plain coroutine: receives the event\n    await store.save(await event.target.value)\n\n\nasync def save_all(data):     # EventHandler: receives the list of arguments\n    await store.save(await data[0].target.value)\n\n\nhtml.button({\"onClick\": save}, \"Save\")\nhtml.button({\"onClick\": EventHandler(save_all, prevent_default=True)}, \"Save\")\n
"},{"location":"reference/common-props/#reactpys-own-options","title":"ReactPy's own options","text":"

Five behaviours run in the browser before the event is sent to the server. Configure them by wrapping the function, not by an attribute:

from reactpy.core.events import EventHandler\n\nhtml.form(\n    {\"onSubmit\": EventHandler(submit, prevent_default=True)},\n    html.button({\"type\": \"submit\"}, \"Submit\"),\n)\n
Parameter Value Effect prevent_default bool calls preventDefault() in the browser before sending stop_propagation bool calls stopPropagation() in the browser before sending stop_immediate_propagation bool also calls stopImmediatePropagation(), blocking sibling listeners on the same element debounce int ms coalesces outgoing events on a trailing edge, so the server sees one after the user pauses \u2014 only on user-input elements throttle int ms rate-limits outgoing events on a leading edge, on any element

A non-negative integer is required for debounce and throttle; anything else is ignored with a browser-console warning rather than an error:

Ignoring invalid debounce value \"fast\" on event handler \"onInput\": expected a non-negative integer.\n

A dict is not an event handler

The React-style {\"function\": ..., \"preventDefault\": True} shape does not work here. A dict is not callable, so ReactPy treats it as an ordinary attribute and no listener is ever registered. With debug mode on you get an immediate error instead of silence:

TypeError: Object of type function is not JSON serializable\n
# wrong: no listener is attached\nhtml.button({\"onClick\": {\"function\": save, \"preventDefault\": True}}, \"...\")\n\n# right\nhtml.button({\"onClick\": EventHandler(save, prevent_default=True)}, \"...\")\n
"},{"location":"reference/common-props/#inline-javascript","title":"Inline JavaScript","text":"

A str under an event handler attribute is treated as JavaScript to run in the page instead of being sent to Python:

html.button({\"onClick\": \"console.log(event)\"}, \"Log the event\")\n

ReactPy records it under the element's inlineJavaScript rather than eventHandlers:

{\n    \"tagName\": \"button\",\n    \"children\": [\"Log the event\"],\n    \"inlineJavaScript\": {\"onClick\": \"console.log(event)\"},\n}\n

The string is eval'd in the page; if it evaluates to a function, that function is called with the event.

Trusted strings only

This runs with your application's privileges. Never build the string from user input \u2014 use a Python handler and pass data through state.

<script> elements reject handlers

html.script raises ValueError: 'script' elements do not support event handlers. It also infers a key from its content or src, so the same script is not re-run on every render.

"},{"location":"reference/common-props/#importsource","title":"importSource","text":"

importSource marks an element as coming from a JavaScript module rather than a built-in tag. ReactPy's web module support produces it for you; you rarely set it by hand. Its keys are source, fallback, sourceType and unmountBeforeUpdate, and sourceType must be \"URL\" or \"NAME\":

JsonSchemaValueException: data.children[0].importSource.sourceType must be one of ['URL', 'NAME']\n
"},{"location":"reference/common-props/#invalid-dictionaries","title":"Invalid dictionaries","text":"

ReactPy validates the six key names as it builds an element:

from reactpy.types import VdomDict\n\nVdomDict(tagName=\"div\", bogus=1)\n
ValueError: Invalid keys: {'bogus'}.\n
element = VdomDict(tagName=\"div\")\nelement[\"bogus\"] = 1\n
KeyError: 'Invalid key: bogus'\n

A dict used as a child with no \"tagName\" does not raise on the server. The layout catches it, logs the traceback, and sends an element whose error field carries the message \u2014 which the client prints in the page, in a <pre> that replaces the component, not in the console:

html.div(None, {\"children\": [\"x\"]})\n
ValueError: Expected a VDOM element dict, not {'children': ['x']}\n

That text exists only under REACTPY_DEBUG=1. Verified in a browser with a bad child between two headings: with debug on, the page read above / ValueError: Expected a VDOM element dict\u2026 / below; with debug off the bad component simply vanished \u2014 above / below, no <pre>, and nothing at all in the console. The server log has the traceback either way. So when an element renders as nothing, check the server log first and re-run with REACTPY_DEBUG=1 before trusting the page. See Protocol Structure.

"},{"location":"reference/common-props/#at-a-glance","title":"At a glance","text":""},{"location":"reference/common-props/#vdomdict-keys","title":"VdomDict keys","text":"Key Type Notes tagName str required; \"\" means a fragment children list any accepted child type, flattened attributes dict[str, Any] must be JSON-serializable eventHandlers dict[str, EventHandler] written for you from callables inlineJavaScript dict[str, str] written for you from str handlers importSource dict written for you by web modules

Any other key raises, as shown above. VdomDict is a dict subclass, so you can read and update it with normal dict access \u2014 but not add a seventh key.

"},{"location":"reference/common-props/#attribute-names-worth-knowing","title":"Attribute names worth knowing","text":"Attribute Value class / className str of space-separated class names for / htmlFor str, the id of the labelled control style dict of CSS property to value; include units in strings dangerouslySetInnerHTML {\"__html\": str} key str or int, unique among siblings anything else forwarded to the browser verbatim"},{"location":"reference/common-props/#related","title":"Related","text":"
  • Usage \u2014 the tag API itself: naming rules, custom tags, SVG
  • Responding to Events \u2014 the event proxy, and why handlers must be coroutines
  • Protocol Structure \u2014 what an event costs on the wire
  • Rendering Lists \u2014 keys and why rows keep the wrong state
  • Use Ref \u2014 ReactPy's Ref, which is never attached to an element
"},{"location":"reference/components-and-hooks-must-be-pure/","title":"Components and Hooks must be pure","text":""},{"location":"reference/components-and-hooks-must-be-pure/#overview","title":"Overview","text":"

A component is a formula: given the same arguments, it must return the same VDOM, and it must change nothing outside itself. This page states that contract precisely \u2014 for components and for hooks \u2014 and names the places ReactPy expects side effects instead.

The short version:

  • A component may read its arguments, call hooks, and return VDOM. It must not mutate anything that existed before it was called, and its output must not depend on anything it did not receive.
  • A hook follows the same rule, plus one of its own: it must be called in the same order on every render \u2014 see Rules of Hooks.

This is not a style preference. ReactPy calls your component at times and frequencies you do not control, and rendering is where the shared-state bug below lives \u2014 because in ReactPy components run on a server, inside one long-lived Python process.

"},{"location":"reference/components-and-hooks-must-be-pure/#the-contract","title":"The contract","text":"

A component body is allowed to:

Do Example Read its own arguments html.p(title) Call hooks count, set_count = use_state(0) Compute from those values html.p(f\"{count} item(s)\") Define functions it hands to the client {\"onClick\": save} Call other components html.div(None, Row(item))

A component body is not allowed to:

Don't Why it fails Assign to a module-level or global variable Shared by every connection in the process; see The server makes this worse Mutate an object it received as an argument The caller still holds it, so the change is visible outside the render Mutate a value read from use_state The comparison that decides whether to re-render can drop an in-place mutation silently Read the clock, random, or the environment Same inputs, different output \u2014 the formula is broken Do I/O: open, requests, a database, print Runs on every render, at a frequency you do not control Start a task, thread, or timer Leaks one per render

The two halves of the rule are independent. random.choice mutates nothing and is still impure of output; writing a module-level dict is an outside effect whether or not it changes what you return.

The pair below is the difference, side by side. Both render identically on the first pass. The first one counts renders in a module-level variable, so the number it prints is a property of the process, not of the component \u2014 press the button twice in two browser tabs and the two pages disagree about what \"the same render\" produced:

impure.pypure.py Run
# Module-level state is shared by every connection and survives across renders,\n# so `total` is not part of the component's inputs at all. Two browsers, or two\n# renders of the same component, see the same counter.\ntotal = 0\n\n\n@component\ndef impure_counter():\n    global total\n    count, set_count = use_state(0)\n\n    # Render mutates module state: an outside effect, and a value that changes\n    # what the next render returns even though nothing the component received\n    # changed. Nothing warns about this -- the symptom is a number that does not\n    # match the button presses.\n    total += 1\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.p(f\"renders (module-wide): {total}\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
@component\ndef counter():\n    count, set_count = use_state(0)\n\n    # Nothing outside this component is touched. `count` comes from use_state,\n    # which ReactPy keeps per component instance -- so two connections to this\n    # same component each have their own number, and re-running the render\n    # changes nothing but the value returned.\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n
"},{"location":"reference/components-and-hooks-must-be-pure/#the-server-makes-this-worse","title":"The server makes this worse","text":"

In a browser, module-level state belongs to one user. In ReactPy it belongs to every connection your process serves, for the whole life of the process. Rendering into a shared container is therefore not a code smell but a data leak \u2014 and it produces no error.

Three separate layouts, three separate visitors, one module-level dict:

SESSION = {}\n\n\n@component\ndef account(name):\n    SESSION.setdefault(\"cart\", [])\n    SESSION[\"cart\"].append(f\"{name}-item\")\n    return html.div(f\"{name}: cart has {len(SESSION['cart'])} item(s)\")\n

Measured output from three independent layouts:

visitor A: cart has 1 item(s): ['A-item']\nvisitor B: cart has 2 item(s): ['A-item', 'B-item']\nvisitor C: cart has 3 item(s): ['A-item', 'B-item', 'C-item']\n

Nothing crashes, each page looks plausible, and every visitor after the first sees other people's data. The same failure appears with total += 1 at module scope, a module-level cache, or a mutable default argument on a component.

Per-user state belongs in use_state or use_reducer, which ReactPy keeps per component instance. State genuinely shared across connections belongs in an explicit store you pass in \u2014 through context or an argument \u2014 never in a bare module global that rendering reaches for.

"},{"location":"reference/components-and-hooks-must-be-pure/#where-side-effects-belong","title":"Where side effects belong","text":"Need Put it in React to a click, keystroke, submit An event handler (Responding to Events) Run once on mount, or when values change use_effect (Use Effect) Await something outside the render use_async_effect (Use Async Effect) Read a request's headers or URL use_connection, use_scope, use_location Build a value for the render, only when inputs change use_memo (Use Memo) Log for debugging use_debug_value, or a logging logger outside the render Load data at process start Executor startup (ReactPy)

An event handler is the default answer. It is defined inside the component but runs later, in response to something the user did, so it may do anything a pure function may not.

use_effect is the escape hatch, not the habit

Effects run after the render, which makes them correct for touching the outside world and awkward for expressing logic. Most of the data you would reach for an effect for can be computed during the render instead. If you find yourself writing an effect to derive a value, stop and compute it in the body.

"},{"location":"reference/components-and-hooks-must-be-pure/#hooks-must-be-pure-too","title":"Hooks must be pure too","text":"

A hook body is a function your component calls during its render, so it inherits every rule above, plus these:

  • No global, no module mutation. Same leak, harder to see because the hook is shared by several components.
  • No time, no randomness. use_memo(lambda: random.random(), []) fixes each instance to whatever the first render produced. Pass the value in as an argument instead.
  • No I/O. A custom hook that queries a database runs that query on every render of every component that uses it.
  • No conditional hook calls, and no hooks in loops. That is the Rules of Hooks, and it is the one a reviewer can catch by reading.

A custom hook that needs the outside world should take the data as an argument, or expose an event handler that does the work when called.

"},{"location":"reference/components-and-hooks-must-be-pure/#what-reactpy-cannot-do-with-purity","title":"What ReactPy cannot do with purity","text":"

React skips re-rendering a component whose props are unchanged \u2014 safe because pure functions are safe to cache. ReactPy has no such bailout: a child component re-runs whenever its parent renders, no matter how pure it is.

There is use_memo for values and use_callback for function identity, which reduce the work a render does, but neither stops the render. So purity here buys correctness, testability, and safety under restart \u2014 not performance. Do not add memoization on the theory that ReactPy will skip the render; it will not.

Why the rule is enforced by review, not by tests

ReactPy cannot detect impurity \u2014 reading a global is a legal Python statement. The rule is about where the language lets you put things, which is why it belongs in code review.

"},{"location":"reference/components-and-hooks-must-be-pure/#testing-purity","title":"Testing purity","text":"

A pure component is an ordinary function, so test it as one \u2014 no browser:

import asyncio\n\nfrom reactpy.core.layout import Layout\n\n\ndef render_model(component):\n    async def go():\n        async with Layout(component) as layout:\n            return (await layout.render())[\"model\"]\n\n    return asyncio.run(go())\n\n\ndef test_same_arguments_same_output():\n    assert render_model(Greeting(\"a\")) == render_model(Greeting(\"a\"))\n

Run each layout in its own event loop and build a fresh component instance per call. If a component renders differently the second time, or one layout's render changes another's output, it is impure and the test says so \u2014 which is exactly what happens for a component that counts its own renders.

"},{"location":"reference/components-and-hooks-must-be-pure/#symptoms","title":"Symptoms","text":"You see Likely cause Two users see each other's data Module-level state written during render A counter is higher than the number of clicks The component rendered more often than you assumed, and it counts renders Numbers change on reload with no user action Clock or random during render A list mutation does nothing State mutated in place; see Updating Arrays in State One component's render changes another's output A shared object reached through a global, a default argument, or a class attribute"},{"location":"reference/components-and-hooks-must-be-pure/#related","title":"Related","text":"
  • Rules of React \u2014 the section overview
  • Rules of Hooks \u2014 the hook-order rule, and what breaking it does in ReactPy
  • React calls Components and Hooks \u2014 why \"it works locally\" does not prove purity
  • Keeping Components Pure \u2014 the tutorial version, with exercises
  • Use State \u2014 per-instance state, and the comparison that decides re-renders
  • Use Effect \u2014 the sanctioned place for side effects
"},{"location":"reference/jinja/","title":"Jinja","text":""},{"location":"reference/jinja/#overview","title":"Overview","text":"

ReactPyJinja adds three tags to a Jinja2 environment so an existing Python web app can mount ReactPy components inside templates it already owns \u2014 one tag for server-rendered components, two for components that run in the browser via PyScript.

{% component \"my_app.pages.counter\" %}\n

Install the extra the extension needs:

pip install \"reactpy[asgi,jinja]\"\n

It lives at reactpy.templatetags.ReactPyJinja. Despite the templatetags name it is not a Django app \u2014 it is a jinja2-simple-tags StandaloneTag, so it works with any Jinja2 environment.

"},{"location":"reference/jinja/#reference","title":"Reference","text":""},{"location":"reference/jinja/#registering","title":"Registering the extension","text":"

Two equivalent forms:

from jinja2 import Environment, FileSystemLoader\n\nfrom reactpy.templatetags import ReactPyJinja\n\n# at construction\nenv = Environment(loader=FileSystemLoader(\"templates\"), extensions=[ReactPyJinja])\n\n# or afterwards\nenv = Environment(loader=FileSystemLoader(\"templates\"))\nenv.add_extension(ReactPyJinja)\n

Both register all three tags at once; there is no way to register one alone, and no configuration to pass. Registering ReactPyJinja(...) as an instance and calling .register() does not work \u2014 that object has no register method.

Register before a template is compiled

Tags are resolved when a template is parsed. If the environment has already compiled a template that uses them, you get jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'component'. Frameworks that cache templates at startup make this a startup-time error, which is easy to attribute to something else.

"},{"location":"reference/jinja/#component-tag","title":"{% component \"dotted.path\" %}","text":"

Emits the markup that mounts a server-side component: an empty mount element, an import-map fallback, and a module script that calls mountReactPy.

{% component \"my_app.pages.counter\" %}\n{% component \"my_app.pages.badge\", class=\"badge\" %}\n
  • The dotted path (required): the import path of the root component function \u2014 the same string you give ReactPyMiddleware's root_components. It is embedded verbatim into the emitted JavaScript; ReactPy imports it on the server when the browser asks to connect.
  • class (optional): a CSS class for the generated mount element, producing <div id=\"...\" class=\"badge\"></div>.

safe_output is set on the tag, so Jinja does not escape the returned HTML even under autoescape=True.

Arguments are parsed as Python expressions

The tag uses Jinja's argument parser, so keyword arguments must follow a comma \u2014 {% component \"m.f\" class=\"x\" %} fails at template-compile time with TemplateSyntaxError: expected token ',', got 'class'. Write {% component \"m.f\", class=\"x\" %}.

The path may be a quoted string (as above) or a Jinja expression that resolves to one; in the latter case it is read from the template context, so an unquoted my_app.pages.counter requires my_app to be a context variable.

"},{"location":"reference/jinja/#errors-this-tag-raises","title":"Errors this tag raises","text":"Mistake Error Omitting the path TypeError: component() missing 1 required positional argument: 'dotted_path' Passing a second positional TypeError: component() takes 1 positional argument but 2 were given Any other keyword ValueError: Unexpected keyword arguments: id, style A typo in the tag name TemplateSyntaxError: Encountered unknown tag 'bogus_tag'.

A wrong dotted path is not one of them. Nothing checks that the module or attribute exists: the tag happily emits markup pointing at a path that will never connect, and the only symptom is a permanently empty element plus a failed request in the browser console. Verify the path against what you passed to root_components.

"},{"location":"reference/jinja/#pyscript-setup","title":"{% pyscript_setup %}","text":"

Emits the PyScript bootstrap \u2014 stylesheet, module script, and the config that installs ReactPy into the browser.

<head>\n    {% pyscript_setup %}\n</head>\n
  • extra_py (optional, positional, repeatable): extra Python packages for the PyScript runtime \u2014 {% pyscript_setup \"numpy\", \"rich\" %}. They are appended to the package list alongside ReactPy's own wheel.
  • extra_js (optional, keyword): a JSON object mapping module URLs to names, e.g. extra_js='{\"./my.js\": \"mylib\"}'. It is merged into the config's js_modules.main. A plain string that is not valid JSON raises json.decoder.JSONDecodeError.
  • config (optional, keyword): a JSON object (or JSON string) merged over the generated config, letting you set anything PyScript accepts.

Put this tag in the document head, once. A page with several pyscript_component tags needs one bootstrap, not one per component.

Packages install in the browser

Anything in extra_py must be installable under Pyodide \u2014 pure Python, or a wheel built for it. A package with C extensions that installs cleanly on the server will fail in the browser, and the failure appears only in the browser console.

"},{"location":"reference/jinja/#pyscript-component-tag","title":"{% pyscript_component \"path/to/file.py\" %}","text":"

Emits a <div> plus a <script type='py'> that runs the named file's component in the browser.

{% pyscript_component \"components/counter.py\" initial=\"Loading...\" %}\n
  • File paths (required, positional, repeatable): paths to Python files, resolved relative to the process's current working directory at template-render time. Several files may be passed; they are concatenated into one script.
  • initial (optional): markup rendered inside the mount element while PyScript boots.
  • root (optional): the name of the root component function, default \"root\".

Because the files are read and inlined, the browser never fetches them and your app serves no route for them.

The file's source ships to the client

Nearly the whole file is embedded in the HTML: executors/pyscript/utils.py, minify_python deletes # comments and inner docstrings before inlining, but the code and every string literal survive. One docstring does escape the filter \u2014 the module docstring, because the regex that removes docstrings needs a newline before the opening quotes and that docstring sits at the very start of the file (measured: a module docstring reached the page, a function docstring and both comment styles did not). Treat all of it as published, including any secret read into a string at render time. This is unrelated to debug mode: turning reactpy.config.REACTPY_DEBUG off hides nothing here.

Mistake Error File does not exist (wrong cwd or path) FileNotFoundError: [Errno 2] No such file or directory: 'components/missing.py' No root function in the file ValueError: Could not find the root component function 'root' in your PyScript file(s).

Both are raised at render time, so they surface as a 500 on the page \u2014 not as a broken widget.

Prefer ReactPyCsr unless you are embedding

For a page built from scratch, ReactPyCsr is an ASGI app with its own URL and handles the setup tag for you. These two tags exist for putting a component inside a template you do not control. See ReactPyCsr.

"},{"location":"reference/jinja/#usage","title":"Usage","text":""},{"location":"reference/jinja/#a-server-rendered-component-in-an-existing-page","title":"A server-rendered component in an existing page","text":"main.pyhome.htmlpages.py Run
templates = Jinja2Templates(\n    env=Environment(\n        loader=FileSystemLoader(\"templates\"),\n        extensions=[ReactPyJinja],\n        autoescape=True,\n    )\n)\n\n\nasync def home(request):\n    return templates.TemplateResponse(request, \"home.html\")\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", home)])\n\n# The dotted path in the template is resolved on the server, not in the\n# template: the browser asks ReactPy to render `pages.counter`, and ReactPy\n# imports it to satisfy that request. Register exactly the paths the template\n# passes to `{% component %}` -- here the `counter` and `badge` functions in\n# the sibling `pages.py`.\nmy_app = ReactPyMiddleware(\n    starlette_app, root_components=[\"pages.counter\", \"pages.badge\"]\n)\n
<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <title>Accounts</title>\n</head>\n\n<body>\n  <h1>Welcome</h1>\n\n  {# One tag per component; ReactPy gives each mount element its own id. #}\n  {% component \"pages.counter\" %}\n\n  {# `class` must follow a comma -- the tag parses its arguments as Python. #}\n  {% component \"pages.badge\", class=\"badge\" %}\n</body>\n\n</html>\n
from reactpy import component, html, use_state\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n\n\n@component\ndef badge():\n    # The tag's `class` argument styles the mount element, not this markup, so\n    # the component is free to render whatever it likes inside it.\n    return html.div(\n        html.span({\"class\": \"badge-label\"}, \"Status:\"),\n        html.span({\"class\": \"badge-value\"}, \"active\"),\n    )\n
# TODO\n

The components cannot live in main.py even though that is where the wiring is. The browser reaches them by dotted path, so they must sit in a module the server can import \u2014 a component defined next to the view is reachable as main.counter, but then the template has to say exactly that. Whatever you choose, the path in {% component %} and the entry in root_components must agree character for character. Nothing checks it for you: a mismatch emits markup that mounts an element which stays empty forever, and the failed import surfaces only as a server-side traceback on the websocket request.

"},{"location":"reference/jinja/#a-client-side-component","title":"A client-side component","text":"

Setup in the head, the component where it belongs:

home.htmlcounter.py Run
<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Counter</title>\n    {% pyscript_setup %}\n  </head>\n  <body>\n    {% pyscript_component \"components/counter.py\" initial=\"Loading...\" %}\n  </body>\n</html>\n
@component\ndef root():\n    # `root` is the name the tag looks for by default; pass root=\"other\" to\n    # point it somewhere else. This file is inlined into the page at render\n    # time and runs in the browser, so treat all of it as public.\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n

There is no websocket, so use_connection, use_scope, and use_location have no connection to read and raise \u2014 see Use Connection.

"},{"location":"reference/jinja/#which-one-to-use","title":"Which one to use","text":"{% component %} {% pyscript_setup %} + {% pyscript_component %} Component runs Server Browser Needs a websocket Yes No Component source visible to users No Yes First paint Server HTML, then it connects initial until PyScript boots Can read the request Yes (use_connection, use_scope, use_location) No Needs a route on your app Yes No Packages must be Pyodide-compatible No Yes"},{"location":"reference/jinja/#related","title":"Related","text":"
  • Add React to an Existing Project \u2014 the full Starlette wiring these tags are for
  • ReactPyMiddleware \u2014 the middleware that serves server-rendered components, and the markup this tag generates
  • ReactPyCsr \u2014 the ASGI-app form of the PyScript tags
  • ReactPy \u2014 standalone server-rendered app
  • Common Props \u2014 what a component may return
"},{"location":"reference/overview/","title":"Overview","text":"

This section is the detailed reference for ReactPy. For an introduction, start with the Learn section.

The reference is split into functional subsections:

"},{"location":"reference/overview/#hooks","title":"Hooks","text":"

Call these at the top level of a component. ReactPy provides twelve:

  • use_state \u2014 declare a state variable.
  • use_effect \u2014 run a synchronous side effect.
  • use_async_effect \u2014 run an async def effect, or one that keeps running across renders.
  • use_reducer \u2014 hold state behind a reducer.
  • use_context \u2014 read a value provided higher in the tree.
  • use_ref \u2014 hold a mutable box that survives renders.
  • use_memo \u2014 cache a computed value.
  • use_callback \u2014 cache a function.
  • use_debug_value \u2014 attach a debug label to a component.
  • use_connection \u2014 inspect the current websocket connection.
  • use_scope \u2014 read the ASGI connection scope.
  • use_location \u2014 read the browser's current URL.

There is no use_id, use_transition, use_deferred_value, use_layout_effect, use_insertion_effect, use_imperative_handle, or use_sync_external_store. Texts ported from React that rely on them have no ReactPy equivalent.

"},{"location":"reference/overview/#html","title":"HTML","text":"

How to describe markup. ReactPy has no JSX; you call functions.

  • Common Props \u2014 attributes shared by every tag.
  • Usage \u2014 the html.* functions and the shape of a VDOM dictionary.
"},{"location":"reference/overview/#executors","title":"Executors","text":"

The ASGI applications that serve your components:

  • ReactPy \u2014 standalone single-page app; components render on the server.
  • ReactPyMiddleware \u2014 add ReactPy routes to an ASGI app you already have.
  • ReactPyCsr \u2014 run component code in the browser via PyScript.
"},{"location":"reference/overview/#template-integration","title":"Template integration","text":"
  • Jinja \u2014 render components from Jinja templates.
"},{"location":"reference/overview/#rules-of-react","title":"Rules of React","text":"

Idioms for expressing React patterns so they stay understandable and correct:

  • Components and Hooks must be pure
  • React calls Components and Hooks
  • Rules of Hooks
"},{"location":"reference/overview/#internals","title":"Internals","text":"
  • Protocol Structure \u2014 the messages exchanged between server and client.
  • Client API \u2014 the browser-side entry point.
"},{"location":"reference/overview/#where-react-differs","title":"Where React differs","text":"

ReactPy models React's function-component API, but it is not React. Effects infer their dependencies instead of requiring an array, state updates are replaced rather than merged, an update that mutates in place is dropped without any error, and several React hooks do not exist at all. Each page calls out the differences that matter to it, so when porting code or prose from a React tutorial, read the ReactPy page rather than assume parity.

"},{"location":"reference/protocol-structure/","title":"Protocol Structure","text":""},{"location":"reference/protocol-structure/#overview","title":"Overview","text":"

ReactPy renders on the server and paints in the browser. A single websocket carries four message types: the server sends \"layout-update\" patches, the client sends \"layout-event\" interactions, and a handler which wants to read the browser goes back and forth over \"event-proxy-request\" / \"event-proxy-response\".

server --{\"type\": \"layout-update\", \"path\": ..., \"model\": ...}--------------->  client\nserver <--{\"type\": \"layout-event\", \"target\": ..., \"data\": [...]}------------  client\nserver --{\"type\": \"event-proxy-request\", \"eventId\": ..., \"path\": ...}------->  client\nserver <--{\"type\": \"event-proxy-response\", \"requestId\": ..., \"value\": ...}-  client\n

The last two are new, and they change what an event is. Nothing about the DOM event is serialized at all: the client keeps the live object in a registry of its own and sends the server a handle. What the server can do with that handle \u2014 and what happens when the browser never answers \u2014 is the subject of The event proxy round trip.

"},{"location":"reference/protocol-structure/#the-connection","title":"The connection","text":"

Every mounted component opens exactly one websocket. The client builds it from the pathPrefix and componentPath it was handed at mount time (static/index.js, mountReactPy):

ws://<host><pathPrefix><componentPath>?path=<page path>&qs=<page query string>\n

With the default REACTPY_PATH_PREFIX of \"/reactpy/\" and a component registered as \"main.counter/\", mounting on /page?tab=1 opens:

ws://127.0.0.1:8000/reactpy/main.counter/?path=%2Fpage&qs=%3Ftab%3D1\n

Two things about that URL are load-bearing:

  • The trailing slash after the dotted path is required. The server matches the websocket path against

    ^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$\n

    which ends in /$. Without the slash the route never matches, the request falls through to your own ASGI app, and the handshake is refused. Verified from a page: ws://\u2026/reactpy/main.counter/?path=/ opens, ws://\u2026/reactpy/main.counter?path=/ fails with Unexpected response code, and the console logs [ReactPy] Failed to connect!.

  • path and qs are how the component learns its URL. ReactPy runs on the server, where the browser's location does not exist. The client copies window.location.pathname into path and window.location.search into qs; the server parses them back out (executors/asgi/middleware.py, _location_from_websocket_query_string) into the Location that use_location() returns. They are percent-encoded and carried in the websocket's own query string, so a server that normalises the handshake URL cannot silently rewrite them.

Reconnection does not re-read the mount

When the socket drops, the client waits and reconnects. Each attempt refreshes path and qs from the current page, but it never re-reads componentPath or the connect options \u2014 those were captured when the element mounted. A client-side navigation that changes which component belongs on the page needs a fresh mount, not a reconnect.

The client retries with exponential backoff: REACTPY_RECONNECT_INTERVAL (750 ms) times REACTPY_RECONNECT_BACKOFF_MULTIPLIER (1.25) per attempt, capped at REACTPY_RECONNECT_MAX_INTERVAL (60 s), for REACTPY_RECONNECT_MAX_RETRIES (150) tries. Those are arguments to mountReactPy(...); see ReactPyMiddleware for the bootstrap that emits them.

"},{"location":"reference/protocol-structure/#the-server-to-client-message","title":"The server-to-client message","text":"

One message type goes down the wire. Its keys are fixed (reactpy.types.LayoutUpdateMessage):

Key Value Meaning \"type\" \"layout-update\" Always this literal. \"path\" str A JSON Pointer into the client's copy of the model. \"\" means replace everything. \"model\" dict The value to place at that pointer.

The model is JSON, not a VdomDict. Six key names are legal (reactpy/types.py, ALLOWED_VDOM_KEYS), and only tagName is required:

Key Value Notes \"tagName\" str The DOM tag, or \"\" for a wrapper element. \"children\" list Nested models or plain strings. \"attributes\" dict Sent to the client as written, and written to the DOM essentially verbatim. Two ReactPy-specific exceptions: key is read for reconciliation and consumed, never becoming an attribute, and _reactpy_ack_seq is deleted by the client before render. \"eventHandlers\" dict Handler name to target spec, described below. \"inlineJavaScript\" dict Handler name to JavaScript source string. \"importSource\" dict Marks an element as coming from a JavaScript module.

Any other key name is rejected by VdomDict while the element is being built (reactpy/types.py):

ValueError: Invalid keys: {'bogus'}.\n

The wire schema in core/vdom.py, VDOM_JSON_SCHEMA is a looser thing: it lists these six plus \"error\" (which the layout injects when a component raises), and it is only consulted when REACTPY_CHECK_VDOM_SPEC or REACTPY_DEBUG is on.

Here is a real mount message for a counter, captured off the wire:

{\n  \"type\": \"layout-update\",\n  \"path\": \"\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"children\": [\n      {\n        \"tagName\": \"\",\n        \"children\": [\n          {\n            \"tagName\": \"\",\n            \"children\": [\n              {\n                \"tagName\": \"div\",\n                \"children\": [\n                  {\"tagName\": \"h1\", \"children\": [\"count is 0\"]},\n                  {\n                    \"tagName\": \"button\",\n                    \"eventHandlers\": {\n                      \"onClick\": {\n                        \"target\": \"/0/0/0/1:onClick\",\n                        \"preventDefault\": false,\n                        \"stopPropagation\": false\n                      }\n                    },\n                    \"children\": [\"Add\"]\n                  }\n                ]\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}\n
"},{"location":"reference/protocol-structure/#wrapper-elements","title":"Wrapper elements","text":"

Read from the top, that model is three elements with \"tagName\": \"\" before the div appears. ReactPy needs somewhere to put a component's output that is not itself a DOM node, and it does not reuse your tag for the purpose.

Do not depend on how many there are. The count is an implementation detail that shifts with structure: the same component produced one wrapper rendered bare through Layout, and three behind the ASGI executor's context provider. Never index the tree by position, and never assume the root has a tag name. The client renders a wrapper as a fragment, so it costs you nothing on screen.

"},{"location":"reference/protocol-structure/#patches-not-re-renders","title":"Patches, not re-renders","text":"

The server does not resend the tree. When only an inner component's state changes, it sends the subtree rooted where that component renders:

{\n  \"type\": \"layout-update\",\n  \"path\": \"/children/0/children/0\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"children\": [\n      {\n        \"tagName\": \"div\",\n        \"children\": [\n          {\"tagName\": \"h1\", \"children\": [\"count is 1\"]},\n          {\n            \"tagName\": \"button\",\n            \"eventHandlers\": {\n              \"onClick\": {\n                \"target\": \"/0/0/0/1:onClick\",\n                \"preventDefault\": false,\n                \"stopPropagation\": false\n              }\n            },\n            \"children\": [\"Add\"]\n          }\n        ]\n      }\n    ]\n  }\n}\n

That is the whole reply to a click: one-third the depth of the mount, and the only text that changed is inside it. The pointer indexes the model, and path: \"\" replaces the root \u2014 which is what a state change in the topmost component produces.

A patch replaces rather than merges, so whatever subtree it carries is complete: keys you would expect to be sticky, eventHandlers among them, are resent inside it. Nothing is deleted by a patch; a smaller subtree simply arrives with fewer children.

"},{"location":"reference/protocol-structure/#errors-arrive-as-a-model","title":"Errors arrive as a model","text":"

A component that raises during render does not kill the socket. The layout sends an element whose tagName is empty and whose error is the message, which the client prints in a <pre> in place of the component:

{\n  \"type\": \"layout-update\",\n  \"path\": \"\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"error\": \"ValueError: Expected a VDOM element dict, not {'noTag': True}\"\n  }\n}\n

The schema ties the two together: when error is present, tagName must be empty. Without REACTPY_DEBUG=1 the same element carries \"error\": \"\" \u2014 an empty string, which the client renders as nothing, so the component silently disappears instead of showing a blank box.

"},{"location":"reference/protocol-structure/#the-client-to-server-message","title":"The client-to-server message","text":"

One message type goes up (reactpy.types.LayoutEventMessage):

Key Value Meaning \"type\" \"layout-event\" Always this literal. \"target\" str The handler's target, copied from the model. \"data\" list The arguments for the handler. Usually one event handle. \"seq\" int Optional. A client-assigned counter, per element.

A real click on the button above:

{\n  \"type\": \"layout-event\",\n  \"target\": \"/0/0/0/1:onClick\",\n  \"data\": [{\"__reactpy_event__\": {\"id\": \"lzk4q1-0\"}, \"type\": \"click\"}],\n  \"seq\": 1\n}\n
"},{"location":"reference/protocol-structure/#how-data-is-built","title":"How data is built","text":"

The client does not serialize the DOM event. It stores the live object in a registry and sends a handle \u2014 {\"__reactpy_event__\": {\"id\": ...}} \u2014 plus \"type\", the one plain value the client attaches because the server needs it to name the event and it is too cheap to be worth a round trip (vdom.tsx). Anything else the handler wants is fetched, per property, over the proxy messages described below.

That replaced the old event-to-object package, which walked the event and its target: enumerable properties, plus value, checked, files, type and name read off the element even though they are accessors, plus dataset, plus every named control of a form on submit. A single <input> used to put the whole element on the wire \u2014 around ninety keys, of which a handler reads one. What goes over the wire for an event now is a handle and a type: on the order of a hundred and fifty bytes.

The consequence for you: the second element of data is whatever the event was dispatched with, and the first is the handle \u2014 a dict of the marker and nothing but the id, never a copy of the event. Handlers are still free to take more than one argument \u2014 vdom.tsx builds the list from the event plus any extra values the dispatch supplied \u2014 so index data positionally rather than counting on a specific key surviving.

"},{"location":"reference/protocol-structure/#unpacking-and-why-the-two-handler-forms-differ","title":"Unpacking, and why the two handler forms differ","text":"

Layout._process_event_queue turns data into arguments and calls the handler \u2014 but not the same way for both kinds of handler (core/events.py, to_event_handler_function):

function(*data)   # a plain coroutine: the list is unpacked\nfunction(data)    # an EventHandler: the list itself\n

So async def f(event) receives the event when passed directly, while async def f(data) must index into the list once wrapped in EventHandler. Converting a working handler just to add prevent_default breaks it in a way the traceback does not explain. Both forms are coroutines: EventHandler.__init__ and to_event_handler_function each run the value through check_is_async_handler, so a sync function or a lambda raises at element-build time. Common Props shows both shapes side by side.

"},{"location":"reference/protocol-structure/#the-event-proxy-round-trip","title":"The event proxy round trip","text":"

A handler which reads await event.target.value makes the server ask the browser. Layout.set_event_proxy_sender wires the backend to the connection, and the request that goes down is one per property (_event_proxy.py):

{\n  \"type\": \"event-proxy-request\",\n  \"requestId\": \"f5a9b645f0a145e984b7c2d8e19f753c\",\n  \"eventId\": \"lzk4q1-0\",\n  \"path\": [\"target\", \"value\"],\n  \"call\": false,\n  \"args\": [],\n  \"reply\": true,\n  \"maxDepth\": 10\n}\n

The two ids come from different sides and mean different things. \"requestId\" is a fresh uuid4().hex per request, minted by the server so it can match the answer to whoever asked. \"eventId\" is the client's own registry key for the object behind the handle \u2014 an opaque string such as \"lzk4q1-0\" from EventProxyRegistry.register, never a handler target \u2014 so build it from the handle you were given and do not try to guess one.

The client answers with the value, or with a failure it can name:

{\n  \"type\": \"event-proxy-response\",\n  \"requestId\": \"f5a9b645f0a145e984b7c2d8e19f753c\",\n  \"ok\": true,\n  \"value\": \"typed text\",\n  \"error\": null,\n  \"code\": null\n}\n

Two requests raised in the same synchronous stretch \u2014 a batch via await event.read(...), or an asyncio.gather of reads \u2014 are flushed together as {\"type\": \"event-proxy-request\", \"requests\": [ ... ]} and answered the same way with a \"responses\" array. Requests which genuinely depend on each other (await one, then the next) cost one round trip each.

A client that never answers does not hang the handler. After REACTPY_EVENT_PROXY_TIMEOUT seconds the read raises EventProxyTimeout. Strikes are counted by the connection's EventProxyBackend \u2014 one backend per layout, so one per connection \u2014 and one unanswered message costs one strike however many properties it was asking for; after REACTPY_EVENT_PROXY_MAX_TIMEOUTS consecutive unanswered round trips, live access is switched off for that connection and every later read raises EventProxyUnavailable at once rather than stalling. Binding a new sender \u2014 a reconnect \u2014 clears the strikes and gives the client another chance. REACTPY_EVENT_PROXY_MAX_DEPTH bounds how deep a returned object is expanded. See Configuring ReactPy.

The client is the one that decides what may be read. It refuses the members that would let a server reach outside the event \u2014 window, document, parentNode, eval, setTimeout, and friends (BLOCKED_MEMBERS in event-proxy.ts) \u2014 and it also refuses __reactpy_event__, __reactpy_ref__, and anything matching /^on[a-z]/i, so a handler cannot read the handle back off its own event or fish for another one. A refusal is an error on the response, not a value.

The client also decides how long an event stays readable, and it enforces that two different ways. An event which the server never once asked about is forgotten after LIVE_OBJECT_TTL_MS (60 s); one the server has read is exempt, because the handler may simply be slow. What bounds the page overall is MAX_LIVE_OBJECTS (512): past that, the event the server has gone longest without reading is dropped. Either way the id keeps working as an address and keeps explaining why it failed \u2014 \"not-found\" for one that aged out unread, \"evicted\" for one dropped to make room while this side was still reading it \u2014 which is the difference between EventProxyNotFound and EventProxyEvicted. An \"evicted\" points at the page rather than at your handler.

Build a non-Python client from this page and you must implement both proxy message types as well, or every await on an event raises. The stock client does, which is why a handler written against it works.

"},{"location":"reference/protocol-structure/#the-target-string","title":"The target string","text":"

\"/0/0/0/1:onClick\" is two parts joined by a colon:

  • Before the colon, the element's key path: one segment per level, where each segment is that child's key if it has one and its index otherwise (core/layout.py, _get_children_info and _make_element_model_state). Patch path\\ s, by contrast, are always positional (.../children/<index>). The two line up only while nobody sets a key \u2014 give a child {\"key\": \"row-3\"} and its target segment becomes row-3 while its patch path stays children/2.
  • After the colon, the attribute name the handler was declared under. It is whatever key you used, which is why {\"onMyThing\": f} produces a target ending in :onMyThing.

Treat the whole thing as an opaque handle. It is generated per render and is not meant to be constructed by hand; two structurally identical elements get distinct targets because their index paths differ.

Events for unmounted elements are dropped, not queued

If the server has no handler for a target \u2014 because the element was removed since the client last rendered, or the target was mistyped \u2014 the event is discarded with an informational log and the socket stays open:

Ignored event - handler '/99/99:nope' does not exist or its component unmounted\n

ReactPy retries the lookup three times at 10 ms intervals first, to cover a handler that is briefly absent mid-render. That window is a race guard, not a queue: nothing is replayed later. A click that happens while the socket is down is held in a client-side send buffer and flushed if the connection reopens \u2014 but once REACTPY_RECONNECT_MAX_RETRIES is exhausted the client stops trying, and everything buffered stays unsent.

"},{"location":"reference/protocol-structure/#seq-and-typed-input","title":"seq and typed input","text":"

Typing needs a protocol answer that a plain request/response does not give. Each keystroke sends an event carrying a counter the client incremented for that element. The server records the highest number it has finished handling, per event name for that target, and on the next render of an <input>, <select>, or <textarea> stamps that whole mapping into the element's attributes under _reactpy_ack_seq \u2014 not one number for the element, because a single counter would let one event's progress stand in for another's (core/layout.py, _inject_event_ack_seq):

{\n  \"type\": \"layout-update\",\n  \"path\": \"/children/0/children/0\",\n  \"model\": {\n    \"tagName\": \"\",\n    \"children\": [\n      {\n        \"tagName\": \"input\",\n        \"attributes\": {\"value\": \"hello world\", \"_reactpy_ack_seq\": {\"onInput\": 1}},\n        \"eventHandlers\": {\n          \"onInput\": {\n            \"target\": \"/0/0/0:onInput\",\n            \"preventDefault\": false,\n            \"stopPropagation\": false\n          }\n        }\n      }\n    ]\n  }\n}\n

The client compares that number against its own counter to decide whether the server has caught up with the latest keystrokes. If the server is still behind, the client keeps what the user typed instead of overwriting the box with a stale value. Without it, a slow handler erases text mid-word \u2014 the classic complaint about server-rendered inputs.

The value lives in a dedicated attribute rather than among your own so it cannot collide with, or leak into, a real DOM attribute. It is added only to those three tags, and only once at least one event has arrived for the element.

Debounce and throttle do travel on the wire

They change when events leave the browser, not what is in them \u2014 but the layout still has to tell the client about them. core/layout.py, _serialize_event_handler copies both into the handler's model entry whenever they are not None, and vdom.tsx reads them back off that entry. @event(handler, debounce=250, throttle=500) arrives as:

{\n  \"onClick\": {\n    \"target\": \"/0:onClick\",\n    \"preventDefault\": false,\n    \"stopPropagation\": false,\n    \"debounce\": 250,\n    \"throttle\": 500\n  }\n}\n

Both are omitted entirely when None, which is what keeps the common case minimal. On arrival the client validates each with isValidDebounce \u2014 finite, non-negative integer \u2014 and an invalid one is dropped with a console.warn naming the handler rather than an error, leaving the handler live but un-debounced. Build a non-Python client from this page and you must carry these two fields; skipping them silently loses the throttling. See Common Props.

One further key may appear in that entry, and it is new with this release: \"stopImmediatePropagation\". It is a boolean like its two neighbours, but unlike preventDefault and stopPropagation \u2014 which are always sent \u2014 it is omitted rather than sent as false: _serialize_event_handler in core/layout.py adds the key only when the handler asked for it, so read its absence as false. @event(stop_immediate_propagation=True), or a detected event.stopImmediatePropagation() in the handler's body, is what puts it there. The client then calls stopImmediatePropagation() on the event before running the handler, so the other listeners on that same element are skipped too (vdom.tsx).

"},{"location":"reference/protocol-structure/#watching-it-yourself","title":"Watching it yourself","text":"

The protocol is plain JSON text frames, so an ordinary websocket client is a sufficient observer. Start this app:

main.py Run
from starlette.applications import Starlette\nfrom starlette.responses import HTMLResponse\nfrom starlette.routing import Route\n\nfrom reactpy import component, html, use_state\nfrom reactpy.executors.asgi import ReactPyMiddleware\nfrom reactpy.executors.utils import server_side_component_html\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def increment(_event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(f\"count is {count}\"),\n        html.button({\"onClick\": increment}, \"Add\"),\n    )\n\n\nasync def home(request):\n    return HTMLResponse(\n        \"<!doctype html>\"\n        \"<html><body>\"\n        + server_side_component_html(\"root\", \"\", \"main.counter/\")\n        + \"</body></html>\"\n    )\n\n\nmy_app = ReactPyMiddleware(\n    Starlette(routes=[Route(\"/\", home)]),\n    root_components=[\"main.counter\"],\n)\n
# TODO\n

Then read the mount message, send a click, and read the patch:

client.py Run
import asyncio\nimport json\n\nimport websockets\n\nURL = \"ws://127.0.0.1:8000/reactpy/main.counter/?path=%2F\"\n\n\nasync def main():\n    patches = asyncio.Queue()\n\n    async with websockets.connect(URL) as socket:\n        mount = json.loads(await socket.recv())\n        print(\"1. the mount update:\", mount[\"path\"] or \"(root)\")\n        print(json.dumps(mount[\"model\"], indent=2))\n\n        target = find_handler_target(mount[\"model\"])\n        print(\"\\n2. sending a click to\", target)\n        await socket.send(\n            json.dumps(\n                {\n                    \"type\": \"layout-event\",\n                    \"target\": target,\n                    # The client keeps the DOM event and sends a handle, not a copy\n                    # of it. \"type\" is the one plain value sent along up front.\n                    \"data\": [\n                        {\"__reactpy_event__\": {\"id\": \"lzk4q1-0\"}, \"type\": \"click\"}\n                    ],\n                    \"seq\": 1,\n                }\n            )\n        )\n\n        # One reader for the whole connection: answer proxy requests as they arrive\n        # and hand everything else to the caller. Two consumers on one websocket\n        # would race for each frame. With a handler which only calls a state setter\n        # \u2014 the counter this observes \u2014 no proxy request is ever sent, and this task\n        # simply forwards patches.\n        reading = asyncio.create_task(read(socket, patches))\n        try:\n            patch = await asyncio.wait_for(patches.get(), timeout=10)\n            print(\"\\n3. the reply is a patch at path:\", repr(patch[\"path\"]))\n            print(json.dumps(patch[\"model\"], indent=2))\n        finally:\n            reading.cancel()\n\n\nasync def read(socket, patches):\n    async for raw in socket:\n        message = json.loads(raw)\n        if message.get(\"type\") == \"event-proxy-request\":\n            await answer_requests(socket, message)\n        else:\n            await patches.put(message)\n\n\nasync def answer_requests(socket, message):\n    \"\"\"Reply to every property path the server is asking about\"\"\"\n    requests = message.get(\"requests\", [message])\n    for request in requests:\n        print(\"   the server asks for\", request[\"path\"])\n    if \"requests\" in message:\n        await socket.send(\n            json.dumps(\n                {\n                    \"type\": \"event-proxy-response\",\n                    \"responses\": [answer(request) for request in requests],\n                }\n            )\n        )\n    else:\n        await socket.send(json.dumps(answer(requests[0])))\n\n\ndef answer(request):\n    \"\"\"The value behind one property path, in the shape the client must use\"\"\"\n    return {\n        \"type\": \"event-proxy-response\",\n        \"requestId\": request[\"requestId\"],\n        \"ok\": True,\n        \"value\": \"typed text\" if request[\"path\"][-1] == \"value\" else None,\n        \"error\": None,\n        \"code\": None,\n    }\n\n\ndef find_handler_target(model):\n    \"\"\"Return the target of the first event handler found in a model.\"\"\"\n    if isinstance(model, dict):\n        for spec in (model.get(\"eventHandlers\") or {}).values():\n            return spec[\"target\"]\n        for child in model.get(\"children\", []):\n            found = find_handler_target(child)\n            if found:\n                return found\n    return None\n\n\nasyncio.run(main())\n
# TODO\n

Both were run against each other, which is where the mount message, the target, and the patch above come from. The counter's handler only calls a setter, so it never reads a property and the proxy messages never appear \u2014 the observer answers them, and gets nothing to answer, which is the \"a read you never make costs nothing\" rule from Responding to Events showing up on the wire. The two proxy frames further up the page were captured from a handler which does read, against the same middleware. Nothing about the protocol is private to ReactPy's client \u2014 given a mount element and a correct target, you can drive a server-rendered component from any language.

The server trusts the client completely

Nothing checks that target names a real handler, that data describes a real event, or that the sender is the page that mounted the component. Whatever your handler reads out of data, it read because the browser asserted it. Treat it as user input: await event.target.value is a string an attacker chose, at any length. The one thing which does not travel in the message at all is the ASGI scope: the server built that itself when the socket was accepted, which is why Use Connection and User Scope are the trusted side of the picture \u2014 even there, remember that the headers and peer address it reports are what the client chose to send.

The proxy did not weaken this; it moved the trust question rather than answering it. EventProxyBackend._route takes \"value\" from a response verbatim \u2014 no schema, no type check \u2014 so a crafted client can make any read return anything, including a list where a str was expected. And a call: true request runs a method in the page, which is why the client, not the server, holds the veto: BLOCKED_MEMBERS and the /^on[a-z]/i rule are the page's defence against a hostile server. Read that as what it is \u2014 the two sides do not trust each other, and only the page defends the page. Anything the client volunteers without being asked (\"type\") is the same kind of assertion as the rest of data.

"},{"location":"reference/protocol-structure/#failure-modes","title":"Failure modes","text":"Symptom Cause Handshake refused, [ReactPy] Failed to connect! Missing trailing slash in componentPath, or a path_prefix that differs between the bootstrap and the server. Socket opens then closes; server logs RuntimeError: Attempting to use an unregistered root component <path>. The dotted path is not in root_components. The socket is accepted before the component is resolved, so the failure is visible only in the server log. Server log: Unknown message type: <type> Anything other than \"layout-event\" or \"event-proxy-response\". The server accepts those two and drops the rest, leaving the connection open. Server log: Ignored event - handler ... does not exist or its component unmounted Stale or wrong target. Console: Received message without type or Received message without handler A frame the client has no handler for. Ignored; the connection stays up. A <pre> with an exception message appears in the page The component raised. See Errors arrive as a model. Component renders once and never updates The handler never fires \u2014 usually a snake_case event name such as on_click, so no event is ever sent. [ReactPy] Disconnected! then Reconnecting in \u2026 The socket dropped and backoff is in effect. If Connection max retries exhausted! follows, the page needs a reload."},{"location":"reference/protocol-structure/#related","title":"Related","text":"
  • Common Props \u2014 the attribute names that become eventHandlers and inlineJavaScript.
  • Usage \u2014 writing the model by hand as a plain dict.
  • ReactPyMiddleware \u2014 the paths the server claims, and the bootstrap that opens the socket.
  • Use Connection and Use Location \u2014 the server-side view of the two query parameters.
"},{"location":"reference/react-calls-components-and-hooks/","title":"React calls Components and Hooks","text":""},{"location":"reference/react-calls-components-and-hooks/#overview","title":"Overview","text":"

You do not call a component or a hook \u2014 ReactPy does. Understanding who calls what explains why components must be pure, why hook order matters, and why calling a component yourself almost never does what you meant.

@component\ndef counter():\n    count, set_count = use_state(0)  # ReactPy runs this, not you\n    return html.h1(count)\n

Writing counter() does not run the function. And use_state does not work from anywhere you like \u2014 it works only while ReactPy is in the middle of rendering the component that called it.

"},{"location":"reference/react-calls-components-and-hooks/#reference","title":"Reference","text":""},{"location":"reference/react-calls-components-and-hooks/#reactpy-calls-components","title":"ReactPy calls components","text":"

@component turns a function into a constructor. Calling it returns a Component object \u2014 a description of \"render this function with these arguments\", to be rendered later, by the layout:

>>> type(counter())\n<class 'reactpy.types.Component'>\n

Component is defined in reactpy/types.py; core/component.py only imports it to build these objects, so reactpy.core.component.Component is an alias, not the class's own module.

The layout is what invokes your function body, at times it decides:

ReactPy calls your component When On mount The first time it appears in the tree On state change A use_state or use_reducer in that component is updated On parent render Whenever the component that renders it re-renders On an event Indirectly \u2014 your handler runs, and any state it sets schedules a render

Two consequences follow immediately.

You do not control how often it runs. A parent re-render re-renders its children, and ReactPy has no memo bailout for components. Code that counts, logs, or accumulates in a component body accumulates faster than the number of user actions. Measured: one press of a parent's button produced exactly one render of the parent and one of an unrelated child \u2014 but nothing guarantees those counts match your mental model once several state updates are in play.

Calling it yourself does not render it. Two mistakes, both silent:

html.div(None, my_component)  # passing the function, not calling it\nhtml.div(None, my_component())  # correct: an instance to be rendered\n

The first is accepted, and the function's repr becomes a text child \u2014 the page shows something like <function my_component at 0x7f0d1b2c4e00>. There is no error, because a callable that is not a known child type is stringified like a number would be. If your page ever displays a hex address, this is why.

Calling .render() on an instance does not work either:

await my_component().render()\n
RuntimeError: No life cycle hook is active. Are you rendering in a layout?\n

Rendering needs a life cycle hook, which only a layout provides. To see what a component produces, put it in one:

import asyncio\n\nfrom reactpy.core.layout import Layout\n\n\ndef render_model(component):\n    async def go():\n        async with Layout(component) as layout:\n            return (await layout.render())[\"model\"]\n\n    return asyncio.run(go())\n
"},{"location":"reference/react-calls-components-and-hooks/#components-are-values","title":"Components are values","text":"

An instance is an ordinary Python object, so you can build one in a loop or pass it as an argument \u2014 the way ReactPy expects you to compose:

html.ul(None, [row(item) for item in items])\n

Each call creates a fresh instance, and each gets its own state. Reusing one instance in two positions in the tree is the thing to avoid: build a new one per position, and key them when they come from a loop.

"},{"location":"reference/react-calls-components-and-hooks/#key-is-reserved","title":"key is a reserved parameter","text":"

A component constructor accepts an optional key keyword, which the layout uses for reconciliation:

[row(item, key=item[\"id\"]) for item in items]\n

Because of that, key is reserved as a component parameter name, and a component that declares one is rejected at import time \u2014 before anything renders:

@component\ndef row(key):  # do not do this\n    return html.li(key)\n
TypeError: Component render function <function row at 0x...> uses reserved parameter 'key'\n

Name the argument something else \u2014 row_key, label, item_id \u2014 and pass the reconciliation key as key= at the call site.

Note this differs from plain elements, where the key lives inside the attributes dict. See Common Props.

"},{"location":"reference/react-calls-components-and-hooks/#hooks-are-called-by-reactpy","title":"Hooks are called by ReactPy, during a render","text":"

A hook is a plain function that reads the current life cycle hook off a task-local stack. That is why it raises outside a render \u2014 and in several places that look like renders:

You call a hook from Result A component body Works \u2014 the intended place A custom hook called from a component body Works, and is how you build one Module level, directly RuntimeError: No life cycle hook is active. Are you rendering in a layout? await component().render() The same RuntimeError An event handler The same RuntimeError, logged on the server as Failed to execute event handler ... An use_effect callback body The same RuntimeError, logged on the server as Error in effect \u2014 a different log line, from a different place

The last two are the trap, and they look alike from the browser and nothing like each other in the log. Calling use_state from inside an event handler does not crash the app; it kills that handler, and the traceback goes to the server log:

Failed to execute event handler EventHandler(...)\n...\nRuntimeError: No life cycle hook is active. Are you rendering in a layout?\n

That line comes only from core/layout.py, _process_event_queue \u2014 delivered browser events. An effect body is a task, not a delivered event, so it fails through core/_life_cycle_hook.py, affect_component_will_unmount, and you see Error in effect instead. Search the log for both strings, not one.

Nothing changes in the browser, so the symptom is \"the button does nothing\". Move the hook call to the component body and close over the value instead.

"},{"location":"reference/react-calls-components-and-hooks/#usage","title":"Usage","text":""},{"location":"reference/react-calls-components-and-hooks/#the-mental-model","title":"The mental model","text":"You ReactPy Define what to render Decide when to render Return VDOM from a component Call the component Declare state with a hook Store it, per component instance Write an event handler Run it, when the event arrives Declare dependencies Compare them, and decide whether to re-run an effect

Everything in the Rules of React follows from the left column being yours and the right column being ReactPy's. You cannot ask ReactPy to render less often, so you write components that are safe to render often.

"},{"location":"reference/react-calls-components-and-hooks/#a-custom-hook-is-a-components-helper-not-a-standalone-function","title":"A custom hook is a component's helper, not a standalone function","text":"

Because a hook is called during a render, a custom hook is only usable from a render:

def use_cart():\n    items, set_items = use_state([])  # fine: runs during the caller's render\n    return items, add_item\n

Calling use_cart() from an event handler, a utility function, or a background task raises the same RuntimeError. If logic must be callable from there, write it as a normal function that takes what it needs as arguments.

"},{"location":"reference/react-calls-components-and-hooks/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/react-calls-components-and-hooks/#a-hex-address-appeared-on-the-page","title":"A hex address appeared on the page","text":"

You passed a component instead of calling it: html.div(None, my_component). Add the parentheses.

"},{"location":"reference/react-calls-components-and-hooks/#runtimeerror-no-life-cycle-hook-is-active-are-you-rendering-in-a-layout","title":"RuntimeError: No life cycle hook is active. Are you rendering in a layout?","text":"

A hook ran with no render in progress. Usual causes: called from an event handler, from an effect body, from a helper that a handler calls, or by awaiting .render() yourself. Read the server log for the traceback \u2014 the browser will not show it.

"},{"location":"reference/react-calls-components-and-hooks/#typeerror-uses-reserved-parameter-key","title":"TypeError: ... uses reserved parameter 'key'","text":"

A component parameter is named key. Rename it, and pass the reconciliation key as key= at the call site.

"},{"location":"reference/react-calls-components-and-hooks/#a-button-does-nothing-and-the-browser-shows-no-error","title":"A button does nothing, and the browser shows no error","text":"

The handler raised on the server. Handlers that call hooks fail this way. Check the server log.

"},{"location":"reference/react-calls-components-and-hooks/#related","title":"Related","text":"
  • Rules of React \u2014 the section overview
  • Components and Hooks must be pure \u2014 what these calling rules require of your code
  • Rules of Hooks \u2014 the ordering rule, and what breaking it does in ReactPy
  • Your First Component \u2014 writing your first component
  • Responding to Events \u2014 where handler code may and may not reach
"},{"location":"reference/reactpy-csr/","title":"ReactPyCsr","text":""},{"location":"reference/reactpy-csr/#overview","title":"Overview","text":"

ReactPyCsr serves a page that runs your component code in the browser via PyScript. There is no websocket: the component renders entirely client-side.

from reactpy.executors.asgi import ReactPyCsr\n\nmy_app = ReactPyCsr(\"root.py\", initial=\"Loading...\")\n
"},{"location":"reference/reactpy-csr/#reference","title":"Reference","text":""},{"location":"reference/reactpy-csr/#reactpycsrfile_paths-settings","title":"ReactPyCsr(*file_paths, **settings)","text":"
from reactpy.executors.asgi import ReactPyCsr\n
"},{"location":"reference/reactpy-csr/#parameters","title":"Parameters","text":"
  • *file_paths: One or more paths to Python files containing components. Their source is inlined into the served HTML. Accepts str or pathlib.Path.

    Read once, then cached for the life of the process: executors/pyscript/utils.py, fetch_cached_python_file is decorated with functools.cache, keyed on the path. Editing the file does not change the page on reload \u2014 restart the server. The same call minifies the source before inlining \u2014 # comments and inner docstrings are stripped, the module docstring is not \u2014 so what reaches the browser is not quite what you wrote (see Jinja).

  • root_name (optional): The name of the root component inside your Python file. Defaults to \"root\". The file may define any number of components; this one is mounted.

    ReactPyCsr(\"components.py\", root_name=\"my_app\")\n

    The name must match exactly, and the object must be a component.

  • initial (optional): Content shown while PyScript boots. A string or a VdomDict. Defaults to \"\".

  • extra_py (optional): Additional Python packages to install into the PyScript runtime, as version specifier strings.

  • extra_js (optional): Additional JavaScript modules, as a dict[str, str] where each key is the URL of the JavaScript file and the value is the name to export it as \u2014 {\"/static/foo.js\": \"foo\"}. The name is how the module is reached from the component, via pyscript.js_modules.foo. Same direction as the js_modules.main block PyScript itself consumes.

  • pyscript_config (optional): Raw PyScript configuration dictionary.

  • http_headers, html_head, prepend_body, html_lang: Same meaning as in ReactPy.

  • **settings: Any key from ReactPyConfig.

"},{"location":"reference/reactpy-csr/#returns","title":"Returns","text":"

An ASGI application.

"},{"location":"reference/reactpy-csr/#usage","title":"Usage","text":""},{"location":"reference/reactpy-csr/#files","title":"Files","text":"

Two files: the ASGI app, and the root component.

main.pyroot.py Run
my_app = ReactPyCsr(\n    Path(__file__).parent / \"root.py\",\n    initial=\"Loading...\",\n    root_name=\"root\",\n)\n
from reactpy import component, html, use_state\n\n\n@component\ndef root():\n    count, set_count = use_state(0)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n

Serve with uvicorn main:my_app. The served page carries the Loading... placeholder, the PyScript bootstrap, and your component source.

Your component source is public

ReactPyCsr embeds the text of every file you pass into the HTML response, so the browser can execute it. Verified: requesting the index route returned the full body of the component file, including components that are not mounted.

Treat these files as client-side code. Do not read secrets, query a database, or call privileged APIs from a CSR component \u2014 anything reachable from that file is reachable by anyone who loads the page. CSR is for genuinely client-side work: offline apps, demos, UI that needs no server authority. For anything that touches data you do not publish, use ReactPy or ReactPyMiddleware, which keep your Python on the server.

"},{"location":"reference/reactpy-csr/#how-it-loads","title":"How it loads","text":"

The page ReactPy generates does three things:

  1. Links PyScript's CSS and module script from {path_prefix}static/.
  2. Declares what PyScript should fetch: a packages list for Python \u2014 ReactPy's own wheel from {path_prefix}static/wheels/ and jsonpointer==3.* \u2014 and a separate js_modules entry for morphdom, served from {path_prefix}static/morphdom/morphdom-esm.js. They are different lists with different installers; morphdom is not a packages entry.
  3. Inlines your Python, then runs it under PyScript.

Verified against a running server: the initial response carries the PyScript core.js, \"packages\": [\"/reactpy/static/wheels/reactpy-2.0.0b13-py3-none-any.whl\", \"jsonpointer==3.*\"], \"js_modules\": {\"main\": {\"/reactpy/static/morphdom/morphdom-esm.js\": \"morphdom\"}}, and the initial placeholder inside a <div class=\"pyscript\">. pyscript_config lets you replace either list.

The first load is slow

The browser downloads and installs a CPython runtime plus your packages before anything renders. That is what initial is for. pyscript_config and extra_py let you pin packages, and packages_cache controls re-download, but the cold start is inherent to running Python in the browser.

"},{"location":"reference/reactpy-csr/#choosing-csr-over-the-other-executors","title":"Choosing CSR over the other executors","text":"Where your Python runs Needs a websocket Source visible to the client ReactPy server yes no ReactPyMiddleware server yes no ReactPyCsr browser no yes

Pick CSR when the client must work without a server connection. Pick one of the others whenever the component touches anything you would not publish.

PyScript without CSR

ReactPy accepts pyscript_setup=True to load PyScript alongside server-rendered components, for mixed setups. ReactPyCsr is the fully client-side case.

"},{"location":"reference/reactpy-csr/#related","title":"Related","text":"
  • Creating a Standalone React App
  • Adding ReactPy to an Existing Project \u2014 the Jinja template tags include a CSR variant.
"},{"location":"reference/reactpy-middleware/","title":"ReactPyMiddleware","text":""},{"location":"reference/reactpy-middleware/#overview","title":"Overview","text":"

ReactPyMiddleware adds ReactPy routes to an ASGI application you already have. Requests ReactPy does not recognise fall through to your app.

from reactpy.executors.asgi import ReactPyMiddleware\n\nmy_app = ReactPyMiddleware(existing_asgi_app, root_components=[\"main.component\"])\n
"},{"location":"reference/reactpy-middleware/#reference","title":"Reference","text":""},{"location":"reference/reactpy-middleware/#reactpymiddlewareapp-root_components-settings","title":"ReactPyMiddleware(app, root_components, **settings)","text":"
from reactpy.executors.asgi import ReactPyMiddleware\n
"},{"location":"reference/reactpy-middleware/#parameters","title":"Parameters","text":"
  • app: The ASGI application to serve when a request does not match a ReactPy route. Any ASGI app works \u2014 Starlette, FastAPI, or hand-written.

  • root_components: A list, set, or tuple of dotted paths to root components, e.g. [\"main.counter\"]. The path must be importable by Python's import system: module path, dot, attribute name. Resolution is module.rsplit(\".\", 1) followed by getattr, so it must name a module that is importable from the server's working directory.

  • **settings: Any key from ReactPyConfig. See Configuration. path_prefix is validated at construction and raises ValueError if malformed.

"},{"location":"reference/reactpy-middleware/#returns","title":"Returns","text":"

An ASGI application.

"},{"location":"reference/reactpy-middleware/#usage","title":"Usage","text":""},{"location":"reference/reactpy-middleware/#wrapping-an-existing-app","title":"Wrapping an existing app","text":"main.py Run
my_app = ReactPyMiddleware(starlette_app, root_components=[\"main.counter\"])\n
# TODO\n

The wrapped app is untouched. Verified with Starlette: a plain /healthz route still returned its text response through the middleware.

"},{"location":"reference/reactpy-middleware/#what-the-middleware-actually-claims","title":"What the middleware actually claims","text":"

ReactPyMiddleware.__call__ handles exactly three kinds of request and forwards everything else:

Path Handled by websocket matching {path_prefix}{dotted_path}/ ReactPy render stream {path_prefix}static/... ReactPy client bundle {path_prefix}modules/... built web modules anything else your app

There is no fourth row. ReactPyMiddleware.match_extra_paths is a hook that returns None \u2014 its own comment says custom routes are \"unused by default to encourage users to handle routing within your ASGI framework of choice\". The @my_app.route(...) registry people expect belongs to the standalone ReactPy executor, which overrides that method to match extra_http_routes / extra_ws_routes. Under the middleware, every route is your app's.

The middleware never serves your HTML

This is the part that surprises people. Registering a dotted path does not create a page. Requesting \"/\" falls through to your app, and there is no {path_prefix}{dotted_path}/ HTTP route either \u2014 that pattern is matched only when the ASGI scope type is \"websocket\". Verified: requesting /reactpy/main.counter/ over HTTP returns 404, while the websocket on the same path streams the model.

You produce the document yourself, by emitting an empty mount element and the client bootstrap that points at one registered component.

"},{"location":"reference/reactpy-middleware/#serving-a-component-from-your-own-route","title":"Serving a component from your own route","text":"main.py Run
async def home(request):\n    # The mount point plus the client bootstrap, for one registered component.\n    mount = server_side_component_html(\n        element_id=\"app\",\n        class_=\"\",\n        # Trailing slash required: the websocket route matches `<prefix><dotted_path>/`.\n        component_path=\"main.counter/\",\n    )\n    return HTMLResponse(f'<html><body><div id=\"wrapper\">{mount}</div></body></html>')\n\n\nstarlette_app = Starlette(routes=[Route(\"/\", home)])\n\nmy_app = ReactPyMiddleware(starlette_app, root_components=[\"main.counter\"])\n
# TODO\n

server_side_component_html emits the <div id=\"app\">, the import map fallback, and the mountReactPy(...) call. Verified in a real browser: the counter rendered into <div id=\"app\"> with [ReactPy] Connected! in the console.

The trailing slash in component_path is required

Pass \"main.counter/\", not \"main.counter\". The client opens ws://host/reactpy/<component_path>?path=/, and the server matches the websocket path against

^{path_prefix}(?P<dotted_path>[a-zA-Z0-9_.]+)/$\n

which requires the trailing slash. Verified both ways: without it the handshake fails with Unexpected response code and the console logs [ReactPy] Failed to connect!, leaving an empty div; with it, the component renders.

"},{"location":"reference/reactpy-middleware/#multiple-components","title":"Multiple components","text":"

Register several and choose per page. Each page mounts exactly one:

my_app = ReactPyMiddleware(\n    starlette_app,\n    root_components=[\"main.counter\", \"main.settings\", \"main.profile\"],\n)\n

Then emit a different component_path from each of your routes.

Unregistered paths are rejected

\"Attempting to use an unregistered root component <path>.\" is raised when a websocket asks for a dotted path you never listed. Register every component you intend to mount.

"},{"location":"reference/reactpy-middleware/#import-errors","title":"Import errors","text":"

A dotted path that cannot be imported fails at construction, with a message naming the part that failed:

ImportError: ReactPy failed to import \"main\"\nAttributeError: ReactPy failed to import \"counter\" from \"main\"\n

The first means the module was not found; the second means the module loaded but has no such attribute. ValueError is raised earlier still if the path has no dot at all.

"},{"location":"reference/reactpy-middleware/#choosing-an-executor","title":"Choosing an executor","text":"Goal Use Whole app is ReactPy, no existing server code ReactPy Add components to an existing ASGI app ReactPyMiddleware Run component code in the browser via PyScript ReactPyCsr

ReactPy is itself a subclass of ReactPyMiddleware that registers a single root and serves its own index document, which is why the standalone executor can do the HTML the middleware leaves to you.

"},{"location":"reference/reactpy-middleware/#related","title":"Related","text":"
  • Adding ReactPy to an Existing Project
  • ReactPy \u2014 the standalone executor.
"},{"location":"reference/reactpy/","title":"ReactPy","text":""},{"location":"reference/reactpy/#overview","title":"Overview","text":"

ReactPy is ReactPy's standalone ASGI application. It serves the HTML document, the client bundle, and the websocket through which components render.

from reactpy.executors.asgi import ReactPy\n\nmy_app = ReactPy(root_component)\n
"},{"location":"reference/reactpy/#reference","title":"Reference","text":""},{"location":"reference/reactpy/#reactpyroot_component-settings","title":"ReactPy(root_component, **settings)","text":"

Instantiate ReactPy once, at module level, and hand the instance to any ASGI server:

uvicorn main:my_app\n

ReactPy serves the page at every route that is not claimed by its own static or websocket paths.

from reactpy.executors.asgi import ReactPy\n
"},{"location":"reference/reactpy/#parameters","title":"Parameters","text":"
  • root_component: The component to mount. A component constructor \u2014 the @component function itself, not a call to it. This app is a single-page application: there is exactly one root.

  • html_head (optional): Extra content for the document's <head>.

    Must be built with html.head

    Pass html.head(...) \u2014 not a bare html.title(...) or a html.div(...). Anything else raises at request time:

    ValueError: Head element must be constructed with `html.head`.\n

    Verified: this surfaces as a 500 on the index route, not at import time, so a typo here only shows up once the server is running.

  • prepend_body (optional): Content rendered at the start of the <body>, before the mount point. Accepts a VdomDict built with html.*, or None to omit. Defaults to html.noscript(\"Enable JavaScript to view this site.\").

    This is the one part of the tree that is genuinely server-rendered. See what ReactPy does and does not render.

  • html_lang (optional): Value for the document's lang attribute. Defaults to \"en\".

  • http_headers (optional): Extra response headers for the index document \u2014 a plain dict[str, str]. Use it for CSP, X-Frame-Options, caching, and similar.

  • pyscript_setup (optional): Load PyScript into the document head, for running component code in the browser. Defaults to False. ReactPyCsr sets this for you.

  • pyscript_options (optional): A PyScriptOptions configuring that behaviour.

  • **settings: Any key from ReactPyConfig. Despite appearances these are not scoped to this app instance \u2014 each one is written to the matching global in reactpy.config, so the last executor constructed wins process-wide. See Configuration.

"},{"location":"reference/reactpy/#returns","title":"Returns","text":"

An ASGI application.

"},{"location":"reference/reactpy/#usage","title":"Usage","text":""},{"location":"reference/reactpy/#a-standalone-app","title":"A standalone app","text":"app.py Run
my_app = ReactPy(counter)\n
# TODO\n

Serve it with uvicorn main:my_app. The name you assign matters, because it is what uvicorn module:name refers to.

"},{"location":"reference/reactpy/#page-metadata-and-headers","title":"Page metadata and headers","text":"app.py Run
my_app = ReactPy(\n    page,\n    # Must be built with html.head(); any other tag raises ValueError.\n    html_head=html.head(\n        html.title(\"My ReactPy App\"),\n        html.meta({\"name\": \"description\", \"content\": \"A ReactPy app\"}),\n    ),\n    html_lang=\"fr\",\n    http_headers={\"X-Frame-Options\": \"DENY\"},\n    # Rendered into the <body> before the mount point, on the server.\n    prepend_body=html.header({\"id\": \"nav\"}, \"Server-rendered navigation\"),\n)\n
# TODO\n

Verified against a running server: lang=\"fr\", the custom <title>, the custom response header, and the prepend_body markup all appear in the initial HTML.

"},{"location":"reference/reactpy/#configuration","title":"Configuration","text":"

Settings are ordinary keyword arguments, and they reach the browser-side client:

app.py Run
my_app = ReactPy(\n    page,\n    path_prefix=\"/apps/reactpy/\",\n    reconnect_interval=500,\n    reconnect_max_interval=30000,\n    reconnect_max_retries=50,\n    async_rendering=True,\n    debug=False,\n)\n
# TODO\n
Setting Default Meaning path_prefix /reactpy/ Base path ReactPy serves its own static assets and websocket from. web_modules_dir temp dir Where built web modules are cached. reconnect_interval 750 Milliseconds before the first reconnect attempt. reconnect_max_interval 60000 Ceiling on the reconnect backoff. reconnect_max_retries 150 Attempts before giving up. reconnect_backoff_multiplier 1.25 Growth factor between attempts. async_rendering True Allow renders to overlap. debug False Server-side checks and richer error text \u2014 see the note below. max_queue_size 1000 Bound on the pending events of each handler target \u2014 the layout keeps a queue per target (core/layout.py, _event_queues), not one per connection. event_proxy_timeout 5.0 Seconds to wait for the browser to answer one live event read before EventProxyTimeout. event_proxy_max_depth 10 How deep one resolution may expand nested objects before they come back as references. event_proxy_max_timeouts 3 Consecutive unanswered round trips tolerated before live access is switched off for the connection. tests_default_timeout 15.0 Default timeout for reactpy.testing. Not settable this way \u2014 see the note below.

Verified: setting path_prefix=\"/apps/reactpy/\" moves asset serving to that prefix, and the reconnect values appear in the generated mount call. The three event_proxy_* settings were verified by constructing an executor with them and reading the matching reactpy.config options back.

They belong to a handler which reads the browser rather than to the client bootstrap: they bound the round trips behind await event.target.value. Reach for them only when a handler is timing out or a deeply nested read comes back as references \u2014 see Protocol Structure.

These keywords are process-wide, not per-app

executors/utils.py, process_settings walks the keywords and calls set_current() on the matching reactpy.config Option object. Those objects are module-level globals, so the last constructor to run wins for the whole process \u2014 including for any app built before it. Verified: two ReactPy instances created with reconnect_interval=111 and reconnect_interval=999 leave REACTPY_RECONNECT_INTERVAL.current at 999 for both. There is no per-app configuration layer; do not host two apps that need different values.

tests_default_timeout is worse than ineffective. Its Option is declared mutable=False in reactpy/config.py, and _option.py, set_current raises TypeError on an immutable option \u2014 so passing it raises Option(REACTPY_TESTS_DEFAULT_TIMEOUT=15.0) cannot be modified after initial load out of the constructor. Set it via the REACTPY_TESTS_DEFAULT_TIMEOUT environment variable before import, or not at all.

debug is not a client switch. REACTPY_DEBUG turns on server-side VDOM and JSON-attribute validation, populates the model's error text (without it, a failed component ships \"error\": \"\" and renders as nothing), and changes a few asset URLs to their ?dev forms. See Common Props.

Each setting also has an environment-variable twin in reactpy.config \u2014 REACTPY_PATH_PREFIX, REACTPY_RECONNECT_INTERVAL, and so on. Since the keyword already writes to that same global option, the two differ only in timing: the environment variable is read when the module is first imported, the keyword whenever an executor is constructed.

"},{"location":"reference/reactpy/#what-reactpy-does-and-does-not-render","title":"What ReactPy does and does not render","text":"

This is the distinction that trips people up, because the executor has historically been described as \"SSR\".

The initial HTML document contains an empty mount point:

<body>\n  <noscript>Enable JavaScript to view this site.</noscript>\n  <div id=\"app\" class=\"\"></div>\n  <script type=\"module\" crossorigin=\"anonymous\">\n    import { mountReactPy } from \"/reactpy/static/index.js\";\n    mountReactPy({ mountElement: document.getElementById(\"app\"), ... });\n  </script>\n</body>\n

Verified by fetching the index route: your root component's markup is not in the response. The browser connects over the websocket and ReactPy renders and streams from there.

What is server-rendered into the first response:

  • prepend_body, converted with reactpy_to_string.
  • html_head.
  • The document shell: lang, static asset paths, the mount script.

Practical consequences:

  • Search engines and curl see an empty app. For content that must exist without JavaScript, render it yourself \u2014 reactpy_to_string turns a component into an HTML string, which you can place in prepend_body or emit from your own route. It renders the component's first render directly, without a layout, so the component cannot use any hook: use_state, use_ref, use_effect, use_location and the rest all raise RuntimeError: No life cycle hook is active. Are you rendering in a layout?. Pass plain html.* elements, or a component that calls none of them.
  • There is no first-paint flash to optimise away, because there is no first paint to replace.
  • A component that fails on the server fails after the page loads, leaving a blank <div id=\"app\">. When the page loads but shows nothing, check the server log before the browser console.

Not the same as ReactPyCsr

ReactPyCsr is the one that runs component code in the browser via PyScript. ReactPy runs components on the server and sends VDOM over a websocket. Both need a server; they differ in where your Python executes. See ReactPyMiddleware for mounting into an existing ASGI app.

"},{"location":"reference/reactpy/#related","title":"Related","text":"
  • Creating a Standalone React App \u2014 the tutorial version of this page.
  • ReactPyMiddleware \u2014 mount components inside an existing ASGI application.
  • ReactPyCsr \u2014 run component code in the browser.
"},{"location":"reference/rules-of-hooks/","title":"Rules of Hooks","text":""},{"location":"reference/rules-of-hooks/#overview","title":"Overview","text":"

Call hooks at the top level of a component, in the same order, on every render. No loops, no conditions, no early returns, no nesting inside a function that runs later. ReactPy will not always tell you when you break this rule \u2014 in one case it corrupts state in silence.

@component\ndef form():\n    name, set_name = use_state(\"\")  # top level, every render, same order\n    email, set_email = use_state(\"\")\n    return html.form(name, email)\n

ReactPy identifies a hook's state by where it was called, not by what it is called into. The nth use_state in a component body gets the nth stored slot. Change which hooks run, and you change which slot each one reads.

"},{"location":"reference/rules-of-hooks/#the-rules","title":"The rules","text":"
  • Only call hooks from a component body or another hook \u2014 never from an event handler, an effect body, a plain function, or module level. See React calls Components and Hooks.
  • Call them at the top level \u2014 not inside if, for, try, a while, a comprehension, or a nested function that runs later.
  • Always call the same hooks in the same order \u2014 which hooks run may not depend on props, state, or anything else that can change between renders.
  • No early return before a hook \u2014 an early exit skips the hooks after it.

The one exception is use_context: it takes no slot at all (core/hooks.py reads the provider off the current hook and never touches the state list), so calling it conditionally shifts nothing \u2014 verified, a component whose use_context call runs on only some renders keeps _state at the same length throughout. It is still a hook and still needs an active render to call.

"},{"location":"reference/rules-of-hooks/#why-hooks-are-slots-not-names","title":"Why: hooks are slots, not names","text":"

ReactPy keeps a component's hook values in a plain list and hands out the next entry on each call \u2014 a running index, not a name. From reactpy/core/_life_cycle_hook.py, LifeCycleHook.use_state:

if not self._rendered_atleast_once:\n    # since we're not initialized yet we're just appending state\n    result = function()\n    self._state.append(result)\nelse:\n    # once finalized we iterate over each succesively used piece of state\n    result = self._state[self._current_state_index]\nself._current_state_index += 1\n

The first render appends one entry per hook call, in order. Every later render reads back by position. Nothing records the variable name, the hook's type, or what it stores \u2014 so position is identity.

That is why the rules are phrased as \"same order\". The component cannot tell ReactPy which value is which; only the sequence of calls says so.

"},{"location":"reference/rules-of-hooks/#what-breaking-the-rules-actually-does","title":"What breaking the rules actually does","text":"

This is where ReactPy and React part ways, and why the rules must be enforced by review rather than trusted to a loud failure.

"},{"location":"reference/rules-of-hooks/#more-hooks-than-the-first-render-a-crash","title":"More hooks than the first render: a crash","text":"

A component that mounts with one hook and later calls two tries to read a slot that was never appended:

@component\ndef grow():\n    n, set_n = use_state(0)\n\n    async def bump(event):\n        set_n(n + 1)\n\n    if n >= 1:  # a second hook appears only after the first click\n        extra, set_extra = use_state(\"EXTRA\")\n        return html.div(f\"n={n} extra={extra}\")\n    return html.div(f\"n={n}\", html.button({\"onClick\": bump}, \"bump\"))\n

With debug mode on, the component is replaced by an error node and the server logs the cause:

IndexError: list index out of range\n

With debug mode off, the error field is an empty string, so the region simply renders as nothing. This case fails loudly enough to find.

"},{"location":"reference/rules-of-hooks/#fewer-hooks-than-the-first-render-silent-state-corruption","title":"Fewer hooks than the first render: silent state corruption","text":"

This is the dangerous one. A hook call that disappears from above another one shifts every later index by one \u2014 and the shift is invisible:

@component\ndef shifting():\n    show, set_show = use_state(True)  # slot 0\n    if show:\n        hidden, set_hidden = use_state(\"HIDDEN\")  # slot 1, only while show\n    n, set_n = use_state(\"N\")  # slot 1 while show, slot 2 once it is off\n    return html.div(f\"show={show} n={n}\")\n

Measured, flipping show from True to False once (through its setter \u2014 this snippet has no button; the runnable broken_toggle below wires the same shape to a real Toggle button):

mount:          show=True  n=N\nafter toggle:   show=False  n=HIDDEN     <-- `n` is now reading `hidden`'s slot\n

No error, no warning, no traceback. n starts showing another hook's value and keeps doing so until the branches line up again. React, by contrast, throws Rendered fewer hooks than expected. A ported component can therefore be wrong in ReactPy where it was a crash in React.

The corruption usually surfaces later, somewhere that looks unrelated. With a counter that should hold an int, the shifted read hands a string to arithmetic, and the next click dies in the handler:

TypeError: can only concatenate str (not \"int\") to str\n

That traceback names the event handler, not the hook \u2014 so the hunt starts in the wrong file. The button stops working, and the error is on the server log only.

app.py Run
@component\ndef broken_toggle():\n    \"\"\"Do not copy this: a hook behind a condition that can change.\n\n    `hidden` occupies slot 1 only while `show` is true. Clicking Toggle makes\n    that call disappear, so every hook after it shifts up one slot and starts\n    reading somebody else's value -- `count` begins showing the string that\n    belongs to `hidden`. No error is raised, on the server or in the browser.\n    \"\"\"\n    show, set_show = use_state(True)  # slot 0\n\n    if show:\n        hidden, set_hidden = use_state(\"NOT-A-NUMBER\")  # slot 1, conditionally\n\n    count, set_count = use_state(0)  # slot 1 when `show` is False\n\n    async def handle_show_click(event):\n        set_show(not show)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.p(f\"show={show}\"),\n        html.button({\"onClick\": handle_show_click}, \"Toggle\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n\n\n@component\ndef fixed_toggle():\n    \"\"\"The same component with the hook order held constant.\n\n    Every hook runs on every render, so the slots stay aligned with the\n    variables they belong to. Branching happens in the *return value*, which is\n    what rendering is for.\n    \"\"\"\n    show, set_show = use_state(True)\n    hidden, set_hidden = use_state(\"NOT-A-NUMBER\")\n    count, set_count = use_state(0)\n\n    async def handle_show_click(event):\n        set_show(not show)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.p(f\"show={show} hidden={hidden}\"),\n        html.button({\"onClick\": handle_show_click}, \"Toggle\"),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n    )\n
# TODO\n

In broken_toggle, Add 1 works, then Toggle makes the heading read NOT-A-NUMBER, and the next Add 1 does nothing at all \u2014 the handler raised TypeError on the server. fixed_toggle is the same component with the hooks moved out of the condition: both of its buttons keep working through every toggle.

"},{"location":"reference/rules-of-hooks/#a-hook-in-a-loop-frozen-values-then-a-crash","title":"A hook in a loop: frozen values, then a crash","text":"
@component\ndef rows(n):\n    out = []\n    for i in range(n):\n        value, set_value = use_state(f\"v{i}\")\n        out.append(value)\n    return html.div(out)\n

Measured with n going 3 \u2192 2 \u2192 1 \u2192 0:

vals=['v0', 'v1', 'v2']\nvals=['v0', 'v1']\nvals=['v0']\nvals=[]\n

It looks correct. It is not: use_state(f\"v{i}\") applies its initial value only on the first render, so the i in it is decoration, and the list's length is driven by an argument while its contents come from frozen slots. Growing n past its first-render value raises the IndexError from the first case. A loop of hooks is wrong either way \u2014 build one component per item, which gives each its own state legitimately:

html.div(None, [row(item, key=item[\"id\"]) for item in items])\n
"},{"location":"reference/rules-of-hooks/#an-early-return-before-a-hook","title":"An early return before a hook","text":"
@component\ndef loading():\n    n, set_n = use_state(0)\n    if n == 2:\n        return html.div(\"early\")  # skips the hook below\n    label, set_label = use_state(\"LABEL\")\n    return html.div(f\"{n} {label}\")\n

ReactPy does not crash, and label survives while the branch is away. But this is the same slot-shifting hazard as above, arriving whenever the condition flips. Put the early return after every hook, or move the conditional content into a child component:

@component\ndef loading():\n    n, set_n = use_state(0)\n    label, set_label = use_state(\"LABEL\")  # all hooks first\n    if n == 2:\n        return html.div(\"early\")\n    return html.div(f\"{n} {label}\")\n

Tolerance is not safety

ReactPy lets several of these through; React does not. The case above corrupts state without a word, and the same code raises the moment it is ported, tested against React semantics, or read by someone who assumes React's guarantees. Treat the rules as absolute.

"},{"location":"reference/rules-of-hooks/#custom-hooks-follow-the-same-rules","title":"Custom hooks follow the same rules","text":"

A custom hook is only a function, so nothing checks it:

def use_auth(token):\n    if not token:\n        return None, None  # hooks skipped on some calls\n    user, set_user = use_state(None)\n    return user, set_user\n

Call it with a falsy token and the caller silently loses a slot \u2014 measured, the happy path consumes one hook slot and the falsy path consumes none. Return early only after every hook has run, or split into two hooks and choose between them at the call site, where the choice is visible in the component body:

@component\ndef page(token):\n    return authed(token) if token else anonymous()\n

Because a hook's name is all a reviewer has, name custom hooks use_* and call them only from component bodies or other hooks.

"},{"location":"reference/rules-of-hooks/#catching-violations","title":"Catching violations","text":"

There is a linter: reactpy-flake8, the plugin this repository pins (reactpy-flake8 >=0.7 in pyproject.toml, select = [\"RPY\"]). Its RulesOfHooksVisitor is the working equivalent of eslint-plugin-react-hooks for the call-site rule, and it reports:

Code Catches REACTPY100 a hook defined as a closure inside another function REACTPY101 a hook used outside any component or hook REACTPY102 a hook used inside an if / if expression / try / match / for / while REACTPY103 a hook used after an early return

Run it as part of flake8 (the repo config selects only its RPY codes), or drive it directly:

import ast\nfrom reactpy_flake8.run import run_checks\n\nfor line, col, msg in run_checks(ast.parse(source), exhaustive_hook_deps=False):\n    print(line, col, msg)\n

Verified against a component with a hook after an early return and a hook inside an if: it reports exactly REACTPY103 hook 'use_state' used after an early return on line 7 and REACTPY102 hook 'use_state' used inside if statement. Pass exhaustive_hook_deps=True to add the dependency-shape checks (REACTPY200\u2013REACTPY202), which cover far less than people expect \u2014 see Lifecycle of Reactive Effects.

What the linter cannot do is check the order rule, because order is a runtime property. There is still no runtime check, so the two habits remain:

  • Run with REACTPY_DEBUG=1 during development, so a hook-count increase becomes a visible error instead of an empty region.
  • Treat any use_ call that appears below the first non-hook statement of a component body, or inside an if / for / try / nested def, as a review defect \u2014 and wire reactpy-flake8 into CI so you are not the only one who spots it.

The linter also matches on names: it flags anything whose name fits ^_*use_\\w+ and treats a function as a component only if decorated with something matching ^(component|[\\w.]+\\.component)$. Rename the decorator or the hook and the checks silently stop applying; the patterns are configurable via --component-decorator-pattern and --hook-function-pattern.

"},{"location":"reference/rules-of-hooks/#reference","title":"Reference","text":"Pattern ReactPy behaviour Verdict Hook at component top level Works Do this Hook in a custom hook called from a body Works Do this Hook in an if that never changes Works by accident Broken Hook in an if that can change Fewer hooks: silent state corruption. More hooks: IndexError Broken Hook in a for Frozen initial values; IndexError if the loop grows Broken Hook after an early return Slot shift when the branch flips Broken Hook in an event handler RuntimeError, server log only Broken Hook in an effect body RuntimeError, server log only Broken Hook at module level RuntimeError: No life cycle hook is active. Broken"},{"location":"reference/rules-of-hooks/#related","title":"Related","text":"
  • Rules of React \u2014 the section overview
  • Components and Hooks must be pure \u2014 the other half of the contract
  • React calls Components and Hooks \u2014 who runs a hook, and when
  • Use State, Use Effect, Use Memo \u2014 the hooks these rules govern
  • Use Callback, Use Debug Value \u2014 hooks worth writing your own wrappers around
"},{"location":"reference/rules-of-react/","title":"Overview","text":""},{"location":"reference/rules-of-react/#overview","title":"Overview","text":"

Two rules make ReactPy predictable: components and hooks must be pure, and ReactPy must be the one that calls them. They look like style advice. They are load-bearing \u2014 and in ReactPy, one of them is not enforced by anything except you.

Rule In one line Read Purity A component or hook returns the same output for the same inputs, and changes nothing outside itself. Components and Hooks must be pure Who calls what You write components and hooks; ReactPy calls them \u2014 always from the top level of a component, in the same order. React calls Components and Hooks, Rules of Hooks

The two are one idea seen from either side. ReactPy is free to call your component whenever it likes, as often as it likes, because purity makes that safe. In return, your component gets to assume nothing about when it ran \u2014 which is why state lives in hooks rather than in variables.

"},{"location":"reference/rules-of-react/#why-the-rules-exist","title":"Why the rules exist","text":"

ReactPy renders on a server, inside one Python process that serves many connections, and re-renders a component whenever its state changes or its parent renders. Both facts are what the rules protect against.

  • Impure rendering leaks between users. A module-level dict written during a render is shared by every connection in the process. Three visitors, one cart. No error, and each page looks fine. Measured in Components and Hooks must be pure.
  • Render frequency is not yours to predict. Code that counts, accumulates, or logs in a component body runs more often than the user acts. A child re-renders every time its parent does \u2014 ReactPy has no memo bailout for components.
  • Hook state is positional. ReactPy hands out hook values from a list by index, so changing which hooks run changes which value each hook reads. The usual outcome is not an error but a component quietly reading its neighbour's state. See Rules of Hooks.

The last point is the one that surprises people coming from React. React's hook dispatcher validates call order and throws; ReactPy's does not. Several violations are tolerated silently, and one of them corrupts state with nothing logged anywhere. The rules are still exactly React's \u2014 the safety net underneath them is not.

"},{"location":"reference/rules-of-react/#the-rules-as-a-checklist","title":"The rules, as a checklist","text":"

For a component body:

  • reads only its arguments, and values from its own hooks
  • assigns no global or module-level name
  • mutates no object it did not create during this render
  • reads no clock, random, environment, or I/O
  • starts no task, thread, or timer
  • may freely define event handlers \u2014 they run later, and may do all of the above

For a hook call site:

  • called from a component body or another hook \u2014 never an event handler, effect body, or plain function
  • at the top level of that body: not under if, for, try, and not inside a nested def that runs outside the render (an event handler, an effect callback). A nested def whose name starts with use_ and which the body calls during the render is a custom hook, and calling hooks in it is the point.
  • the same hooks, in the same order, on every render
  • no early return before a hook

use_context is the one hook that takes no slot, so it alone may be called on only some renders without shifting the others. See Rules of Hooks.

For a custom hook (use_*): everything above, plus it must not skip its own hooks on some calls.

"},{"location":"reference/rules-of-react/#what-reactpy-cannot-check-for-you","title":"What ReactPy cannot check for you","text":"

reactpy-flake8 covers the call-site rule at build time (see Rules of Hooks), but there is no runtime assertion for hook order, and that is the one that bites silently. Two settings get you most of the way:

  • REACTPY_DEBUG=1 turns a render failure into a visible error in the model instead of a blank region, and enables the VDOM checks described in Common Props.
  • Tests are cheap precisely because of the rules: a pure component is an ordinary function \u2014 call it inside reactpy.core.layout.Layout, render it twice, assert the models are equal. No browser involved.

Everything else is review. The greppable version: any use_ call that appears below the first non-hook statement of a component body, or inside a conditional, loop, or nested function, is a defect until proven otherwise.

"},{"location":"reference/rules-of-react/#at-a-glance","title":"At a glance","text":"You might be tempted to Instead Cache a computation in a module-level dict use_memo Keep a per-user counter in a global use_state or use_reducer Fetch data in the component body An event handler, or use_async_effect Fetch request headers with os.environ use_scope / use_location Call a hook from an event handler Call it in the body and close over the value Skip a hook when a prop is falsy Return early only after all hooks, or split into two components Loop over use_state One component per item, keyed Reach for use_effect to derive a value Compute it in the body"},{"location":"reference/rules-of-react/#related","title":"Related","text":"
  • Components and Hooks must be pure \u2014 the contract, the server-shared-state leak, where effects belong
  • React calls Components and Hooks \u2014 constructors versus calls, reserved key, where hooks may run
  • Rules of Hooks \u2014 slots, and the measured cost of breaking order
  • Keeping Components Pure \u2014 the tutorial version, with exercises
  • Your First Component \u2014 start here if you have not written one yet
"},{"location":"reference/usage/","title":"Usage","text":""},{"location":"reference/usage/#overview","title":"Overview","text":"

reactpy.html is how you write markup in ReactPy. It is an object whose attributes are tag constructors: html.div(...) builds a <div>, and there is no template language, no JSX, and no parser involved.

from reactpy import html\n\nhtml.div({\"class\": \"card\"}, html.h2(\"Title\"), html.p(\"Body\"))\n

That call produces a plain dict:

{\n    \"children\": [\n        {\"children\": [\"Title\"], \"tagName\": \"h2\"},\n        {\"children\": [\"Body\"], \"tagName\": \"p\"},\n    ],\n    \"attributes\": {\"class\": \"card\"},\n    \"tagName\": \"div\",\n}\n

reactpy.h is the same object under a shorter name \u2014 h is html is True \u2014 so use whichever reads better in a given file. Importing both and mixing them in one module buys nothing.

"},{"location":"reference/usage/#reference","title":"Reference","text":""},{"location":"reference/usage/#html-tag","title":"html.<tag_name>(attributes, *children)","text":"

Every attribute of html is a tag constructor. The common tags are declared for autocomplete, but any name works \u2014 see Any name is a tag.

  • First argument: the attributes \u2014 but only when it is a plain dict. A VdomDict, or any dict subclass, is treated as a child instead. This single rule is the most common source of confusion, and it is worth internalizing:

    html.div({\"class\": \"card\"}, html.p(\"Body\"))  # dict -> attributes\nhtml.div(html.p(\"Body\"))  # no dict -> no attributes\nhtml.div(None, html.p(\"Body\"))  # None -> an explicit \"no attributes\"\n

    Leading None is the conventional way to say \"no attributes\" and keep the arguments in a predictable shape. It renders nothing.

    A VdomDict as the first argument is a child

    VdomDict is a dict subclass, and the check is for the exact plain dict type. So a VdomDict is never mistaken for attributes \u2014 even when you meant it that way:

    from reactpy.types import VdomDict\n\nhtml.div(VdomDict(tagName=\"span\"))  # -> a <span> *child* of the div\n
  • Remaining arguments: children, of any type Common Props accepts \u2014 strings, numbers, None, lists, dicts, components. Lists are flattened.

  • Returns: a VdomDict. Tag constructors are cached, so html.div is html.div \u2014 the same object every time. Nothing is rendered until a component returns it.

"},{"location":"reference/usage/#caveats","title":"Caveats","text":"
  • A plain dict that is meant as an element can be read as attributes. html.div({\"children\": [\"x\"]}) produces a div with an attribute literally named children, not a child \u2014 because the first argument was a plain dict. When in doubt, pass the element as the second argument after None.
  • Void tags reject children. html.br(\"x\") raises TypeError: 'br' nodes cannot have children. See Common Props.
  • Tags do not validate their own attributes. html.img({\"scr\": \"typo.png\"}) is accepted and renders an image with no source. There is no prop-type check; a misspelled attribute name is a silent no-op.
"},{"location":"reference/usage/#fragments","title":"html(*children) \u2014 fragments","text":"

Calling html itself is the fragment constructor: children with no wrapper element. See Common Props \u2014 a fragment accepts key and nothing else.

"},{"location":"reference/usage/#usage","title":"Usage","text":""},{"location":"reference/usage/#any-name-is-a-tag","title":"Any name is a tag","text":"

ReactPy does not keep a list of valid HTML tags. It converts the attribute name and sends it as the tag name, so you can reach tags the module has never declared:

You write You get html.div <div> html.data_table <data-table> html.my_widget <my-widget> html.del_ <del> html.class_ <class>

Two transformations: a trailing underscore is stripped (so you can name tags that are Python keywords, like del and class), and remaining underscores become dashes (so you can name custom elements and hyphenated tags that are not valid Python identifiers).

That makes custom elements first-class \u2014 no registration step, no wrapper component:

app.py Run
# Any name is a tag: underscores become dashes, so this is <user-badge>.\nuser_badge = html.user_badge\n\n\n@component\ndef profile(name, role):\n    return html.section(\n        {\"class\": \"profile\", \"data-role\": role},\n        html.h2(name),\n        # A plain string is a text child; an attribute dict is always first.\n        html.p({\"class\": \"muted\"}, \"Signed in as \", html.strong(name)),\n        user_badge({\"role\": role, \"label\": role.title()}),\n        html.footer(\n            html.small(\n                {\"style\": {\"font-size\": \"12px\", \"color\": \"#666\"}},\n                \"Last seen today\",\n            )\n        ),\n    )\n\n\n@component\ndef app():\n    return html.main(\n        None,\n        html.h1(\"Accounts\"),\n        profile(\"Ada\", \"admin\"),\n        profile(\"Linus\", \"viewer\"),\n    )\n
# TODO\n

The <user-badge> element reaches the browser as written. Whether it does anything is up to whatever defines it \u2014 a web component, an external script, or nothing at all. A tag the browser does not know renders as an unknown element: its children show up, its attributes are set, and it is laid out inline by default.

Prefer a component over a mystery tag

A custom tag is only worth it when something on the client implements it. For grouping your own markup, write a @component \u2014 it takes part in rendering, can hold state, and is checkable by a type checker.

"},{"location":"reference/usage/#tags-versus-components","title":"Tags versus components","text":"

Both are called the same way and both produce VDOM, and the difference is what happens next:

html.div(...) my_component(...) Returns A VdomDict, immediately A Component, rendered later by the layout Owns state No Yes \u2014 hooks belong to it Re-rendered Only when its parent returns a new value When its state changes or its parent re-renders it Defined with Provided by html @component over a function

The practical rule: a tag is for HTML elements, a component is for anything with behaviour. A <div> that has to remember something is not a div any more \u2014 it is a component that returns a div. See Your First Component.

"},{"location":"reference/usage/#nested-and-adjacent-tags","title":"Nested and adjacent tags","text":"

Tags nest by call nesting, and read left-to-right, innermost first:

html.p(html.em(\"emphasized\"), \" and \", html.strong(\"bold\"))\n

Because Python evaluates arguments eagerly, the inner call completes before the outer one runs. There is no indentation-scope or closing-tag syntax to get wrong, and no unbalanced-tag error \u2014 the structure either parses or it does not.

The one habit to build is the leading None. Without it, a first child that is a plain dict is consumed as attributes:

html.div({\"tagName\": \"span\"})  # attributes named \"tagName\" \u2014 not a child\nhtml.div(None, {\"tagName\": \"span\"})  # a <span> child\n

A complete page reads like the HTML it produces:

app.py Run
@component\ndef article_card():\n    # Children are positional. The first argument is attributes only if it is a\n    # plain dict, which is why a leading `None` is needed when there are no\n    # attributes but the first child could look like one.\n    return html.article(\n        {\"class\": \"card\"},\n        html.h2(\"Naming rules\"),\n        html.p(\n            html.em(\"html.del_\"),\n            \" is \",\n            html.code(\"<del>\"),\n            \", \",\n            html.code(\"html.data_table\"),\n            \" is \",\n            html.code(\"<data-table>\"),\n            \".\",\n        ),\n        html.ul(\n            None,\n            [html.li({\"key\": word}, word) for word in (\"class\", \"style\", \"key\")],\n        ),\n        html.hr(),\n        html.p({\"class\": \"muted\"}, \"Void tags take no children.\"),\n    )\n\n\n@component\ndef app():\n    return html.main(None, article_card(), html.br(), html.p(\"End of page.\"))\n
# TODO\n
"},{"location":"reference/usage/#lists-of-tags","title":"Lists of tags","text":"

A list comprehension produces children directly; give each one a key:

html.ul(None, [html.li({\"key\": word}, word) for word in words])\n

See Rendering Lists for why the key matters, and Common Props for where it lives in the dictionary.

"},{"location":"reference/usage/#classes-and-inline-styles","title":"Classes and inline styles","text":"

class and className both work, and style takes a dict. Include units inside the strings \u2014 a bare number is applied by some executors and silently dropped by others:

html.p({\"class\": \"muted\"}, \"...\")\nhtml.p({\"style\": {\"font-size\": \"12px\", \"color\": \"#666\"}}, \"...\")\n

Details, including the executor difference, are in Common Props.

"},{"location":"reference/usage/#svg","title":"SVG","text":"

SVG elements live under html.svg, because their tag names collide with HTML ones (<title>, <a>, <style>):

html.svg(\n    {\"viewBox\": \"0 0 24 24\", \"width\": \"24\"},\n    html.svg.circle({\"cx\": \"12\", \"cy\": \"12\", \"r\": \"10\"}),\n)\n

html.svg is itself a tag constructor, and nested names follow the same rules \u2014 trailing underscore stripped, underscores to dashes. Camel-cased SVG names are used as written, so html.svg.clipPath produces <clipPath>.

Names are not case-normalized

viewBox works because the name is forwarded exactly. Write SVG attribute names the way the spec spells them, capitals included.

"},{"location":"reference/usage/#raw-html","title":"Raw HTML","text":"

Two different jobs get confused here. To put a raw HTML string into the page as-is, without ReactPy parsing it, use dangerouslySetInnerHTML:

html.div({\"dangerouslySetInnerHTML\": {\"__html\": \"<b>bold</b>\"}})\n

To hand ReactPy a block of HTML and have it parsed into VDOM \u2014 real elements you can then inspect and transform \u2014 use string_to_reactpy() instead, which is exported at the top of the package:

from reactpy import string_to_reactpy\n\nstring_to_reactpy(\"<b>bold</b>\")\n# {'children': ['bold'], 'tagName': 'b'}\n

See Convert Between VDOM and HTML for the parse direction in full, including the *transforms argument.

Never pass untrusted input to either one: dangerouslySetInnerHTML is inserted without sanitizing, and string_to_reactpy() parses whatever you give it. Note also that a dangerouslySetInnerHTML attribute suppresses children rather than raising \u2014 nothing checks the combination, the raw HTML simply wins and the children are dropped. See Common Props.

"},{"location":"reference/usage/#inspecting-what-a-tag-produced","title":"Inspecting what a tag produced","text":"

A tag call is a plain dict, so print it:

>>> from reactpy import html\n>>> html.li({\"key\": \"a\"}, \"Item\")\n{'children': ['Item'], 'attributes': {'key': 'a'}, 'tagName': 'li'}\n

Key order can differ, and empty keys are omitted \u2014 a tag with no attributes has no \"attributes\" entry at all. To see what a component renders, drive it through reactpy.core.layout.Layout rather than printing the component instance, which shows only its arguments.

"},{"location":"reference/usage/#script-tags","title":"Script tags","text":"

html.script is special-cased. It rejects event handlers:

html.script({\"onLoad\": handler})\n
ValueError: 'script' elements do not support event handlers\n

and it infers its own key from its content, falling back to src, so the script is not re-run on every render:

>>> html.script({\"src\": \"/analytics.js\"})\n{'tagName': 'script', 'attributes': {'src': '/analytics.js', 'key': '/analytics.js'}}\n

Because the key drives re-execution, giving a script an explicit key is how you make it run again on purpose. As with dangerouslySetInnerHTML, keep untrusted data out of the body of a script.

"},{"location":"reference/usage/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/usage/#the-first-argument-became-an-attribute","title":"The first argument became an attribute","text":"

If your element has an attribute named children or tagName and renders nothing, a plain dict child was read as the attributes dict. Pass None first.

"},{"location":"reference/usage/#typeerror-tag-nodes-cannot-have-children","title":"TypeError: '<tag>' nodes cannot have children.","text":"

You passed content to a void tag. Remove the children.

"},{"location":"reference/usage/#typeerror-fragments-cannot-have-attributes-besides-key","title":"TypeError: Fragments cannot have attributes besides 'key'","text":"

Give the fragment a real element, or move the attributes onto a child.

"},{"location":"reference/usage/#an-attribute-has-no-effect","title":"An attribute has no effect","text":"

The name is forwarded verbatim and never validated, so a typo is invisible. Print the dict and compare the key against what the browser expects. Only class/className and for/htmlFor have aliases; nothing else does.

"},{"location":"reference/usage/#a-numeric-style-does-nothing","title":"A numeric style does nothing","text":"

Add the unit to the string. See Common Props.

"},{"location":"reference/usage/#at-a-glance","title":"At a glance","text":""},{"location":"reference/usage/#tag-names","title":"Tag names","text":"You write You get Rule html.div <div> as-is html.data_table <data-table> _ \u2192 - html.del_ <del> trailing _ stripped html.class_ <class> trailing _ stripped html.svg.clipPath <clipPath> as-is, under svg"},{"location":"reference/usage/#arguments","title":"Arguments","text":"Position Type Meaning first plain dict attributes, or pass None for none first (otherwise) any accepted child a child rest any accepted child children, flattened"},{"location":"reference/usage/#accepted-children","title":"Accepted children","text":"Child Renders as str text, verbatim int / float str(value) None nothing True / False the words True / False list / tuple flattened children dict an element (needs \"tagName\") @component instance that component's output"},{"location":"reference/usage/#related","title":"Related","text":"
  • Common Props \u2014 the VDOM dictionary: attributes, style, key, event handlers, dangerouslySetInnerHTML
  • Your First Component \u2014 components, and when to write one instead of a tag
  • Rendering Lists \u2014 keys
  • Responding to Events \u2014 onClick and friends
  • ReactPy \u2014 serving a component tree to a browser
"},{"location":"reference/use-async-effect/","title":"Use Async Effect","text":""},{"location":"reference/use-async-effect/#overview","title":"Overview","text":"

use_async_effect is a ReactPy hook that lets you run an async def effect, and keep it running across renders.

use_async_effect(effect)\nuse_async_effect(effect, dependencies=[value])\n
"},{"location":"reference/use-async-effect/#reference","title":"Reference","text":""},{"location":"reference/use-async-effect/#use_async_effecteffect-dependencies-shieldfalse","title":"use_async_effect(effect, dependencies=..., shield=False)","text":"

Call use_async_effect at the top level of your component to run an asynchronous effect. ReactPy wraps the coroutine in an asyncio.Task, so it runs on the connection's event loop rather than blocking the render.

from reactpy import use_async_effect\n
"},{"location":"reference/use-async-effect/#parameters","title":"Parameters","text":"
  • effect: An async def function taking no arguments. To run work in the background and stop it later, loop and handle cancellation \u2014 see running a long-lived effect.

  • dependencies (optional): Same four-way meaning as in use_effect, and the default is again not React's:

    Value Behaviour (verified) omitted (...) Inferred from the free variables of your closure. dependencies=[] Starts once, on mount. dependencies=[a, b] Restarts when a or b changes. dependencies=None Restarts after every render.

    Comparison is strictly_equal (core/hooks.py), not is \u2014 see Use Effect for the rule.

  • shield (optional): When True, an in-flight effect is not cancelled when the component unmounts or the effect is superseded. Default False.

"},{"location":"reference/use-async-effect/#returns","title":"Returns","text":"

None when given the effect, or a decorator when called without one.

"},{"location":"reference/use-async-effect/#caveats","title":"Caveats","text":"
  • The effect must take zero arguments. ReactPy calls asyncio.create_task(func()) (core/hooks.py). The 1.x signature async def effect(stop): is therefore broken \u2014 and it fails silently. Verified: the component renders normally, the model carries no error, the browser shows nothing wrong; the only trace is Error in effect / TypeError: ... missing 1 required positional argument: 'stop' in the server log. If your async effect \"does nothing\", check the server log first.
  • There is no stop event to wait on. Cancellation is ordinary asyncio.CancelledError; a long-running effect should loop and break out of it.
  • Cancelling is one of two clean-up paths: when dependencies change, the running task is cancelled before the new one starts. The other is the function your coroutine returns \u2014 see Cleanup.
  • The default dependency behaviour is inference, not \"every render\". See use_effect for the full table.
  • Effects run on the server. document and window do not exist.
  • An exception raised inside the coroutine is logged on the server only \u2014 no error reaches the model or the browser.
  • The new effect cancels and awaits the previous task before starting its own (core/hooks.py), but that hand-off is best-effort: the bookkeeping that locates the previous task can miss it, so two effect coroutines can be alive at once. Measured over five runs each, with three dependency changes per run: changes spaced 0.3 s apart produced zero overlaps, anything faster produced two. Do not write an effect that assumes it is the only one running; make teardown idempotent.
"},{"location":"reference/use-async-effect/#usage","title":"Usage","text":""},{"location":"reference/use-async-effect/#importing-use_async_effect","title":"Importing use_async_effect","text":"
from reactpy import use_async_effect\n
"},{"location":"reference/use-async-effect/#running-a-one-shot-async-effect","title":"Running a one-shot async effect","text":"

For a single await \u2014 a request, a slow lookup \u2014 write the coroutine straight:

@component\ndef stats(repo):\n    data, set_data = use_state(None)\n\n    async def effect():\n        result = await fetch_stats(repo)\n        set_data(result)\n\n    use_async_effect(effect, dependencies=[repo])\n\n    return html.p(data if data is not None else \"loading...\")\n

Setting state from the effect schedules the re-render; nothing else is needed.

"},{"location":"reference/use-async-effect/#running-a-long-lived-effect","title":"Running a long-lived effect","text":"

To keep work running across renders, loop and honour cancellation. This is what ReactPy's version of React's setInterval example looks like:

app.py Run
import asyncio\nfrom datetime import datetime, timezone\n\nfrom reactpy import component, html, use_async_effect, use_state\n\n\n@component\ndef clock():\n    now, set_now = use_state(datetime.now(timezone.utc))\n\n    async def effect():\n        # Zero arguments. A long-running effect loops and honours cancellation.\n        while True:\n            set_now(datetime.now(timezone.utc))\n            try:\n                await asyncio.sleep(1)\n            except asyncio.CancelledError:\n                break\n\n    use_async_effect(effect)\n\n    return html.h1(now.strftime(\"%H:%M:%S\"))\n
# TODO\n

The clock advances on its own \u2014 verified, one update per second. When dependencies change or the component unmounts, ReactPy cancels the task and the loop exits through CancelledError.

Do not wait for a stop event

ReactPy 1.x passed a stop event into async effects. That parameter no longer exists, and passing it is not reported anywhere the user can see. Write this:

async def effect():\n    while True:\n        do_work()\n        try:\n            await asyncio.sleep(1)\n        except asyncio.CancelledError:\n            break\n

Not this:

async def effect(stop):  # \ud83d\udea9 silent failure\n    while not stop.is_set():\n        ...\n
"},{"location":"reference/use-async-effect/#shielding-an-effect-from-cancellation","title":"Shielding an effect from cancellation","text":"

shield=True lets an in-flight effect run to completion even as the component unmounts \u2014 useful for finishing a request whose result must not be discarded:

use_async_effect(effect, dependencies=[], shield=True)\n

Verified: with shield=True a sleeping effect reached its completion line after the layout exited; with the default it was cancelled at that same moment. Leave this off unless you specifically want work to outlive the component, since it also means the effect keeps holding references and consuming the loop.

"},{"location":"reference/use-async-effect/#cleanup","title":"Cleanup","text":"

Async effects have two clean-up paths, and which one applies depends on whether your coroutine finishes.

A coroutine that completes may return a clean-up function. ReactPy stores the coroutine's result (core/hooks.py, cleanup_func.current = task.result()) and runs it when the effect is next stopped \u2014 before a replacement effect starts, and again on unmount. Verified across three dependency changes and an unmount: every (start, n) was followed by its own (cleanup, n).

async def effect():\n    handle = await open_connection()\n    return handle.close  # runs at the next dependency change, and on unmount\n

A coroutine that never completes \u2014 the while True: loop \u2014 never returns, so its return value cannot be the clean-up. Cancellation is:

async def effect():\n    handle = await open_connection()\n    try:\n        while True:\n            await handle.poll()\n    except asyncio.CancelledError:\n        handle.close()\n        raise\n

Re-raising matters: swallowing CancelledError leaves the task in a state asyncio did not expect.

"},{"location":"reference/use-async-effect/#choosing-between-use_effect-and-use_async_effect","title":"Choosing between use_effect and use_async_effect","text":"Need Use A synchronous side effect use_effect Anything await-based use_async_effect A repeating timer, polling, a subscription stream use_async_effect with a loop Work that must block the render neither \u2014 do it during render, or raise

Passing an async def to use_effect fails loudly, which is the one mismatch ReactPy does report: TypeError: use_effect does not support async functions. Use use_async_effect instead.

"},{"location":"reference/use-callback/","title":"Use Callback","text":""},{"location":"reference/use-callback/#overview","title":"Overview","text":"

use_callback is a ReactPy hook that caches a function, replacing it only when one of its listed dependencies changes.

cached_handler = use_callback(handler, dependencies=[dep_1, dep_2])\n

It is the function-shaped counterpart of use_memo: same rules, same dependency behaviour, but the thing you cache is a callable rather than a value.

You probably do not need it

In React, useCallback earns its place by keeping a child from re-rendering behind memo and by keeping effect dependencies from thrashing. Neither reason applies to ReactPy. There is no render bailout and no memo, and a function you define in the component body already counts as an unchanged dependency, because ReactPy compares local functions by their code rather than by identity. So the effect-churn problem useCallback exists to solve does not occur here.

What it does give you is a genuinely stable function object, which matters only when the function leaves ReactPy and something else keys on its identity \u2014 a subscription registry, an event bus, a cached table. If none of that describes your code, write a plain def and skip this page.

"},{"location":"reference/use-callback/#reference","title":"Reference","text":""},{"location":"reference/use-callback/#use-callback","title":"use_callback(function, dependencies)","text":"

Call use_callback at the top level of your component to cache an event handler or other callback.

from reactpy import use_callback\n
"},{"location":"reference/use-callback/#parameters","title":"Parameters","text":"
  • function: The function you want to keep. It must be pure in the sense that matters here: same closure, same behaviour.
  • dependencies: The values the function reads. ReactPy replaces the cached function when any of them changes. Three shapes, identical to use_memo:

    You pass Meaning dependencies=[a, b] Replace the function when a or b changes. omitted (the default) Infer the dependencies from the function's closure. dependencies=None Return a new function on every render \u2014 no caching at all.
"},{"location":"reference/use-callback/#returns","title":"Returns","text":"

The cached function \u2014 either the one from a previous render, or, if a dependency changed, the one you just passed in.

"},{"location":"reference/use-callback/#caveats","title":"Caveats","text":"
  • The function you pass is not discarded, it is stored. ReactPy does not call it; it keeps the object. That is why caching a function that closes over state can hand you an old closure \u2014 see A cached handler sees stale values.
  • dependencies=[] pins the function forever. The same object comes back on every render, including the one holding the values it closed over on the first render. This is the single most common use_callback bug, and ReactPy will not warn you.
  • Listing a dependency you read is not optional. Forgetting one produces a handler that quietly acts on outdated values. Nothing checks this for you.
  • Stable identity does not mean stable behaviour. A function can be the same object across renders and still be wrong, because identity was the point and the closure went stale. Cache identity when something external needs identity; do not use it as a performance gesture.
  • Comparison follows strictly_equal, not identity \u2014 see the equivalent caveat in use_memo: same type first, code comparison for local functions, == otherwise.
  • The decorated name is the function, not a wrapper. @use_callback(dependencies=[x]) receives your function and returns a function, so the name still calls fine \u2014 unlike use_memo, where the name becomes the value.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return.
"},{"location":"reference/use-callback/#usage","title":"Usage","text":""},{"location":"reference/use-callback/#when-a-stable-identity-matters","title":"When a stable identity matters","text":"

Here is the case use_callback is actually for. The component subscribes to a bus that lives outside ReactPy. That bus stores handlers in a list and skips duplicates by identity \u2014 so a new function object on each render registers another handler, and every handler receives each message.

Press Re-render three times. handlers registered stays at 1, because the handler really is the same object each time. Press Publish and it is delivered once. Replace the use_callback line with a plain def and the same interaction registers four handlers.

app.pydata.py Run
@component\ndef listener():\n    renders, set_renders = use_state(0)\n    heard, set_heard = use_state(0)\n\n    # The registry lives outside ReactPy, so nothing deduplicates it for you.\n    # A plain `def` here would be a brand new function object on every render,\n    # and the registry would therefore collect another handler each time --\n    # all of which receive every message. Pinning the identity keeps exactly\n    # one handler, however often this component renders.\n    handler = use_callback(\n        lambda message: set_heard(lambda previous: previous + 1), dependencies=[]\n    )\n\n    def subscribe():\n        bus.subscribe(handler)\n\n    use_effect(subscribe, dependencies=None)\n\n    async def handle_renders_click(event):\n        set_renders(renders + 1)\n\n    async def handle_click(event):\n        bus.publish(\"ping\")\n\n    return html.div(\n        html.p(f\"renders: {renders}\"),\n        html.p(f\"handlers registered: {len(bus.handlers)}\"),\n        html.p(f\"this component heard: {heard}\"),\n        html.button({\"onClick\": handle_renders_click}, \"Re-render\"),\n        html.button({\"onClick\": handle_click}, \"Publish\"),\n    )\n
\"\"\"A stand-in for any registry that lives outside ReactPy.\n\nThis is the shape of code where a callback's identity actually matters: the\nregistry is ordinary Python, so nothing deduplicates it for you -- it stores\nhandlers by identity, and a *new* function object on each render means a new\nentry each render.\n\"\"\"\n\n\nclass Bus:\n    def __init__(self):\n        self.handlers = []\n        self.handled = []\n\n    def subscribe(self, handler):\n        # `in` on a list of plain functions compares by identity, which is\n        # exactly what a new `def` per render changes.\n        if handler not in self.handlers:\n            self.handlers.append(handler)\n\n    def publish(self, message):\n        for handler in list(self.handlers):\n            handler(message)\n\n\nbus = Bus()\n
# TODO\n

Note the heard updater: set_heard(lambda previous: previous + 1). It reads no state, which is what lets this handler keep an empty dependency list honestly \u2014 an updater function gets the current value at the moment the setter runs, so the handler does not need to close over anything.

"},{"location":"reference/use-callback/#a-cached-handler-sees-stale-values","title":"A cached handler sees stale values","text":"

The notes app below caches a handler with dependencies=[] while the handler reads draft. ReactPy keeps the first function, which closed over an empty draft, so every click adds a blank note no matter what you type.

app.py Run
@component\ndef notes():\n    notes_list, set_notes_list = use_state([])\n    draft, set_draft = use_state(\"\")\n\n    # `draft` is read here, but `dependencies=[]` promises the function never\n    # changes -- so ReactPy keeps handing back the *first* function, which\n    # closed over an empty `draft`. Each click adds a blank note no matter what\n    # you typed. Listing what the handler reads is what fixes it.\n    async def add_note_handler(event):\n        set_notes_list([*notes_list, draft])\n\n    add_note = use_callback(add_note_handler, dependencies=[])\n\n    async def handle_draft_change(event):\n        set_draft(await event.target.value)\n\n    return html.div(\n        html.h1(\"Notes\"),\n        html.ul(\n            [\n                html.li({\"key\": str(i)}, note or \"(blank)\")\n                for i, note in enumerate(notes_list)\n            ]\n        ),\n        html.input(\n            {\n                \"value\": draft,\n                \"onChange\": handle_draft_change,\n            }\n        ),\n        html.button({\"onClick\": add_note}, \"Add note\"),\n    )\n
# TODO\n

Type something and press Add note: the list gains (blank). The handler is working perfectly \u2014 on the render it was created on.

"},{"location":"reference/use-callback/#listing-what-the-handler-reads","title":"Listing what the handler reads","text":"

The fix is to name the values the function reads. Now the cached function is replaced whenever notes_list or draft changes, so a click always sees what is currently in the box \u2014 while the identity still stays put between changes.

app.py Run
@component\ndef notes():\n    notes_list, set_notes_list = use_state([])\n    draft, set_draft = use_state(\"\")\n\n    # List what the handler reads and the cached function is replaced exactly\n    # when those values change: the click always sees the current draft, while\n    # the identity still stays put between changes.\n    async def add_note_handler(event):\n        set_notes_list([*notes_list, draft])\n\n    add_note = use_callback(add_note_handler, dependencies=[notes_list, draft])\n\n    async def handle_draft_change(event):\n        set_draft(await event.target.value)\n\n    return html.div(\n        html.h1(\"Notes\"),\n        html.ul(\n            [\n                html.li({\"key\": str(i)}, note or \"(blank)\")\n                for i, note in enumerate(notes_list)\n            ]\n        ),\n        html.input(\n            {\n                \"value\": draft,\n                \"onChange\": handle_draft_change,\n            }\n        ),\n        html.button({\"onClick\": add_note}, \"Add note\"),\n    )\n
# TODO\n

Type something and press Add note: the text lands. These two files differ by one line; the behaviour difference is the whole point.

"},{"location":"reference/use-callback/#omitting-the-dependency-list","title":"Omitting the dependency list","text":"

Leave out dependencies and ReactPy infers them from the function's closure, exactly as it does for use_memo and use_effect. For a handler that reads component state this is usually what you want, and it removes the \"forgot a dependency\" class of bug. Omitting is not the same as dependencies=[] \u2014 the inferred list is built from what the function actually reads, which is rarely empty.

"},{"location":"reference/use-callback/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-callback/#my-handler-acts-on-old-values","title":"My handler acts on old values","text":"

You cached it with an empty or incomplete dependency list. The function is the same object, and so is everything it closed over. Either list the values it reads, or stop the value from being read at all by using an updater \u2014 set_items(lambda previous: [*previous, new]) instead of set_items([*items, new]).

"},{"location":"reference/use-callback/#my-handler-never-fires","title":"My handler never fires","text":"

Check the attribute name. {\"on_click\": handler} renders without complaint, lands in eventHandlers, and is never called \u2014 the client registers a DOM listener for the key with on stripped, so the underscore spelling subscribes to an event named _click that no browser ever fires. The key must be the DOM event name in camelCase (\"onClick\"), and the value must be your function, not a call to it. A wrong name is invisible: the only difference is that nothing happens.

"},{"location":"reference/use-callback/#i-still-get-a-new-function-every-render","title":"I still get a new function every render","text":"
  • You passed dependencies=None, which means exactly that.
  • A dependency is rebuilt each render and does not compare equal \u2014 a plain class instance without __eq__, for instance.
  • The function is defined outside the component and you expect it to be cached; use_callback caches what you hand it, per component instance.
"},{"location":"reference/use-callback/#do-i-need-this-to-stop-a-child-re-rendering","title":"Do I need this to stop a child re-rendering?","text":"

No \u2014 that mechanism does not exist in ReactPy. See the note at the top of the page: children re-render whenever their parent does, regardless of prop or handler identity.

"},{"location":"reference/use-connection/","title":"Use Connection","text":""},{"location":"reference/use-connection/#overview","title":"Overview","text":"

use_connection is a ReactPy hook that returns the Connection object describing the websocket your component is rendering into.

connection = use_connection()\n

Each connected browser tab has its own. The object carries three things: the ASGI scope of the websocket, the location the browser reports, and the server's object mediating the connection.

"},{"location":"reference/use-connection/#reference","title":"Reference","text":""},{"location":"reference/use-connection/#use-connection","title":"use_connection()","text":"

Call it at the top level of a component. It takes no arguments.

from reactpy import use_connection\n
"},{"location":"reference/use-connection/#returns","title":"Returns","text":"

A reactpy.types.Connection dataclass with three fields:

  • scope: The ASGI scope of the websocket \u2014 the same dict use_scope returns.
  • location: A Location describing the page the browser is on \u2014 the same object use_location() returns.
  • carrier: The object mediating the connection. What it is depends on the executor; under the ASGI executors it is a ReactPyWebsocket. Capture type(connection.carrier).__name__ rather than assuming.

Import the type with from reactpy.types import Connection; it is not exported from the reactpy namespace.

"},{"location":"reference/use-connection/#caveats","title":"Caveats","text":"
  • This is a read-only view. Connection has no deliver, no send, no close, no id \u2014 verified against the class. You cannot push a message to this client from here; communicate through state, effects, and event handlers. The carrier object under the ASGI executors is an internal ReactPyWebsocket, not a supported API to call into.
  • scope and location are live, mutable objects, not copies. Writing into connection.scope changes what every other read sees. Treat both as read-only.
  • There is one per connection, not per render. Each connected tab gets its own \u2014 verified: two concurrent clients saw distinct scopes and distinct locations \u2014 but within one tab the same object comes back on every render.
  • It needs a live connection. Two distinct failures:

    • Called outside any component render \u2192 RuntimeError: No life cycle hook is active. Are you rendering in a layout?
    • Called inside a plain Layout render with no provider \u2192 RuntimeError: No backend established a connection.

    Both messages are verbatim. In practice the second is the one you meet in tests; see injecting a connection in tests. - It does not exist under client-side rendering. ReactPyCsr runs components without a Connection at all, so the hook raises there even though Python is in the browser. Verified in Chrome. - Like every hook, call it only at the top level.

"},{"location":"reference/use-connection/#usage","title":"Usage","text":""},{"location":"reference/use-connection/#who-is-connected","title":"Who is connected","text":"

Each field answers a different question, and mixing them up is the usual source of confusion.

app.py Run
@component\ndef connection_report():\n    connection = use_connection()\n\n    # Three things, three sources: `scope` is what the ASGI server received,\n    # `location` is the URL the browser reported, `carrier` is the object\n    # mediating the connection (a websocket for the ASGI executors).\n    return html.div(\n        html.h1(\"Who is connected\"),\n        html.p(f\"peer: {connection.scope['client']}\"),\n        html.p(f\"page: {connection.location.path}\"),\n        html.p(f\"protocol: {connection.scope['type']}\"),\n        html.p(f\"carrier: {type(connection.carrier).__name__}\"),\n    )\n
# TODO\n

scope['client'] is what the server saw when the websocket opened. location.path is what the browser reported. They are independent sources, which is why the page path is not in the scope \u2014 see use_location for why that matters.

"},{"location":"reference/use-connection/#injecting-a-connection-in-tests","title":"Injecting a connection in tests","text":"

Components that use any of the connection hooks fail in a bare Layout render. For a unit test, build the Connection yourself and wrap the component in the provider \u2014 ConnectionContext is meant to be set by the backend at the root of an app, and a test is a backend.

import asyncio\n\nfrom reactpy.core.hooks import ConnectionContext\nfrom reactpy.core.layout import Layout\nfrom reactpy.types import Connection, Location\n\nfrom .app import page  # the component under test\n\nconnection = Connection(\n    scope={\"type\": \"websocket\", \"client\": (\"127.0.0.1\", 5000)},\n    location=Location(path=\"/reports\", query_string=\"?tab=2\"),\n    carrier=None,\n)\n\n\ndef test_page():\n    async def render():\n        async with Layout(ConnectionContext(page(), value=connection)) as layout:\n            update = await layout.render()\n        return str(update[\"model\"])\n\n    rendered = asyncio.run(render())\n    assert \"/reports\" in rendered  # came from the injected Location\n

carrier can be anything \u2014 None is fine, since nothing in a unit test dispatches over it. Note that ConnectionContext lives in reactpy.core.hooks rather than the public reactpy namespace: it is deliberately the backend's knob, and in a test your code is the backend.

The hooks consume no hook slot

All three connection hooks ultimately call use_context, which reads the provider without reserving a hook slot. Unlike use_state or use_effect, they are safe to call conditionally or on only some renders; they will not shift other hooks' state. Verified: a component calling use_connection() only on even renders re-rendered cleanly across five renders.

"},{"location":"reference/use-connection/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-connection/#runtimeerror-no-backend-established-a-connection","title":"RuntimeError: No backend established a connection.","text":"

No ConnectionContext was set. Under ReactPy or ReactPyMiddleware it is set for you once the websocket opens, so in production this means the component rendered somewhere the executor does not cover \u2014 most commonly a plain Layout render, a reactpy_to_string call, or a ReactPyCsr page. Either inject one as above, or make the component tolerate its absence:

try:\n    connection = use_connection()\nexcept RuntimeError:\n    return html.p(\"(not connected yet)\")\n
"},{"location":"reference/use-connection/#runtimeerror-no-life-cycle-hook-is-active-are-you-rendering-in-a-layout","title":"RuntimeError: No life cycle hook is active. Are you rendering in a layout?","text":"

You called the hook outside a render \u2014 at module level, in a plain function the component calls before rendering, or directly in your test body. Hooks only work while a component is rendering inside a Layout.

"},{"location":"reference/use-connection/#connectioncarrier-is-none-in-my-test","title":"connection.carrier is None in my test","text":"

Correct \u2014 you passed None, and nothing reads it. Only the executor's own plumbing touches the carrier.

"},{"location":"reference/use-connection/#i-need-to-send-something-to-this-specific-client","title":"I need to send something to this specific client","text":"

There is no supported way to reach a single connection from a component. State changes re-render that client's own components; anything beyond that is outside the scope of these hooks.

"},{"location":"reference/use-context/","title":"Use Context","text":""},{"location":"reference/use-context/#overview","title":"Overview","text":"

use_context is a ReactPy hook that reads a value provided by a component higher in the tree, without every component in between having to pass it along.

value = use_context(SomeContext)\n

Context is for values a whole subtree needs and the middle of the tree does not: a theme, the current user, the active locale, a store. If a component would only forward a prop to its children, context removes both the prop and the component's reason to exist.

"},{"location":"reference/use-context/#reference","title":"Reference","text":""},{"location":"reference/use-context/#use-context","title":"use_context(context)","text":"

Call it at the top level of a component, passing the context object you created with create_context.

from reactpy import use_context\n
"},{"location":"reference/use-context/#parameters","title":"Parameters","text":"
  • context: The object returned by create_context. It is matched by identity, so the same object must be used on both the provider and the reader \u2014 import it from one place.
"},{"location":"reference/use-context/#returns","title":"Returns","text":"

The value of the nearest context provider above the component. With no provider anywhere above, it returns the default given to create_context.

"},{"location":"reference/use-context/#caveats","title":"Caveats","text":"
  • Reading context is not a subscription that spares renders. When a provider re-renders with a new value, its children re-render \u2014 which they would have anyway, since ReactPy re-renders children unconditionally. Context hands you the new value; it does not narrow the work. See What context does not do.
  • It returns the default, not None, when nothing provides it. create_context(\"light\") yields \"light\" to an unprovided reader, so a component renders on its own without a wrapper. Pick a default that makes sense standalone, or None plus an explicit fallback branch.
  • Call it during the render, not from an effect body. An effect runs later, in its own task, where no hook is active; calling use_context there raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? Read it in the component and let the closure carry the value in \u2014 see Reading context inside an effect.
  • The context object must be hashable and must be the same object. Passing something else raises: TypeError: <x> is not a Context for a class or string, TypeError: unhashable type: 'dict' if you hand it a dict or list. Two create_context calls over the same default are two unrelated contexts.
  • A None default narrows the type. create_context(None) infers a value type of None, so a type checker rejects value=[...]. Annotate the name to widen it: Cart: Context[list[str] | None] = create_context(None).
  • Like every hook, call it only at the top level. Unusually, context is safe to call conditionally \u2014 it reserves no hook slot, so unlike use_state it cannot shift other hooks' state.
"},{"location":"reference/use-context/#creating-and-providing-a-context","title":"Creating and providing a context","text":"

A context is created once, at module level, with create_context:

from reactpy import create_context\n\nTheme = create_context(\"light\")\n

What comes back is an ordinary function, and calling it is the provider: pass the children positionally and the value as a keyword.

Theme(child_a, child_b, value=\"dark\")\n

There is no Theme.Provider \u2014 calling the context is the only form. The provider renders a nameless fragment around its children, so it adds nothing to the output.

The value= keyword is optional; omit it and the default from create_context is used. A key= keyword is also accepted, with the same meaning as on any other element.

"},{"location":"reference/use-context/#usage","title":"Usage","text":""},{"location":"reference/use-context/#passing-data-deeply","title":"Passing data deeply","text":"

swatch needs the theme. toolbar does not \u2014 it renders swatch and never mentions it. Without context, toolbar would take a theme argument purely to forward it, and every component added in between would inherit that obligation.

Press Switch to dark: the provider gets a new value, and the component far below reads it.

app.py Run
# One context per concept, created once at module level. The argument is the\n# value descendants get when nothing provides one -- a sensible default means\n# a component can render before any provider exists.\nTheme = create_context(\"light\")\n\n\n@component\ndef swatch():\n    # Reads whatever the nearest provider above it supplied. It takes no props:\n    # the value arrives without anything in between having to pass it along.\n    theme = use_context(Theme)\n    return html.div(\n        html.p(f\"theme: {theme}\"),\n        html.div({\"style\": {\"background\": theme, \"height\": \"2rem\"}}, \"\"),\n    )\n\n\n@component\ndef toolbar():\n    # A component in the middle. It neither receives nor cares about `theme`;\n    # it only has to render its children for them to reach the provider above.\n    return html.div(html.h2(\"Toolbar\"), swatch())\n\n\n@component\ndef app():\n    theme, set_theme = use_state(\"light\")\n\n    async def handle_theme_click(event):\n        set_theme(\"dark\" if theme == \"light\" else \"light\")\n\n    return html.div(\n        Theme(toolbar(), value=theme),\n        html.button(\n            {\"onClick\": handle_theme_click},\n            f\"Switch to {'dark' if theme == 'light' else 'light'}\",\n        ),\n    )\n
# TODO\n
"},{"location":"reference/use-context/#choosing-a-default","title":"Choosing a default","text":"

The default is what a component sees when no provider is above it, which is exactly the situation in a unit test or a standalone render. Make it something the component can handle.

cart_badge below copes with either \u2014 rendered bare it says the cart is unavailable, wrapped in the provider it counts the items.

app.py Run
from reactpy import component, create_context, html, use_context\nfrom reactpy.types import Context\n\n# The annotation is what lets the value be a list *or* the None default. Without\n# it, create_context(None) fixes the value type to None and a type checker then\n# rejects `value=[...]`.\nCart: Context[list[str] | None] = create_context(None)\n\n\n@component\ndef cart_badge():\n    # With no provider above, use_context returns the default given to\n    # create_context. Choosing that default well is what lets a component\n    # render -- or be tested -- on its own, with no provider wrapper.\n    items = use_context(Cart)\n    if items is None:\n        return html.p(\"cart: not available\")\n    return html.p(f\"cart: {len(items)} item(s)\")\n\n\n@component\ndef shop():\n    return html.div(cart_badge())\n\n\n@component\ndef shop_with_cart():\n    return Cart(shop(), value=[\"widget\", \"gadget\", \"gizmo\"])\n
# TODO\n

Note the type annotation on Cart. Because the default is None, without the annotation a type checker infers the value type as None and rejects Cart(shop(), value=[\"widget\", ...]).

"},{"location":"reference/use-context/#nesting-providers","title":"Nesting providers","text":"

The provider nearest above the reader wins, and sibling subtrees are independent. \"Above\" means in the tree that renders it, so the reader has to live inside a child component \u2014 reading in the enclosing body runs before the provider is rendered:

Theme = create_context(\"light\")\n\n\n@component\ndef swatch():\n    # A child component: it renders after the provider above it has registered.\n    return html.p(use_context(Theme))\n\n\n@component\ndef page():\n    return Theme(\n        swatch(),                                     # \"outer\"\n        Theme(swatch(), value=\"inner\"),               # \"inner\"\n        value=\"outer\",\n    )\n

Verified: the first reads \"outer\", the second \"inner\"; two sibling providers with different values each see their own.

Do not read the context in the same body that renders the provider

@component\ndef page():\n    return Theme(\n        html.p(use_context(Theme)),   # \"light\", not \"outer\"\n        value=\"outer\",\n    )\n

Arguments are evaluated while page() runs, and the provider registers its value only when the layout later renders it. Both reads here return the create_context(\"light\") default. Move the read into a child component. The same rule explains why reading inside an effect fails (below).

Removing a provider returns its descendants to the default. What actually produces that is the remount: a provider is a component node in the tree, so dropping it moves its children to a different position and ReactPy rebuilds them \u2014 verified, a counter below a removed provider went back to 0 along with its value. Do not lean on that. core/_life_cycle_hook.py, set_current only ever merges the parent's providers into a hook (_context_providers.update(...)) and nothing prunes the dict, so a component that survives a render keeps whatever it inherited. If you need a guaranteed reset, change the child's \"key\".

"},{"location":"reference/use-context/#reading-context-inside-an-effect","title":"Reading context inside an effect","text":"

Effects run in their own task, outside any component render, so no hook is active there and use_context will not work. Read it in the component and use the local:

app.py Run
Config = create_context({\"endpoint\": \"https://api.example\"})\n\n\n@component\ndef status():\n    # Read the context HERE, during the render. An effect body runs later, in\n    # its own task, where no hook is active -- calling use_context there fails\n    # with `RuntimeError: No life cycle hook is active.` Capture the value in a\n    # local and let the closure carry it into the effect.\n    config = use_context(Config)\n\n    def on_mount():\n        print(f\"polling {config['endpoint']}\")\n\n    use_effect(on_mount, dependencies=[config])\n    return html.p(f\"endpoint: {config['endpoint']}\")\n
# TODO\n

Putting use_context(Config) inside on_mount instead raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? \u2014 and since an effect error is logged only on the server, it is easy to miss.

"},{"location":"reference/use-context/#what-context-does-not-do","title":"What context does not do","text":"

Context solves plumbing: it moves a value past components that do not care about it. It is not a performance feature.

  • It does not stop children from re-rendering. ReactPy has no render bailout, so when the provider re-renders, everything below it re-renders too. A component reading a context whose value did not change still re-renders \u2014 verified: toggling a theme re-ran the bodies of both a context reader and an unrelated sibling.
  • It does not make a value reactive by itself. The value changes because the component that renders the provider re-rendered with a new one. If nothing re-renders the provider, readers keep seeing the old value.
  • It does not replace state. Context carries values down. To let a component change a shared value, put the state in a common ancestor and pass the setter down alongside it \u2014 or move to a reducer, see use_reducer.

For a value that only one component's subtree uses, a prop is simpler and easier to trace. Reach for context when the value genuinely crosses several levels that have no use for it.

"},{"location":"reference/use-context/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-context/#readers-always-get-the-default","title":"Readers always get the default","text":"

Usually one of:

  • The provider is not above the reader in the tree. Theme(child, value=...) must be an ancestor \u2014 calling it and discarding the result provides nothing.
  • The provider and the reader hold different context objects. Two create_context calls are different contexts even with the same default, so create it once and import it.
  • The provider's value= never changes, so readers keep seeing the value from the render that set it.
"},{"location":"reference/use-context/#runtimeerror-no-life-cycle-hook-is-active-are-you-rendering-in-a-layout","title":"RuntimeError: No life cycle hook is active. Are you rendering in a layout?","text":"

You called use_context outside a render \u2014 from an effect body, from a plain function called before the component runs, or directly in a test. Move the call into the component body.

"},{"location":"reference/use-context/#typeerror-is-not-a-context","title":"TypeError: ... is not a Context","text":"

You passed something other than the result of create_context \u2014 often the provider instead of the context (use_context(Theme(child, value=\"x\"))), or the class of a value rather than a context.

"},{"location":"reference/use-context/#my-type-checker-rejects-the-providers-value","title":"My type checker rejects the provider's value","text":"

The default narrowed the type. Annotate the module-level name with Context[<your type> | None], as in the default-value example above.

"},{"location":"reference/use-debug-value/","title":"Use Debug Value","text":""},{"location":"reference/use-debug-value/#overview","title":"Overview","text":"

use_debug_value is a ReactPy hook that writes a message to the server log whenever it changes, for the component that calls it.

use_debug_value(message, dependencies)\n

ReactPy components run on the server, so there is no DevTools panel to label. This is the equivalent: a value that shows up in your server's log output, tagged with the component that produced it, printed only when it differs from the last thing printed.

It needs debug mode, and logs go to the server

Nothing happens unless REACTPY_DEBUG is on. Set it before importing ReactPy \u2014 REACTPY_DEBUG=1 python app.py, or os.environ[\"REACTPY_DEBUG\"] = \"1\" at the top of your entry point. Output goes through the standard logging module under the reactpy.core.hooks logger, so it lands wherever your logging configuration sends it, not in the browser console. A reader who expects DevTools will conclude the hook is broken; it is not.

"},{"location":"reference/use-debug-value/#reference","title":"Reference","text":""},{"location":"reference/use-debug-value/#use-debug-value","title":"use_debug_value(message, dependencies)","text":"

Call it at the top level of a component, or at the top level of one of your own hooks.

from reactpy import use_debug_value\n
"},{"location":"reference/use-debug-value/#parameters","title":"Parameters","text":"
  • message: The value to log, or a function of no arguments that produces it. Pass a function when building the message costs something \u2014 ReactPy memoizes it, so it re-runs only when its dependencies change.
  • dependencies: Only meaningful with a function message. Same three shapes as every other hook that takes them:

    You pass Meaning dependencies=[a, b] Rebuild the message when a or b changes. omitted (the default) Infer them from the function's closure. dependencies=None Rebuild the message on every render.
"},{"location":"reference/use-debug-value/#returns","title":"Returns","text":"

None.

"},{"location":"reference/use-debug-value/#caveats","title":"Caveats","text":"
  • Silent without debug mode. With REACTPY_DEBUG off, the hook logs nothing. Whether it still builds the message is decided by the dependency comparison, not by debug mode: the message is produced through use_memo, so a builder whose dependencies never change runs once and its result is discarded on every later render. A builder that closes over changing state runs every render, debug or no debug. If the build is expensive, control it with the dependency argument \u2014 do not assume the hook skipped it, and do not assume it ran.
  • \"Changed\" means !=, not identity. Unlike every other hook, which compares dependencies with strictly_equal, this one compares the new message against the previous one with plain !=. So a freshly constructed {\"kind\": \"rows\"} logs once and then stays quiet, because it compares equal to what was logged last time. Verified.
  • The comparison runs on every render, on the value you supply. For a large or slow-to-compare object that is a real cost, paid whether or not anything gets logged. This is the one hook where the docstring's performance note is worth taking literally.
  • An uncomparable message breaks the component. If message != previous raises, the exception is not caught: it surfaces as a render error in the model. A class with a __eq__ that raises will fail the component, so keep messages to plain data \u2014 strings, numbers, dicts, lists.
  • The value is memoized like any other hook value. With a stable message, the builder function runs once; with a message that changes each render, it runs each render. Measured across four renders: stable \u2192 1 call, changing \u2192 4 calls.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return.
"},{"location":"reference/use-debug-value/#usage","title":"Usage","text":""},{"location":"reference/use-debug-value/#labelling-a-custom-hook","title":"Labelling a custom hook","text":"

The useful case. When a component calls three of your own hooks, a log line that says only shop(...) tells you nothing; a message naming what the hook holds tells you which hook is behaving oddly, and when.

Run this with debug on and press Add item. Each add logs one line through the reactpy.core.hooks logger, prefixed with the component and its id:

shop(7f7ef6f0d540) 0 item(s): -\nshop(7f7ef6f0d540) 1 item(s): item 1\nshop(7f7ef6f0d540) 2 item(s): item 1, item 2\nshop(7f7ef6f0d540) 3 item(s): item 1, item 2, item 3\n

Pressing a button that leaves the cart alone logs nothing, because the message did not change.

app.py Run
def use_cart():\n    \"\"\"A custom hook: it holds the cart and reports what it holds.\"\"\"\n    items, set_items = use_state([])\n\n    # Logged to the server as `use_cart's component(...) <message>` whenever the\n    # message differs from the last one logged. A plain string here would be\n    # built on every render; reading state makes it change only when it should.\n    use_debug_value(f\"{len(items)} item(s): {', '.join(items) or '-'}\")\n\n    async def add(event=None):\n        set_items([*items, f\"item {len(items) + 1}\"])\n\n    return items, add\n\n\n@component\ndef shop():\n    items, add = use_cart()\n    return html.div(\n        html.h1(f\"Cart ({len(items)})\"),\n        html.ul([html.li({\"key\": i}, item) for i, item in enumerate(items)]),\n        html.button({\"onClick\": add}, \"Add item\"),\n    )\n
# TODO\n
"},{"location":"reference/use-debug-value/#passing-a-function-as-the-message","title":"Passing a function as the message","text":"

If producing the message is more than string formatting, pass a function and let ReactPy memoize it. The report below finds the highest score to build its label; that walk happens once per distinct rows, not once per render.

Run it with debug on and press Add row:

report(7f7ef6f0da80) 1 rows, best=ada:91\nreport(7f7ef6f0da80) 2 rows, best=ada:91\nreport(7f7ef6f0da80) 3 rows, best=ada:91\n
app.py Run
@component\ndef report():\n    rows, set_rows = use_state([{\"name\": \"ada\", \"score\": 91}])\n\n    # Pass a function when turning the message into text is not free. ReactPy\n    # memoizes it like any other hook, so it re-runs only when what it reads\n    # changes -- and not at all on renders that would not have logged anything.\n    def describe():\n        best = max(rows, key=lambda row: row[\"score\"])\n        return f\"{len(rows)} rows, best={best['name']}:{best['score']}\"\n\n    use_debug_value(describe)\n\n    async def add_row(event=None):\n        set_rows([*rows, {\"name\": f\"row {len(rows)}\", \"score\": len(rows) * 7}])\n\n    return html.div(\n        html.h1(f\"{len(rows)} rows\"),\n        html.button({\"onClick\": add_row}, \"Add row\"),\n    )\n
# TODO\n

Keep the message honest about what it reads. A function that reads no state is computed once and then logs nothing ever again, which usually reads as \"the hook stopped working\" rather than as correct memoization.

"},{"location":"reference/use-debug-value/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-debug-value/#nothing-is-logged","title":"Nothing is logged","text":"

Almost always debug mode. The REACTPY_DEBUG environment variable is read when reactpy.config is first imported, so setting os.environ after ReactPy is already imported has no effect \u2014 start the process with REACTPY_DEBUG=1, or set the variable before importing anything from reactpy.

The option object can also be flipped at any point, which is the version that works from inside a test or a REPL:

from reactpy.config import REACTPY_DEBUG\n\nREACTPY_DEBUG.current = True\n

If debug is on and you still see nothing, the logger is being filtered. ReactPy logs at DEBUG level under reactpy.core.hooks; a configuration that shows only warnings will hide these lines.

"},{"location":"reference/use-debug-value/#it-logs-on-every-render","title":"It logs on every render","text":"

Your message changes every render. A timestamp, id(self), a freshly built object compared by identity, or a counter you increment during rendering all do this. Make the message describe state rather than the render.

"},{"location":"reference/use-debug-value/#it-stopped-logging","title":"It stopped logging","text":"

The message has not changed since the last line, which is the hook working as designed. If you expected a change, either the value genuinely did not change or your function is memoized on dependencies that never move \u2014 pass dependencies=None briefly to confirm which.

"},{"location":"reference/use-debug-value/#my-component-fails-to-render-after-adding-it","title":"My component fails to render after adding it","text":"

You passed something whose != raises. The comparison is not guarded, so the exception becomes a render error. Pass plain data instead, and format it lazily in a function if it is large.

"},{"location":"reference/use-deferred-value/","title":"Use deferred value","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-effect/","title":"Use Effect","text":""},{"location":"reference/use-effect/#overview","title":"Overview","text":"

use_effect is a ReactPy hook that lets you synchronize a component with an external system.

use_effect(effect)\nuse_effect(effect, dependencies=[value])\n
"},{"location":"reference/use-effect/#reference","title":"Reference","text":""},{"location":"reference/use-effect/#use_effecteffect-dependencies","title":"use_effect(effect, dependencies=...)","text":"

Call use_effect at the top level of your component to run an effect after rendering.

from reactpy import use_effect\n
"},{"location":"reference/use-effect/#parameters","title":"Parameters","text":"
  • effect: A function taking no arguments. ReactPy calls it after the component renders. It may return a clean-up function (also taking no arguments), which ReactPy runs before the next time this effect runs and again when the component unmounts.

    The effect must be pure, and it must not be declared async def \u2014 ReactPy raises for that; use use_async_effect instead.

  • dependencies (optional): Controls when the effect re-runs. Unlike React, this argument has four distinct settings, and the default is not React's:

    Value Behaviour (verified) omitted (...) Inferred. ReactPy reads the free variables of your closure (core/hooks.py, _try_to_infer_closure_values) and re-runs when any of them changes. A closure that reads nothing runs once. dependencies=[] Runs once, after mount. dependencies=[a, b] Runs when a or b changes. dependencies=None Runs after every render.

    \"Changes\" means strictly_equal (core/hooks.py), not is: the two values must share a type, two locally-defined functions or lambdas are compared by bytecode, and everything else falls through to == with is as the last resort. A rebuilt list or dict with equal contents does not re-run the effect. Use Memo documents the same function in full.

"},{"location":"reference/use-effect/#returns","title":"Returns","text":"

None when given the effect, or a decorator when called without one.

"},{"location":"reference/use-effect/#caveats","title":"Caveats","text":"
  • The default is inference, not \"every render\". In React, omitting the dependency array means the effect runs after every render. In ReactPy it means \"re-run when the variables my closure reads change\". Code ported from React will therefore behave differently \u2014 pass dependencies=None if you really want every-render behaviour, though usually you want to be explicit instead.
  • Inference is structural, not declared, and it does not compare by id. A closure that happens to read an extra local variable gains an extra dependency, and the comparison is strictly_equal \u2014 type, then bytecode for local functions, then == \u2014 so a rebuilt-but-equal container counts as unchanged. If the effect runs more or less often than you expect, look at which names it actually reads, and then at how those values compare.
  • Effects run on the server, after rendering, not in the browser. There is no DOM to touch: document and window do not exist here. There is no document or window to reach for, and no ReactPy equivalent of attaching a listener to a node \u2014 see Responding to Events for the supported way to react to the browser.
  • Children's effects run before their parent's, and siblings run in reverse order. Verified output: ['child effect b', 'child effect a', 'parent effect']. Two effects in one component run in declaration order. Do not rely on React's parent-first or source-order guarantees.
  • Passing async def to use_effect fails loudly: TypeError: use_effect does not support async functions. Use use_async_effect instead. The error lands in the model's error field and the component stops rendering.
  • An exception raised inside an effect body is logged on the server only; the model carries no error and the UI looks unchanged. Check the server log.
  • Effects are not a substitute for computing values. Derive them during render or with use_memo.
  • There is no use_layout_effect, use_insertion_effect, or use_imperative_handle. Nothing runs synchronously during the render phase.
"},{"location":"reference/use-effect/#usage","title":"Usage","text":""},{"location":"reference/use-effect/#importing-use_effect","title":"Importing use_effect","text":"
from reactpy import use_effect\n
"},{"location":"reference/use-effect/#describing-an-effect","title":"Describing an effect","text":"

Pass the setup function directly:

app.pydata.py Run
@component\ndef live_feed():\n    latest, set_latest = use_state(\"waiting for a message...\")\n\n    def effect():\n        # Runs once on mount; the returned function runs on unmount.\n        unsubscribe = ticker.subscribe(\"news\", set_latest)\n        return unsubscribe\n\n    use_effect(effect, dependencies=[])\n\n    async def handle_click(event):\n        ticker.publish(\"news\", \"Hello!\")\n\n    return html.div(\n        html.h1(latest),\n        html.button(\n            {\"onClick\": handle_click},\n            \"Publish\",\n        ),\n    )\n
\"\"\"A stand-in for any external system with subscribe/unsubscribe semantics.\"\"\"\n\n\nclass Ticker:\n    def __init__(self):\n        self.topics = {}\n\n    def subscribe(self, topic, callback):\n        self.topics.setdefault(topic, []).append(callback)\n\n        def unsubscribe():\n            self.topics[topic].remove(callback)\n\n        return unsubscribe\n\n    def publish(self, topic, message):\n        for callback in list(self.topics.get(topic, [])):\n            callback(message)\n\n    def count(self):\n        return sum(len(v) for v in self.topics.values())\n\n\nticker = Ticker()\n
# TODO\n

Or use it as a decorator, which is equivalent:

@use_effect\ndef effect():\n    print(\"mounted\")\n
"},{"location":"reference/use-effect/#telling-reactpy-when-to-re-run-the-effect","title":"Telling ReactPy when to re-run the effect","text":"

Pass a dependency list to re-run only when something relevant changes:

app.pydata.py Run
@component\ndef live_feed():\n    topic, set_topic = use_state(\"news\")\n    latest, set_latest = use_state(\"\")\n\n    def effect():\n        # Re-runs whenever `topic` changes; the returned clean-up unsubscribes\n        # the previous topic first, so you never keep two subscriptions.\n        unsubscribe = ticker.subscribe(topic, set_latest)\n        return unsubscribe\n\n    use_effect(effect, dependencies=[topic])\n\n    async def handle_topic_click(event):\n        set_topic(\"sports\" if topic == \"news\" else \"news\")\n\n    async def handle_click(event):\n        ticker.publish(topic, \"ping\")\n\n    return html.div(\n        html.p(f\"Subscribed to: {topic}\"),\n        html.p(latest or \"(nothing yet)\"),\n        html.button(\n            {\"onClick\": handle_topic_click},\n            \"Switch topic\",\n        ),\n        html.button({\"onClick\": handle_click}, \"Publish\"),\n    )\n
\"\"\"A stand-in for any external system with subscribe/unsubscribe semantics.\"\"\"\n\n\nclass Ticker:\n    def __init__(self):\n        self.topics = {}\n\n    def subscribe(self, topic, callback):\n        self.topics.setdefault(topic, []).append(callback)\n\n        def unsubscribe():\n            self.topics[topic].remove(callback)\n\n        return unsubscribe\n\n    def publish(self, topic, message):\n        for callback in list(self.topics.get(topic, [])):\n            callback(message)\n\n    def count(self):\n        return sum(len(v) for v in self.topics.values())\n\n\nticker = Ticker()\n
# TODO\n

Switching topic tears down the old subscription before creating the new one \u2014 verified, the subscriber count stays at 1 across switches and drops to 0 on unmount.

"},{"location":"reference/use-effect/#omitting-the-dependencies-argument","title":"Omitting the dependencies argument","text":"
use_effect(effect)               # inferred from what the closure reads\n

The example below makes inference visible. The effect reads count but not label, so renaming re-renders the component without re-running the effect:

app.py Run
@component\ndef logger():\n    count, set_count = use_state(0)\n    label, set_label = use_state(\"counter\")\n    runs, set_runs = use_state([])\n\n    def effect():\n        # No `dependencies` argument, so ReactPy infers them from the names\n        # this closure reads. Only `count` is a real dependency here -- the\n        # setter is stable, and `label` is not read at all. So pressing\n        # Rename re-renders the component but does NOT re-run this effect.\n        set_runs(lambda previous: [*previous, f\"effect ran with count={count}\"])\n\n    use_effect(effect)\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_label_click(event):\n        set_label(label + \"!\")\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_count_click}, \"Add 1\"),\n        html.button({\"onClick\": handle_label_click}, \"Rename\"),\n        html.p(f\"Label: {label}\"),\n        html.ul([html.li({\"key\": i}, line) for i, line in enumerate(runs)]),\n    )\n
# TODO\n

Pressing Rename twice adds no new log line \u2014 verified. Be aware this works because of which names the closure reads; it is implicit, so many teams prefer to spell the list out.

"},{"location":"reference/use-effect/#passing-an-empty-dependency-array","title":"Passing an empty dependency array","text":"
use_effect(effect, dependencies=[])   # once, after mount\n

For mount-only work \u2014 subscribing, starting a timer, reading a config \u2014 say so explicitly.

"},{"location":"reference/use-effect/#passing-none","title":"Passing None","text":"
use_effect(effect, dependencies=None)  # after every render\n

This is the closest ReactPy gets to React's \"no array\" behaviour. It re-runs the effect after every render, verified. Reach for it rarely: it usually means the effect is doing work that belongs in render or in use_memo.

"},{"location":"reference/use-effect/#adding-clean-up","title":"Adding clean-up","text":"

Return a function from the effect. ReactPy calls it before the next run of that effect and when the component unmounts:

app.py Run
@component\ndef app():\n    connected, set_connected = use_state(False)\n\n    async def handle_connected_click(event):\n        set_connected(not connected)\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_connected_click},\n            \"Connect\" if not connected else \"Disconnect\",\n        ),\n        *([] if not connected else [connection()]),\n    )\n
# TODO\n

Connecting and disconnecting runs the pair in order \u2014 verified: connected, disconnected, connected across connect / disconnect / reconnect.

Clean-up is the difference between a leak and a working app

Because ReactPy components live in long-lived server processes, a forgotten subscription is not a brief flash of stale data \u2014 it is a permanent leak that outlives every browser tab that opened it. Every subscribe, every timer, every held connection needs a clean-up function.

"},{"location":"reference/use-effect/#effects-and-the-request-lifecycle","title":"Effects and the request lifecycle","text":"

An effect runs in the connection's own task, after the render that scheduled it. It can call setters, and those schedule further renders. Two consequences:

  • An effect that awaits gives the event loop control. Long awaits belong in use_async_effect, which is built for it.
  • Setting state from an effect that depends on that same state loops forever. If an effect seems to spin, read what it sets and what it depends on.
"},{"location":"reference/use-effect/#reading-the-latest-state-inside-an-effect","title":"Reading the latest state inside an effect","text":"

An effect closes over the render that created it. If it then runs for a long time \u2014 an interval callback, a subscription handler \u2014 it can act on values that are no longer current. Two ways to handle it:

  • Put the value in the dependency list, so a change tears the effect down and starts a new one with fresh values.
  • Keep the value in a use_ref box and read ref.current when you need it. The box persists across renders, so readers see the newest value \u2014 as long as the write happens somewhere legal. Do not assign ref.current from the component body: a render-phase write is exactly the impurity Use Ref warns about, and ReactPy has no Strict Mode to catch it. Assign it where the value is already produced \u2014 the event handler that changed it \u2014 or in its own effect, which runs after the render.

If an effect reads state it does not declare, that is usually a sign the effect should be re-created when that state changes \u2014 reach for the dependency list first.

"},{"location":"reference/use-imperative-handle/","title":"Use imperative handle","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-insertion-effect/","title":"Use insertion effect","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-layout-effect/","title":"Use layout effect","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-location/","title":"Use Location","text":""},{"location":"reference/use-location/#overview","title":"Overview","text":"

use_location is a ReactPy hook that returns the URL the browser is currently showing.

location = use_location()\n

Your components run on the server and know nothing about the browser unless you ask. use_location is how you ask: it reports the page the visitor is looking at, so navigation, breadcrumbs, and query-driven views can be built from it.

"},{"location":"reference/use-location/#reference","title":"Reference","text":""},{"location":"reference/use-location/#use-location","title":"use_location()","text":"

Call it at the top level of a component. It takes no arguments.

from reactpy import use_location\n
"},{"location":"reference/use-location/#returns","title":"Returns","text":"

A reactpy.types.Location \u2014 a plain dataclass with two fields, both required:

  • path: The URL path, mirroring window.location.pathname. For example \"/reports/2024/q1\".
  • query_string: The query string, mirroring window.location.search \u2014 including the leading \"?\". For example \"?tab=2&user=ada\", or \"\" when there is no query.

It is mutable and compares by value. Import the type with from reactpy.types import Location; it is not exported from the reactpy namespace.

"},{"location":"reference/use-location/#caveats","title":"Caveats","text":"
  • The \"?\" is part of the value. parse_qs(location.query_string) mis-parses the first parameter, producing a key \"?tab\" instead of \"tab\". Strip it: parse_qs(location.query_string.lstrip(\"?\")). See the example below.
  • It is not the ASGI scope's path. use_scope()[\"path\"] is the ReactPy websocket endpoint \u2014 \"/reactpy/\" \u2014 for every component on every page. use_location().path is the page the visitor is on. Verified in Chrome: visiting /some/deep/page?tab=2&user=ada gives Location(path='/some/deep/page', query_string='?tab=2&user=ada') while the scope still says \"/reactpy/\".
  • The browser reports it, so it is untrusted. The value arrives as parameters on the websocket URL and ReactPy does not validate it against your routes. Treat it as input: match it, never interpolate it into a \"href\" or a filesystem path without checking.
  • It needs a live connection. Called in a test's plain Layout render, it raises RuntimeError: No backend established a connection.. See working without a connection.
  • It does not exist under client-side rendering. With ReactPyCsr, Python runs in the browser but no ReactPy connection object is established, so the hook raises the same RuntimeError. Verified in Chrome against a CSR app. Read window.location through JavaScript there instead.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return.
"},{"location":"reference/use-location/#usage","title":"Usage","text":""},{"location":"reference/use-location/#reading-query-parameters","title":"Reading query parameters","text":"

query_string arrives exactly as the browser shows it, which means the leading \"?\" is attached to the first parameter's name. That is the one detail that trips people up.

app.py Run
@component\ndef product_page():\n    location = use_location()\n\n    # `query_string` mirrors `window.location.search`, so it *keeps* the\n    # leading \"?\". Strip it first or the first parameter gets a mangled name:\n    # parse_qs(\"?tab=2&sort=price\") yields the key \"?tab\", not \"tab\".\n    params = parse_qs(location.query_string.lstrip(\"?\"))\n    tab = params.get(\"tab\", [\"overview\"])[0]\n    sort = params.get(\"sort\", [\"name\"])[0]\n\n    return html.div(\n        html.h1(f\"Products \u2014 {tab}\"),\n        html.p(f\"sorted by: {sort}\"),\n        html.p(f\"raw query string: {location.query_string!r}\"),\n        html.p(f\"path: {location.path}\"),\n    )\n
# TODO\n

With ?tab=sales&sort=price this renders \"Products \u2014 sales\", sorted by price. With no query string at all it falls back to the defaults.

"},{"location":"reference/use-location/#building-navigation-from-the-current-path","title":"Building navigation from the current path","text":"

Splitting the path gives you the trail from the site root to wherever the visitor is.

app.py Run
@component\ndef breadcrumb():\n    location = use_location()\n\n    # `path` mirrors `window.location.pathname`, so a component rendered on the\n    # ReactPy server can describe the page the browser is actually on -- useful\n    # for navigation that needs to know where it is.\n    parts = [part for part in location.path.split(\"/\") if part]\n\n    return html.nav(\n        html.a({\"href\": \"/\"}, \"home\"),\n        *[\n            html.span(\" / \", html.a({\"href\": \"/\" + \"/\".join(parts[: i + 1])}, part))\n            for i, part in enumerate(parts)\n        ],\n    )\n
# TODO\n

On /reports/2024/q1 this produces links to /reports, /reports/2024, and /reports/2024/q1. Build the URLs yourself rather than pasting path segments in raw if they can contain anything you did not choose.

"},{"location":"reference/use-location/#working-without-a-connection","title":"Working without a connection","text":"

A component that reads the location should say what it shows when there is none \u2014 a unit test, a reactpy_to_string call, or a CSR page all take this path.

@component\ndef page_title():\n    try:\n        location = use_location()\n    except RuntimeError:\n        return html.h1(\"(no browser yet)\")\n    return html.h1(f\"at {location.path}\")\n

Verified both ways through Layout: with no provider it renders the fallback; with one it renders the path.

"},{"location":"reference/use-location/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-location/#my-query-parameters-never-match","title":"My query parameters never match","text":"

The leading \"?\". parse_qs(\"?tab=2\") gives {\"?tab\": [\"2\"]}. Strip it before parsing.

"},{"location":"reference/use-location/#it-says-reactpy","title":"It says /reactpy/","text":"

You are reading use_scope()[\"path\"], which is the websocket endpoint. Use use_location().path for the page.

"},{"location":"reference/use-location/#it-raises-in-my-tests","title":"It raises in my tests","text":"

Expected: a bare Layout render has no connection. Either wrap the call as shown above, or inject one \u2014 see use_connection.

"},{"location":"reference/use-location/#it-raises-inside-a-reactpycsr-app","title":"It raises inside a ReactPyCsr app","text":"

Client-side rendering establishes no connection, so there is nothing to report. Read the URL from JavaScript instead, or move that component server-side.

"},{"location":"reference/use-memo/","title":"Use Memo","text":""},{"location":"reference/use-memo/#overview","title":"Overview","text":"

use_memo is a ReactPy hook that caches the result of a calculation, recomputing it only when one of its listed dependencies changes.

cached_value = use_memo(calculate_value, dependencies=[dep_1, dep_2])\n

Use it when a calculation is genuinely expensive and the inputs it depends on change less often than the component renders. It caches a value; it does not stop anything from rendering.

What use_memo does not do in ReactPy

It does not prevent a child component from re-rendering. ReactPy has no render bailout and no memo: a child's body runs whenever its parent re-renders, even with identical props, and even if you cache the child element itself with use_memo. Texts ported from React that use useMemo to \"skip re-rendering of components\" describe a mechanism that is not here. use_memo saves the computation, nothing else.

"},{"location":"reference/use-memo/#reference","title":"Reference","text":""},{"location":"reference/use-memo/#use-memo","title":"use_memo(function, dependencies)","text":"

Call use_memo at the top level of your component to cache a calculation.

from reactpy import use_memo\n
"},{"location":"reference/use-memo/#parameters","title":"Parameters","text":"
  • function: A function taking no arguments that returns the value you want. ReactPy calls it only when it must. It must be pure: same inputs, same output, no side effects.
  • dependencies: The values the calculation reads. ReactPy recomputes when any of them changes. It accepts three shapes:

    You pass Meaning dependencies=[a, b] Recompute when a or b changes. omitted (the default) ReactPy reads the function's closure and infers the free variables. dependencies=None Recompute on every render.
"},{"location":"reference/use-memo/#returns","title":"Returns","text":"

The current value \u2014 either freshly computed, or the one cached from the last render in which a dependency changed.

"},{"location":"reference/use-memo/#caveats","title":"Caveats","text":"
  • The decorated name holds the value, not a function. The decorator form is the call form under another name, and the call form returns the value -- so after decorating, my_value is the cached result, and calling it raises TypeError: 'int' object is not callable. To keep the name callable, give the inner function its own name and pass it to use_memo.
  • Caching lasts for the lifetime of the component. A cached value survives as long as the component stays mounted; there is no way to invalidate it from inside. Mounting the component under a new \"key\" throws it away, because that unmounts and remounts the component and every hook starts over.
  • You must list every value the function reads. ReactPy does not check, and does not warn. A missing dependency produces a silently stale value, not an error. See I removed a dependency and nothing complained.
  • Comparison is not plain identity. Dependencies go through reactpy.core.hooks.strictly_equal, which requires the two values to be the same type first, compares two locally-defined functions or lambdas by their code objects (so a fresh, structurally identical lambda counts as unchanged), and otherwise compares with ==, falling back to is only if that raises. Two practical consequences: a freshly built list or dict with equal contents does not force a recompute, and a value that changes type between renders \u2014 1 then 1.0, or 0 then False \u2014 always counts as changed.
  • Mutating a dependency in place defeats the cache. Change an object and pass the same object back, and it compares equal to itself, so the cached value is kept. Build a new object instead \u2014 see Updating Objects in State.
  • A ref never invalidates a memo. Writing to some_ref.current does not change how the ref compares, so listing a use_ref box in dependencies accomplishes nothing.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check; a hook that runs on some renders but not others produces a scrambled component and a cryptic IndexError.
"},{"location":"reference/use-memo/#usage","title":"Usage","text":""},{"location":"reference/use-memo/#skipping-an-expensive-calculation","title":"Skipping an expensive calculation","text":"

Give use_memo the function that does the work, and list what it reads. The leaderboard sorts seven rows here, which is nothing \u2014 but it is the same shape you would use for a sort over ten thousand, and the counter on screen shows exactly how often the sort runs.

Press Change highlight: the component re-renders, the sort does not run, and sorted 1 time(s) stays put. Press Everyone scores +1: players changes, so the sort runs again.

app.py Run
ROSTER = [\n    (\"ada\", 91),\n    (\"grace\", 120),\n    (\"alan\", 77),\n    (\"edsger\", 64),\n    (\"katherine\", 143),\n    (\"dorothy\", 88),\n    (\"margaret\", 155),\n]\n\nsorts = []  # demo-only: lets you *see* how often `rank` actually runs\n\n\n@component\ndef leaderboard():\n    players, set_players = use_state(\n        [{\"name\": name, \"score\": score} for name, score in ROSTER]\n    )\n    highlight, set_highlight = use_state(\"ada\")\n\n    def rank():\n        # The work you want to skip: sorting the whole table. Listing\n        # `dependencies=[players]` runs this only when the roster changes, so\n        # changing the highlight leaves the sorted list alone.\n        sorts.append(1)\n        return sorted(players, key=lambda player: -player[\"score\"])\n\n    ranked = use_memo(rank, dependencies=[players])\n\n    async def handle_highlight_click(event):\n        set_highlight(\"grace\" if highlight == \"ada\" else \"ada\")\n\n    async def handle_players_click(event):\n        set_players([{**player, \"score\": player[\"score\"] + 1} for player in players])\n\n    return html.div(\n        html.h1(\"Leaderboard\"),\n        html.ol([html.li(f\"{player['name']}: {player['score']}\") for player in ranked]),\n        html.p(f\"sorted {len(sorts)} time(s)\"),\n        html.p(f\"highlighted: {highlight}\"),\n        html.button(\n            {\"onClick\": handle_highlight_click},\n            \"Change highlight\",\n        ),\n        html.button(\n            {\"onClick\": handle_players_click},\n            \"Everyone scores +1\",\n        ),\n    )\n
# TODO\n

The counter (sorts) and both buttons exist only to make the caching observable. In real code you would keep the use_memo call and drop the rest.

"},{"location":"reference/use-memo/#how-expensive-is-expensive","title":"How expensive is \"expensive\"?","text":"

Enough to measure. Sorting a small list, building a dict from a handful of items, or formatting a string is not worth it \u2014 the hook has a cost of its own, and a dependency list you must keep correct is a cost too.

Reach for it when you recognize one of these:

  • A sort, filter, or search over a large collection.
  • Parsing: JSON, CSV, a template, a source file.
  • Building a structure that then gets walked repeatedly.
  • Anything you have already timed and found annoying.

If you have not measured it, prefer the plain calculation. Adding use_memo later is a one-line change precisely because the calculation is already in its own function.

"},{"location":"reference/use-memo/#should-you-add-use_memo-everywhere","title":"Should you add use_memo everywhere?","text":"

No. Each one is a dependency list you now have to maintain correctly, and a wrong list is a silent bug rather than a loud one. Memoize where you have measured, not everywhere.

"},{"location":"reference/use-memo/#omitting-the-dependency-list","title":"Omitting the dependency list","text":"

Leave out dependencies and ReactPy infers them from the free variables in the function's closure. This is a ReactPy feature with no React equivalent, and it removes the most common class of mistake, because a value the function reads is by definition a free variable.

The word count below reads text but not upper, so toggling the case re-renders the component without re-running the count. Watch counted 1 time(s).

app.py Run
counts = []  # demo-only: lets you *see* how often `count` actually runs\n\n\n@component\ndef word_count():\n    text, set_text = use_state(\"reactpy is a python ui library\")\n    upper, set_upper = use_state(False)\n\n    # No `dependencies` argument at all. ReactPy inspects the function's\n    # closure and infers the free variables it reads -- here that is `text`,\n    # but not `upper`, so toggling the case does not redo the count.\n    def count():\n        counts.append(1)\n        return len(text.split())\n\n    total = use_memo(count)\n\n    async def handle_upper_click(event):\n        set_upper(not upper)\n\n    async def handle_text_change(event):\n        set_text(await event.target.value)\n\n    return html.div(\n        html.h1(f\"{total} words\"),\n        html.p(text.upper() if upper else text),\n        html.p(f\"counted {len(counts)} time(s)\"),\n        html.button({\"onClick\": handle_upper_click}, \"Toggle case\"),\n        html.input(\n            {\n                \"value\": text,\n                \"onChange\": handle_text_change,\n            }\n        ),\n    )\n
# TODO\n

Two catches. Inference only sees closure variables, so a module-level name, or anything reached through an object, is invisible to it. And inference is a convenience for a function defined inside the component \u2014 pass the list explicitly when the function comes from elsewhere.

"},{"location":"reference/use-memo/#recomputing-on-every-render","title":"Recomputing on every render","text":"

dependencies=None opts out of caching entirely: the function runs on every render. That is what you want when the calculation reads something no dependency list can express, such as a module-level mutable.

The label below reads config, which lives at module scope. Inference only sees closure variables, so it would pick up count and suffix and miss config entirely \u2014 the label would keep a stale prefix whenever config changed. dependencies=None sidesteps the question: label() runs each render.

app.py Run
# Module-level, so it is *not* a closure variable and ReactPy cannot infer it\n# as a dependency.\nconfig = {\"prefix\": \"count\"}\n\ncalls = []  # demo-only\n\n\n@component\ndef counter():\n    count, set_count = use_state(0)\n    suffix, set_suffix = use_state(\"\")\n\n    def label():\n        # Reads `config`, which ReactPy cannot see as a dependency. With\n        # inferred (i.e. empty) dependencies this would be computed once and\n        # then never notice an edited `config`. `dependencies=None` says: this\n        # depends on the render itself, so recompute every time.\n        calls.append(1)\n        return f\"{config['prefix']}: {count}{suffix}\"\n\n    async def handle_count_click(event):\n        set_count(count + 1)\n\n    async def handle_suffix_click(event):\n        set_suffix(\"!\")\n\n    return html.div(\n        html.h1(use_memo(label, dependencies=None)),\n        html.p(f\"label() ran {len(calls)} time(s)\"),\n        html.button({\"onClick\": handle_count_click}, \"Count +1\"),\n        html.button({\"onClick\": handle_suffix_click}, \"Set suffix\"),\n    )\n
# TODO\n
"},{"location":"reference/use-memo/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-memo/#my-memo-value-never-updates","title":"My memo value never updates","text":"

In order of likelihood:

  • You mutated a dependency instead of replacing it. Same object in, equal to itself, cached value kept. Build a new object.
  • You listed a ref as a dependency. Mutating some_ref.current never invalidates anything. Use state, or list the value itself.
  • The dependency compares equal to its replacement. Two [1, 2, 3] lists are equal, so rebuilding one each render changes nothing.
  • You left the value out of the list. See the next item.
"},{"location":"reference/use-memo/#i-removed-a-dependency-and-nothing-complained","title":"I removed a dependency and nothing complained","text":"

Because nothing checks. The example below omits tax_rate from the list, and it seems to work: press Add 10% tax twice and the rate climbs while the total stays frozen at whatever a 0% rate produced. Then press Add an item \u2014 a real dependency \u2014 and the total jumps to 12.00, silently incorporating both tax increases you could see never happened.

app.py Run
@component\ndef totals():\n    items, set_items = use_state([1, 2, 3])\n    tax_rate, set_tax_rate = use_state(0.0)\n\n    # `tax_rate` is read here but deliberately left out of `dependencies`.\n    # ReactPy does not complain. The cached total just keeps the rate from the\n    # render that last computed it, so \"Add 10% tax\" appears to do nothing --\n    # until something that *is* a dependency changes, and the stale number\n    # jumps to the right one all at once.\n    def grand_total():\n        return sum(items) * (1 + tax_rate)\n\n    total = use_memo(grand_total, dependencies=[items])\n\n    async def handle_tax_rate_click(event):\n        set_tax_rate(tax_rate + 0.1)\n\n    async def handle_items_click(event):\n        set_items([*items, len(items) + 1])\n\n    return html.div(\n        html.h1(f\"total: {total:.2f}\"),\n        html.p(f\"tax rate: {tax_rate:.0%}\"),\n        html.button({\"onClick\": handle_tax_rate_click}, \"Add 10% tax\"),\n        html.button(\n            {\"onClick\": handle_items_click},\n            \"Add an item\",\n        ),\n    )\n
# TODO\n

This is the failure mode to watch for: a stale value that eventually arrives, so it reads as lag rather than as a bug. List everything the function reads, or omit dependencies and let ReactPy infer them.

"},{"location":"reference/use-memo/#my-calculation-runs-on-every-render-anyway","title":"My calculation runs on every render anyway","text":"
  • You passed dependencies=None.
  • A dependency really is new each render \u2014 commonly an object constructed in the component body above the use_memo call.
  • A dependency is an object whose == is identity-based and which you rebuild each time.
"},{"location":"reference/use-memo/#i-wanted-to-stop-a-child-from-re-rendering","title":"I wanted to stop a child from re-rendering","text":"

use_memo cannot do this in ReactPy, and caching the child element does not help either \u2014 a cached element is still re-rendered. ReactPy re-renders children unconditionally. If a child is genuinely expensive, the levers are to make its own calculations cheap (memoize inside it) or to avoid rendering it at all behind a conditional.

"},{"location":"reference/use-reducer/","title":"Use Reducer","text":""},{"location":"reference/use-reducer/#overview","title":"Overview","text":"

use_reducer is a ReactPy hook that holds state behind a function: you send an action, and a reducer turns the current state plus that action into the next state.

state, dispatch = use_reducer(reducer, initial_value)\n

Reach for it when a component's updates are more than \"set this to that\" \u2014 several fields that change together, transitions that depend on what is already there, or update logic you want out of the component so it can be tested as a plain function.

"},{"location":"reference/use-reducer/#reference","title":"Reference","text":""},{"location":"reference/use-reducer/#use-reducer","title":"use_reducer(reducer, initial_value)","text":"

Call use_reducer at the top level of your component.

from reactpy import use_reducer\n
"},{"location":"reference/use-reducer/#parameters","title":"Parameters","text":"
  • reducer: (state, action) -> new_state. A pure function: given the same two arguments it returns the same result, and it does not touch anything outside them. It must return a new object rather than modifying state \u2014 see the reducer must not mutate.
  • initial_value: The state to start with. Pass a function taking no arguments to build it lazily; ReactPy calls it on the first render only. See avoiding recreating the initial state.
"},{"location":"reference/use-reducer/#returns","title":"Returns","text":"

A two-item tuple, normally unpacked:

  • state: The current state, as of the start of this render.
  • dispatch: A function that takes an action and schedules the update. The same object on every render \u2014 verified, one dispatcher across four renders \u2014 so it is safe to pass into a child or a dependency list.
"},{"location":"reference/use-reducer/#caveats","title":"Caveats","text":"
  • Define the reducer at module level. ReactPy builds the dispatcher once, on the first render, and the dispatcher closes over the reducer you passed then. A reducer defined inside the component is a new function object each render and ReactPy keeps using the first one, so any value it closes over is frozen at its first-render value. Measured: four renders, four distinct reducer objects passed in, and the state kept reporting seen_n=0. This differs from React, which re-reads the reducer every render. Anything the reducer needs must arrive in state or action. See The reducer never changes.
  • use_reducer is use_state underneath. The dispatcher calls the state setter with lambda last_state: reducer(last_state, action), so every rule from use_state applies here \u2014 replaced, never merged; equal values schedule no render; mutations vanish silently. See The reducer must not mutate.
  • The reducer runs eagerly, inside dispatch \u2014 not later during the render. Verified by ordering statements around the call. A reducer that raises is swallowed the same way any event-handler error is: logged on the server as Failed to execute event handler, with nothing in the browser console.
  • Actions are yours; ReactPy never inspects them. A dict with a \"type\" key is convention, not a requirement \u2014 you can dispatch a string, a number, or a tuple.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check; a hook that runs on some renders but not others produces a scrambled component and a cryptic IndexError.
"},{"location":"reference/use-reducer/#usage","title":"Usage","text":""},{"location":"reference/use-reducer/#managing-complex-state-with-a-reducer","title":"Managing complex state with a reducer","text":"

One reducer holds the whole shape, and each behaviour is a branch chosen by the action. Because it is an ordinary function of two arguments, you can test it without rendering anything.

Press Add task twice, task 1, then Clear done, and watch the reducer's branches run:

app.py Run
def todos_reducer(state, action):\n    \"\"\"One function per behaviour, chosen by the action's type.\n\n    Defined at module level on purpose: ReactPy builds the dispatcher once, on\n    the first render, and keeps that reducer -- a reducer defined inside the\n    component would keep its first-render closure forever.\n\n    Every branch returns a NEW dict. Returning the mutated `state` would be\n    dropped without a word.\n    \"\"\"\n    if action[\"type\"] == \"add\":\n        return {\n            **state,\n            \"items\": [*state[\"items\"], {\"text\": action[\"text\"], \"done\": False}],\n        }\n    if action[\"type\"] == \"toggle\":\n        items = [\n            {**item, \"done\": not item[\"done\"]} if i == action[\"index\"] else item\n            for i, item in enumerate(state[\"items\"])\n        ]\n        return {**state, \"items\": items}\n    if action[\"type\"] == \"clear_done\":\n        return {**state, \"items\": [i for i in state[\"items\"] if not i[\"done\"]]}\n    return state\n\n\n@component\ndef todo_list():\n    state, dispatch = use_reducer(todos_reducer, {\"items\": []})\n\n    async def handle_add_click(event):\n        dispatch({\"type\": \"add\", \"text\": f\"task {len(state['items']) + 1}\"})\n\n    async def handle_clear_done_click(event):\n        dispatch({\"type\": \"clear_done\"})\n\n    def make_toggle(index):\n        # A factory rather than a bound-argument lambda: a handler must be a\n        # coroutine, and a coroutine cannot be a lambda.\n        async def toggle(event):\n            dispatch({\"type\": \"toggle\", \"index\": index})\n\n        return toggle\n\n    return html.div(\n        html.h1(f\"To-do ({sum(1 for i in state['items'] if not i['done'])} left)\"),\n        html.ul(\n            [\n                html.li(\n                    {\"key\": i},\n                    html.button(\n                        {\"onClick\": make_toggle(i)},\n                        state[\"items\"][i][\"text\"],\n                    ),\n                )\n                for i in range(len(state[\"items\"]))\n            ]\n        ),\n        html.button(\n            {\"onClick\": handle_add_click},\n            \"Add task\",\n        ),\n        html.button({\"onClick\": handle_clear_done_click}, \"Clear done\"),\n    )\n
# TODO\n

Three things to notice in todos_reducer. It is at module level, so the closure trap above never arises. Every branch builds a new dict with {**state, ...} \u2014 none of them mutate. And the fallback return state makes an unhandled action a deliberate no-op rather than a crash.

"},{"location":"reference/use-reducer/#the-reducer-must-not-mutate","title":"The reducer must not mutate","text":"

This is the failure mode most likely to cost you an afternoon, and it is silent. A reducer that modifies state and returns it hands back the very object ReactPy already holds; the comparison finds them equal and schedules nothing. No error, no warning, nothing in the console \u2014 the button just stops working while your data quietly changes underneath.

app.py Run
def counter_reducer(state, action):\n    # Always return a NEW object. `use_reducer` is built on `use_state`, which\n    # compares what you return against what it already holds and schedules no\n    # render when the two are equal. Mutating `state` here and returning it\n    # would update the data, schedule nothing, and fail with no error and\n    # nothing in the browser console.\n    return {**state, \"count\": state[\"count\"] + 1}\n\n\n@component\ndef counter():\n    state, dispatch = use_reducer(counter_reducer, {\"count\": 0})\n\n    async def handle_click(event):\n        dispatch({\"type\": \"inc\"})\n\n    return html.div(\n        html.h1(state[\"count\"]),\n        html.button({\"onClick\": handle_click}, \"Add 1\"),\n    )\n
# TODO\n

Return {**state, \"count\": state[\"count\"] + 1}, not state[\"count\"] += 1; return state. The same applies one level down: {**state, \"items\": state[\"items\"] + [new]}, never state[\"items\"].append(new). See Updating Objects in State and Updating Arrays in State.

"},{"location":"reference/use-reducer/#the-reducer-never-changes","title":"The reducer never changes","text":"

Because the first reducer is the only one that ever runs, a reducer that closes over component state reads a value frozen at the first render \u2014 permanently, with no warning.

Run this, press Add step twice, then Bump step, then Add step again:

app.py Run
@component\ndef stepper():\n    step, set_step = use_state(1)\n\n    def reducer(state, action):\n        # BUG: this reducer closes over `step`, but ReactPy builds the\n        # dispatcher once, on the first render, and keeps that reducer\n        # forever. Bump `step` to 3 and this keeps adding 1.\n        return {**state, \"total\": state[\"total\"] + step}\n\n    state, dispatch = use_reducer(reducer, {\"total\": 0})\n\n    async def handle_click(event):\n        dispatch({\"type\": \"add\"})\n\n    async def handle_step_click(event):\n        set_step(step + 1)\n\n    return html.div(\n        html.p(f\"total: {state['total']}\"),\n        html.p(f\"step: {step}\"),\n        html.button({\"onClick\": handle_click}, \"Add step\"),\n        html.button({\"onClick\": handle_step_click}, \"Bump step\"),\n    )\n
# TODO\n

step goes to 2 on screen, but the total keeps rising by 1. The reducer still holds the step from the render that created it. Two ways out: keep the reducer at module level and put step in the state, or send it in the action \u2014 dispatch({\"type\": \"add\", \"amount\": step}).

"},{"location":"reference/use-reducer/#avoiding-recreating-the-initial-state","title":"Avoiding recreating the initial state","text":"

Pass a function and ReactPy calls it once; pass a call and it runs on every render, with every result but the first discarded. Measured across four renders: 1 call versus 4. The counter on screen reports the total, and it stays at 1 no matter what you press.

app.py Run
loads = []  # demo-only: shows how often the initializer actually runs\n\n\ndef load_config():\n    \"\"\"Stand-in for anything slow: reading a file, querying a database.\"\"\"\n    loads.append(1)\n    return {\"rows\": [{\"name\": n} for n in (\"alpha\", \"beta\", \"gamma\")], \"selected\": None}\n\n\ndef reducer(state, action):\n    if action[\"type\"] == \"select\":\n        return {**state, \"selected\": action[\"name\"]}\n    return state\n\n\n@component\ndef report():\n    # Pass a function as the initial state and ReactPy calls it once, on the\n    # first render. Passing `load_config()` instead would call it on every\n    # render and throw every result but the first away.\n    state, dispatch = use_reducer(reducer, load_config)\n\n    async def handle_click(event):\n        dispatch({\"type\": \"select\", \"name\": state[\"rows\"][0][\"name\"]})\n\n    return html.div(\n        html.h1(f\"{len(state['rows'])} rows\"),\n        html.p(f\"selected: {state['selected'] or '-'}\"),\n        html.p(f\"initializer ran {len(loads)} time(s)\"),\n        html.button(\n            {\"onClick\": handle_click},\n            \"Select first\",\n        ),\n    )\n
# TODO\n
"},{"location":"reference/use-reducer/#use_reducer-or-use_state","title":"use_reducer or use_state?","text":"

Prefer use_state. It is shorter and there is no reducer-closure rule to remember. Switch when one of these is true:

  • Several pieces of state always change together, and updating them through separate setters lets the component render in between with an inconsistent mix.
  • The next state depends on the current one in a way you keep writing wrong.
  • The update logic has grown branches you want to name, or unit-test as a plain function.
  • Different parts of a tree need to trigger the same transitions \u2014 passing dispatch around is tidier than passing several setters.
"},{"location":"reference/use-reducer/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-reducer/#my-dispatch-does-nothing","title":"My dispatch does nothing","text":"

Check the reducer's return value first \u2014 this is almost always a mutation. return state after modifying state schedules nothing. Also check for a branch that returns the state unchanged: an action whose \"type\" no branch matches falls through to your return state, which is correctly a no-op.

"},{"location":"reference/use-reducer/#my-reducer-sees-an-old-value-of-some-state","title":"My reducer sees an old value of some state","text":"

You defined it inside the component, so ReactPy is running the first-render version with the first-render closure. Move it to module level and pass the value through state or the action.

"},{"location":"reference/use-reducer/#the-initial-value-seems-to-be-rebuilt-every-render","title":"The initial value seems to be rebuilt every render","text":"

You passed build_initial_state() instead of build_initial_state. ReactPy needs the function itself to know it may skip the work.

"},{"location":"reference/use-reducer/#my-reducer-raised-and-nothing-said-so","title":"My reducer raised and nothing said so","text":"

Expected. Reducer errors land in the server log as Failed to execute event handler and are otherwise swallowed; the state is left unassigned. Look at the server output, not the browser console.

"},{"location":"reference/use-reducer/#i-want-an-updater-function-instead-of-an-action","title":"I want an updater function instead of an action","text":"

That is what use_state gives you: set_value(lambda previous: ...). If that is all you need, use_reducer is ceremony you do not owe yourself.

"},{"location":"reference/use-ref/","title":"Use Ref","text":""},{"location":"reference/use-ref/#overview","title":"Overview","text":"

use_ref is a ReactPy hook that gives you a mutable box which survives re-renders. Writing to it never causes a render.

value_box = use_ref(initial_value)\n

A ref is for values your component uses but never renders. It is a plain Python object holding one field, so reading and writing it is just attribute access \u2014 no setter, no scheduling, no re-render.

There is no DOM ref in ReactPy

React's useRef is often used to grab a DOM node \u2014 inputRef.current.focus(), videoRef.current.play(), myDiv.current.scrollIntoView(). That use does not exist in ReactPy. Your components run on the server, where there is no DOM, and ReactPy has no mechanism for handing a browser node to Python. There is no ref_current attribute, no callback ref, nothing to attach a node to. Anything you write as a \"ref_...\" attribute is an unknown attribute the client silently ignores.

Express browser behavior through state and ordinary attributes instead: {\"autoFocus\": True}, {\"value\": text}, {\"disabled\": not ready}. If you need genuinely imperative browser code, that belongs in a client-side app, where your Python runs in the browser.

"},{"location":"reference/use-ref/#reference","title":"Reference","text":""},{"location":"reference/use-ref/#use-ref","title":"use_ref(initial_value)","text":"

Call use_ref at the top level of your component to declare a value box.

from reactpy import use_ref\n
"},{"location":"reference/use-ref/#parameters","title":"Parameters","text":"
  • initial_value: The value the box starts with. It can be any Python value, including a mutable one.
"},{"location":"reference/use-ref/#returns","title":"Returns","text":"

A reactpy.utils.Ref object \u2014 the same object on every render of that component. It has two members:

  • current: The value. Read and write it directly: value_box.current = new_value. Reading it does not schedule a render.
  • set_current(new_value): Assigns current and returns the value it replaced. Because it is a call rather than an assignment, it fits where a statement cannot go \u2014 inside a comprehension, or as part of a larger expression.
"},{"location":"reference/use-ref/#caveats","title":"Caveats","text":"
  • initial_value is evaluated on every render. use_ref(build_something_expensive()) calls build_something_expensive() each time the component renders, then throws all but the first result away. Verified: four renders, four calls. To build something once, start empty and fill it in:

    index = use_ref(None)\nif index.current is None:\n    index.current = build_something_expensive()\n
  • Writing to a ref never triggers a render. That is the point, and also the trap: if the value is something the screen shows, it will not update. See Ref versus state.

  • Mutating a ref does not invalidate anything. Dependency comparison treats the box as unchanged no matter what you write into it, because it compares equal to itself. Listing a ref in a dependencies list is therefore useless \u2014 the effect or memo will never re-run because of it. Verified: an effect with dependencies=[box] ran once while its box.current was written to three times.
  • A ref is unhashable. hash(use_ref(0)) raises TypeError, so it cannot be a dict key or a set member. Its == compares contents: two separate Ref(0) boxes are equal.
  • Render value_box.current, not the box. html.p(my_ref) does not raise; it renders the repr, so the reader sees Ref(0) on the page. Passing a ref as an attribute value fails at serialization instead.
  • Like every hook, call it only at the top level \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check: shuffling which hooks run between renders yields a scrambled component and a cryptic IndexError, not a helpful message.
  • ReactPy has no Strict Mode, so nothing runs your component twice to catch impure code. A ref written to during rendering is exactly as unsafe here as in React, and nothing will warn you. See Keeping Components Pure.
"},{"location":"reference/use-ref/#usage","title":"Usage","text":""},{"location":"reference/use-ref/#ref-versus-state","title":"Ref versus state","text":"

The question is not \"which is cheaper\" but who needs to know. If changing the value must update the screen, it is state. If only the code needs it, it is a ref.

The two counters below start together and then drift apart, because only one of them causes a render. The ref counter's new value sits in the box unseen until something else renders the component.

app.py Run
@component\ndef counter():\n    # Two buttons, one counter each. Only the state one causes a render, so the\n    # numbers only come back into sync on a render triggered by something else.\n    state_count, set_state_count = use_state(0)\n    ref_count = use_ref(0)\n\n    async def bump_state(event=None):\n        set_state_count(state_count + 1)\n\n    async def bump_ref(event=None):\n        ref_count.current += 1  # writes in place: no render is scheduled\n\n    return html.div(\n        html.p(f\"state: {state_count}\"),\n        html.p(f\"ref: {ref_count.current}\"),\n        html.button({\"onClick\": bump_state}, \"Increment state\"),\n        html.button({\"onClick\": bump_ref}, \"Increment ref\"),\n    )\n
# TODO\n

The usual split:

You need... Use A value the UI renders use_state A value only handlers or effects read use_ref A value that drives the UI and is written by a long-lived loop Both: state for the UI, ref for the loop Something built once, then read-only use_ref, with the guard A mutable value the UI renders use_state \u2014 replace the object instead of mutating it; see Updating Objects in State"},{"location":"reference/use-ref/#remembering-a-value-built-once","title":"Remembering a value built once","text":"

Because the box is the same object across renders, it is the natural place for something expensive to construct and cheap to read: a parsed file, an index, a client object. Start it empty so the expensive call is not repeated on every render.

app.py Run
def build_word_index():\n    \"\"\"Stand-in for something slow: reading a file, opening a connection, etc.\"\"\"\n    return {word: len(word) for word in \"react py is a python ui library\".split()}\n\n\n@component\ndef word_counter():\n    # Built once, on the first render, and then reused for the life of the\n    # component -- no `dependencies`, no invalidation, nothing to keep in sync.\n    index: Ref[dict[str, int] | None] = use_ref(None)\n    if index.current is None:\n        index.current = build_word_index()\n\n    return html.div(\n        html.h1(\"Word lengths\"),\n        html.ul(\n            [\n                html.li(f\"{word}: {length}\")\n                for word, length in sorted(index.current.items())\n            ]\n        ),\n    )\n
# TODO\n

If the value should be rebuilt when some input changes, a ref is the wrong tool \u2014 that is what a dependencies list is for. See use_memo.

"},{"location":"reference/use-ref/#sharing-a-value-with-a-long-lived-effect","title":"Sharing a value with a long-lived effect","text":"

An use_async_effect loop keeps running across renders, so it closes over the state values from the render that started it. A ref sidesteps this: it is the same object every render, so the loop reads whatever the handlers wrote after it started.

The stopwatch below keeps the start timestamp in a ref. The ticking effect reads it and the buttons write it, none of which needs a render of its own. The value the screen shows, elapsed, is state.

app.py Run
@component\ndef stopwatch():\n    # What the UI renders lives in state. What only the handlers and the\n    # ticking effect read -- and which must never trigger a render of its own\n    # -- lives in a ref.\n    elapsed, set_elapsed = use_state(0.0)\n    running, set_running = use_state(False)\n    started_at: Ref[float | None] = use_ref(None)\n\n    async def tick():\n        # The ref is the same object on every render, so this long-lived loop\n        # always sees whatever the button handlers wrote into it.\n        while started_at.current is not None:\n            await asyncio.sleep(0.1)\n            set_elapsed(time.monotonic() - started_at.current)\n\n    use_async_effect(tick, dependencies=[running])\n\n    async def start(event=None):\n        started_at.current = time.monotonic()\n        set_running(True)\n\n    async def stop(event=None):\n        if started_at.current is not None:\n            # Write the exact final value; the ticking effect only samples\n            # every 0.1s, so relying on it alone would leave the display\n            # slightly behind.\n            set_elapsed(time.monotonic() - started_at.current)\n            started_at.current = None\n        set_running(False)\n\n    async def reset(event=None):\n        started_at.current = None\n        set_running(False)\n        set_elapsed(0.0)\n\n    return html.div(\n        html.h1(f\"{elapsed:.1f}s\"),\n        html.button(\n            {\"onClick\": stop if running else start},\n            \"Stop\" if running else \"Start\",\n        ),\n        html.button({\"onClick\": reset}, \"Reset\"),\n    )\n
# TODO\n
"},{"location":"reference/use-ref/#writing-where-an-expression-is-required","title":"Writing where an expression is required","text":"

box.current = value is an assignment, which is a statement, so it cannot appear where an expression is required \u2014 inside a comprehension, or as a value. set_current is the call-shaped form of the same write, and it hands back what you replaced.

app.py Run
@component\ndef theme_picker():\n    # `set_current` is the call-shaped version of `ref.current = value`, and it\n    # hands back the value it replaced. The return value is what makes the swap\n    # below one expression: an assignment cannot be an argument to set_theme().\n    previous = use_ref(\"dark\")\n    theme, set_theme = use_state(\"light\")\n\n    def pick(name):\n        # A factory: it returns a fresh coroutine per button, so each button\n        # gets its own handler and the theme it belongs to is bound by closure.\n        async def choose_theme(event):\n            previous.set_current(theme)  # record the theme being left\n            set_theme(name)\n\n        return choose_theme\n\n    async def swap_back(event):\n        set_theme(previous.set_current(theme))\n\n    return html.div(\n        html.h1(f\"{theme} theme\"),\n        html.p(f\"you were just using: {previous.current}\"),\n        html.button({\"onClick\": swap_back}, \"Swap back\"),\n        html.button({\"onClick\": pick(\"light\")}, \"Light\"),\n        html.button({\"onClick\": pick(\"dark\")}, \"Dark\"),\n    )\n
# TODO\n

pick is a factory: it returns a fresh coroutine per button, so each button gets its own handler and the theme it belongs to is bound by the closure. swap_back is where set_current earns its place \u2014 set_theme(previous.set_current(theme)) exchanges the two in one expression, which an assignment could not do because an assignment is not a value. The re-render still comes from set_theme, not from the write to the ref.

"},{"location":"reference/use-ref/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-ref/#my-ref-value-is-stale","title":"My ref value is stale","text":"

Check which of these you did:

  • You rendered the box, not its contents. html.p(my_ref) prints Ref(...). Use my_ref.current.
  • You expected the write to re-render. A ref never schedules a render. If the screen must change, that value is state.
  • You listed the ref as a dependency. Mutating current does not invalidate effects or memos \u2014 the box always compares equal to itself. Put the value in the dependency list, or use state.
  • You read a ref through an old closure. Inside a long-lived effect, re-read box.current on each pass instead of binding it to a local once.
"},{"location":"reference/use-ref/#my-expensive-setup-runs-on-every-render","title":"My expensive setup runs on every render","text":"

You passed it as the argument: use_ref(build()) calls build() every render. Move it behind a guard:

box = use_ref(None)\nif box.current is None:\n    box.current = build()\n
"},{"location":"reference/use-ref/#i-need-to-focus-an-input-or-scroll-to-an-element","title":"I need to focus an input, or scroll to an element","text":"

There is no way to do this from a ReactPy server component, and no \"ref_current\" attribute will add one. Use the declarative attributes the browser already understands \u2014 \"autoFocus\", \"checked\", \"value\", \"disabled\" \u2014 and reach for a client-side app if you truly need imperative browser access.

"},{"location":"reference/use-scope/","title":"Use Scope","text":""},{"location":"reference/use-scope/#overview","title":"Overview","text":"

use_scope is a ReactPy hook that returns the ASGI connection scope behind the current websocket.

scope = use_scope()\n

Use it for what the server knows about the connection: who the peer is, which headers arrived, which server accepted the request. For the page the visitor is looking at, use use_location instead.

"},{"location":"reference/use-scope/#reference","title":"Reference","text":""},{"location":"reference/use-scope/#use-scope","title":"use_scope()","text":"

Call it at the top level of a component. It takes no arguments.

from reactpy import use_scope\n
"},{"location":"reference/use-scope/#returns","title":"Returns","text":"

The dict at use_connection().scope \u2014 the raw ASGI scope, with the same shape as any other ASGI app's scope. It is a live dictionary: nothing copies it for you.

Read ASGI: Websockets for the specification; the keys below are what uvicorn's websocket implementation actually delivered, captured from a live Chrome session against ReactPy:

Key What it holds \"type\" \"websocket\" for these connections. \"path\" The ReactPy websocket endpoint, e.g. \"/reactpy/\". Not the page. \"headers\" A list of (bytes, bytes) pairs, lower-cased by the server. \"client\" (ip, port) of the peer, or None if unknown. \"server\" (host, port) that accepted the connection. \"scheme\" \"ws\" or \"wss\". \"query_string\" The websocket URL's query string, as bytes \u2014 this carries ReactPy's own path/qs parameters. \"raw_path\" The request path, as bytes, un-decoded. \"root_path\" The ASGI root path, normally \"\". \"http_version\" e.g. \"1.1\". \"asgi\" {\"version\": ..., \"spec_version\": ...}. \"subprotocols\" Requested websocket subprotocols, usually []. \"state\" A dict shared across the connection's tasks; empty unless something put values in it. \"extensions\" Server-specific extras, e.g. {\"websocket.http.response\": {}} under uvicorn.

Which extras appear depends on the server: \"state\" and that \"extensions\" entry are uvicorn/websockets additions. Read them defensively, with scope.get(...).

"},{"location":"reference/use-scope/#caveats","title":"Caveats","text":"
  • \"path\" is not the page. For every component in the app it is the websocket endpoint \u2014 \"/reactpy/\" under the default path_prefix. Verified: on the page /some/deep/page?tab=2&user=ada, the scope still says \"/reactpy/\". Use use_location().path for the page.
  • Headers are bytes. Both halves of each pair are bytes and lower-cased, so compare against b\"user-agent\" and .decode() the value before using it. There is no dict of headers; see the example.
  • \"query_string\" is the websocket's, not the page's. It contains ReactPy's own path= and qs= parameters. Parse the page's query with use_location().
  • None is possible. \"client\" can be absent or None behind a proxy or in-process transport. scope[\"client\"][0] will raise on None; check first.
  • It needs a live connection. In a plain Layout render it raises RuntimeError: No backend established a connection.
  • It does not exist under client-side rendering. With ReactPyCsr there is no connection object, so the hook raises the same RuntimeError.
  • Like every hook, call it only at the top level.
"},{"location":"reference/use-scope/#usage","title":"Usage","text":""},{"location":"reference/use-scope/#reading-the-peer-and-a-header","title":"Reading the peer and a header","text":"app.py Run
def header(scope, name):\n    \"\"\"Read one request header out of an ASGI scope.\n\n    Headers arrive as a list of ``(bytes, bytes)`` pairs, lower-cased by the\n    server, so both halves need decoding before you can compare them.\n    \"\"\"\n    wanted = name.lower().encode()\n    for key, value in scope.get(\"headers\", []):\n        if key == wanted:\n            return value.decode()\n    return None\n\n\n@component\ndef visitor():\n    scope = use_scope()\n    # Note this is the *websocket* scope. Its \"path\" is the ReactPy websocket\n    # path, not the page the visitor is on -- use use_location() for that.\n    # `client` is legitimately absent or None behind some proxies, so read it\n    # through a local and check it rather than subscripting the scope directly.\n    client = scope.get(\"client\")\n    peer = client[0] if client else \"unknown\"\n    return html.div(\n        html.h1(f\"Hello, {peer}\"),\n        html.p(f\"browser: {header(scope, 'user-agent') or 'unknown'}\"),\n        html.p(f\"connection type: {scope['type']}\"),\n    )\n
# TODO\n

header() exists because the ASGI scope hands you raw bytes: it lower-cases the name, matches on bytes, and decodes the value. The component reads scope[\"client\"] defensively because a proxy can leave it unset.

"},{"location":"reference/use-scope/#what-belongs-here-and-what-does-not","title":"What belongs here and what does not","text":"You want Use The visitor's IP address use_scope()[\"client\"] A request header use_scope()[\"headers\"] The page the visitor is on use_location The query string use_location The object mediating the connection use_connection"},{"location":"reference/use-scope/#troubleshooting","title":"Troubleshooting","text":""},{"location":"reference/use-scope/#my-header-lookups-always-return-none","title":"My header lookups always return None","text":"

You compared against a str. The keys are bytes and lower-cased: (b\"user-agent\", b\"Mozilla/5.0 ...\").

"},{"location":"reference/use-scope/#it-raises-in-my-tests","title":"It raises in my tests","text":"

A bare Layout render establishes no connection. Wrap the call in try/except RuntimeError, or inject a connection \u2014 see use_connection.

"},{"location":"reference/use-scope/#every-page-shows-reactpy","title":"Every page shows /reactpy/","text":"

That is the scope's \"path\": the websocket endpoint, identical for all components. Use use_location().path.

"},{"location":"reference/use-scope/#i-need-the-request-path-before-the-websocket-opens","title":"I need the request path before the websocket opens","text":"

use_scope only exists inside a component, which means inside an established websocket. For pre-connection decisions, handle it in ASGI middleware in front of the ReactPy app \u2014 see ReactPyMiddleware.

"},{"location":"reference/use-state/","title":"Use State","text":""},{"location":"reference/use-state/#overview","title":"Overview","text":"

use_state is a ReactPy hook that lets you add a state variable to your component.

value, set_value = use_state(initial_value)\n
"},{"location":"reference/use-state/#reference","title":"Reference","text":""},{"location":"reference/use-state/#use_stateinitial_value","title":"use_state(initial_value)","text":"

Call use_state at the top level of your component to declare a state variable.

from reactpy import use_state\n
"},{"location":"reference/use-state/#parameters","title":"Parameters","text":"
  • initial_value: The value you want the state to start with. It can be any Python value. Alternatively, pass a function taking no arguments and returning the initial value; ReactPy calls it on the first render only, which avoids rebuilding an expensive value on every render. See avoiding recreating the initial state.
"},{"location":"reference/use-state/#returns","title":"Returns","text":"

use_state returns a reactpy.types.State \u2014 a named tuple of two items, which is normally unpacked:

  • value: The current state, as of the start of this render.
  • set_value: A function that updates the state and schedules a re-render. See set_value.

Because State is a named tuple you can also reach the halves explicitly (state.value, state.set_value), but unpacking is the convention.

"},{"location":"reference/use-state/#caveats","title":"Caveats","text":"
  • use_state is a hook, so call it only at the top level of your component or of your own hooks \u2014 never in a loop, a conditional, or after an early return. ReactPy does not check for this: changing which hooks run between renders produces a scrambled component and a cryptic IndexError: list index out of range rather than a helpful message. If you need conditional state, extract a component.
  • initial_value is used only on the first render. Later changes to the expression are ignored \u2014 see resetting state with a key.
  • Do not pass a function you intend to store. A callable initial_value is treated as an initializer and called. use_state(lambda value: value * 2) does not store the lambda; ReactPy calls it with no arguments and the component fails. To keep a function in state, wrap it (use_state(lambda: my_function)) or store it in a use_ref.
  • Unlike React, ReactPy has no Strict Mode, so your initializer is called exactly once \u2014 verified: one call across four renders. Nothing will run it twice to expose impurity for you, so test purity yourself if it matters.
"},{"location":"reference/use-state/#set-value","title":"set_value(next_value)","text":"

The set_value function returned by use_state updates the state and schedules a re-render.

"},{"location":"reference/use-state/#parameters_1","title":"Parameters","text":"
  • next_value: The value you want the state to become. Any Python value, or a function taking one argument and returning the next state. That function receives the current value, runs eagerly inside the setter, and must be pure. See updating state based on the previous state.
"},{"location":"reference/use-state/#returns_1","title":"Returns","text":"

None.

"},{"location":"reference/use-state/#caveats_1","title":"Caveats","text":"
  • Setting state does not change the variable you already have in this render \u2014 it schedules a new one. See State as a Snapshot.
  • Setting state to a value equal to the current one does not re-render. The comparison is core/hooks.py, strictly_equal \u2014 and it is not an identity check: the two values must share a type, two locally-defined functions or lambdas are compared by their code, and everything else is compared with ==, falling back to identity only if that raises. So a brand-new object is dropped too whenever it merely compares equal: setting [1, 2, 3] while the state is an equal list renders nothing, and the same goes for any class whose __eq__ matches. Only values with identity-based __eq__ \u2014 plain class instances, say \u2014 re-render when you replace them.
  • set_value replaces the state; it never merges a partial object into it. Verified: setting {\"a\": 9} over {\"a\": 1, \"b\": 2} yields {\"a\": 9}. Update objects with set_value({**value, \"k\": v}) \u2014 see Updating Objects in State.
  • Mutating the current value and passing it back does nothing. The comparison finds the value equal to itself, so no render is scheduled \u2014 and there is no error, no warning, and nothing in the console. set_value(items.append(x)) also stores None, because most mutating list methods return None.
  • If the updater function raises, the error is logged on the server as Failed to execute event handler and swallowed; the state is left unassigned and the UI simply stops updating. Check the server log, not the browser console.
  • set_value is a stable object: the same callable is returned on every render of that component. Verified across four renders. That makes it safe to put in a dependency list or pass to a child without causing extra renders.
"},{"location":"reference/use-state/#usage","title":"Usage","text":""},{"location":"reference/use-state/#adding-state-to-a-component","title":"Adding state to a component","text":"

Call use_state at the top level of your component, and use the returned value while rendering:

app.py Run
@component\ndef counter():\n    step, set_step = use_state(1)\n\n    async def handle_add(event):\n        set_step(step + 1)\n\n    return html.div(\n        html.h1(step),\n        html.button({\"onClick\": handle_add}, \"Add 1\"),\n    )\n
# TODO\n

Each state variable gets its own setter, so prefer several narrow variables over one crowded object:

first_name, set_first_name = use_state(\"Ada\")\nlast_name, set_last_name = use_state(\"Lovelace\")\nage, set_age = use_state(42)\n
"},{"location":"reference/use-state/#counter-number","title":"Counter (number)","text":"

To update a number based on its current value, use a function:

async def handle_add(event):\n    set_step(lambda step: step + 1)\n

Or read the value from this render directly when you don't need the latest: set_step(step + 1).

"},{"location":"reference/use-state/#text-field-string","title":"Text field (string)","text":"

A controlled input reads its \"value\" from state and writes back through \"onChange\":

app.py Run
@component\ndef text_field():\n    first_name, set_first_name = use_state(\"Ada\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    return html.div(\n        html.input(\n            {\n                \"value\": first_name,\n                \"onChange\": handle_first_name_change,\n            }\n        ),\n        html.h1(f\"Hello, {first_name}!\"),\n    )\n
# TODO\n

Type into the field and the heading follows. Note the key names: \"value\" and \"onChange\" are DOM attribute names inside the attributes dictionary, while the Python names around them are snake_case.

Reading which field an event came from is possible \u2014 await event.target.name asks the page for it \u2014 but it costs a round trip, so when the handler already knows which field it belongs to, bind that in Python instead: a named handler per field, a closure, or a functools.partial. See Responding to Events.

"},{"location":"reference/use-state/#checkbox-boolean","title":"Checkbox (boolean)","text":"

await event.target.checked tells you whether the box is ticked:

app.py Run
@component\ndef checkbox():\n    enabled, set_enabled = use_state(False)\n\n    async def handle_change(event):\n        # `checked` is read from the live element, like any other property.\n        set_enabled(await event.target.checked)\n\n    return html.div(\n        html.label(\n            html.input(\n                {\"type\": \"checkbox\", \"checked\": enabled, \"onChange\": handle_change}\n            ),\n            \"Enabled\",\n        ),\n        html.p(f\"Status: {'on' if enabled else 'off'}\"),\n    )\n
# TODO\n

Tick the box and the status line flips. checked is fetched from the live element when the handler awaits it, in the same message as any other property would be \u2014 nothing arrives up front, so a handler which never reads it never pays for it.

"},{"location":"reference/use-state/#form-two-variables","title":"Form (two variables)","text":"

Two inputs, two state variables, no interference between them:

app.py Run
@component\ndef form():\n    first_name, set_first_name = use_state(\"Ada\")\n    last_name, set_last_name = use_state(\"Lovelace\")\n\n    async def handle_first_name_change(event):\n        set_first_name(await event.target.value)\n\n    async def handle_last_name_change(event):\n        set_last_name(await event.target.value)\n\n    return html.div(\n        html.label(\n            \"First name: \",\n            html.input(\n                {\n                    \"value\": first_name,\n                    \"onChange\": handle_first_name_change,\n                }\n            ),\n        ),\n        html.label(\n            \"Last name: \",\n            html.input(\n                {\n                    \"value\": last_name,\n                    \"onChange\": handle_last_name_change,\n                }\n            ),\n        ),\n        html.h1(f\"Hello, {first_name} {last_name}!\"),\n    )\n
# TODO\n
"},{"location":"reference/use-state/#updating-state-based-on-the-previous-state","title":"Updating state based on the previous state","text":"

A common case is updating state based on the value that came before. Pass a function instead of a value:

set_count(lambda count: count + 1)\n

ReactPy calls your function with the current state and assigns the result.

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n\n    async def handle_add_three(event):\n        # Updaters receive the current value, so all three increments land.\n        set_count(lambda previous: previous + 1)\n        set_count(lambda previous: previous + 1)\n        set_count(lambda previous: previous + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_add_three}, \"+3\"),\n    )\n
# TODO\n

Click \"+3\" and the counter reaches 3, because each updater sees what the previous one produced \u2014 verified. Had these been set_count(count + 1) instead, all three would compute from the same render-time count and the result would be 1. See Queueing a Series of State Updates.

"},{"location":"reference/use-state/#is-using-an-updater-always-preferred","title":"Is using an updater always preferred?","text":"

No. If you are setting state to a value that does not depend on previous state, pass the value. Updaters are for when the next value is derived from the last, or when several updates to the same variable must compose within one handler.

"},{"location":"reference/use-state/#passing-the-updater-function","title":"Passing the updater function","text":"

The function must be pure \u2014 it should only compute and return the next value. Do not send requests, mutate the argument, or set other state inside it.

# Correct: pure, returns the next value\nset_count(lambda count: count + 1)\n\n# Wrong: mutates, and returns None\nset_items(lambda items: items.append(\"new\"))\n
"},{"location":"reference/use-state/#passing-the-next-state-directly","title":"Passing the next state directly","text":"

When the value comes from outside, pass it plainly:

set_first_name(\"Ada\")\n
"},{"location":"reference/use-state/#updating-objects-and-arrays-in-state","title":"Updating objects and arrays in state","text":"

Pass a new object; there is no merge. For dicts use unpacking, for lists use a comprehension or slicing:

async def handle_first_name(event):\n    set_person({**person, \"firstName\": await event.target.value})\n\n\nasync def handle_add(event):\n    set_items([*items, {\"id\": next_id, \"text\": text}])\n

Full treatment, including nested updates and the silent-drop failure mode, is on Updating Objects in State and Updating Arrays in State.

"},{"location":"reference/use-state/#avoiding-recreating-the-initial-state","title":"Avoiding recreating the initial state","text":"

ReactPy ignores initial_value after the first render, but the expression still runs on every render. If building it is expensive, pass a function:

app.py Run
@component\ndef list_page():\n    state, set_state = use_state(create_expensive_state)\n\n    return html.div(\n        html.h1(f\"{len(state['items'])} items\"),\n        html.p(state[\"items\"][0]),\n    )\n
# TODO\n

create_expensive_state is called once, on mount \u2014 verified (one call across four renders).

"},{"location":"reference/use-state/#passing-the-initializer-function","title":"Passing the initializer function","text":"
state, set_state = use_state(create_expensive_state)   # called once\n
"},{"location":"reference/use-state/#passing-the-initial-state-directly","title":"Passing the initial state directly","text":"
state, set_state = use_state(create_expensive_state())  # built every render\n

Correct, but wasteful. Note again that a bare function is interpreted as an initializer, which is why the second form calls it explicitly.

"},{"location":"reference/use-state/#resetting-state-with-a-key","title":"Resetting state with a key","text":"

ReactPy normally preserves state between re-renders, matching children by position and key. To force a component to start fresh, give its call site a different key:

app.py Run
@component\ndef inbox():\n    email, set_email = use_state(\"ada@example.com\")\n\n    async def handle_email_click(event):\n        set_email(\"grace@example.com\")\n\n    return html.div(\n        html.button(\n            {\"onClick\": handle_email_click},\n            \"Switch account\",\n        ),\n        counter(email, key=email),\n    )\n
# TODO\n

Switching accounts resets the counter to zero, because a changed key means a different component as far as ReactPy is concerned. Verified: a keyed child bumped to 2 came back at 0 after its key changed, while the identical child rendered without a key kept its count.

"},{"location":"reference/use-state/#storing-information-from-previous-renders","title":"Storing information from previous renders","text":"

To keep a history, read the previous list through an updater so it composes correctly, and capture this render's value:

app.py Run
@component\ndef counter():\n    count, set_count = use_state(0)\n    history, set_history = use_state([])\n\n    async def handle_add(event):\n        # Read history through an updater: `history` in this closure is the\n        # value from the render that created the handler, not the latest.\n        set_history(lambda previous: [*previous, count])\n        set_count(count + 1)\n\n    return html.div(\n        html.h1(count),\n        html.button({\"onClick\": handle_add}, \"Add 1\"),\n        html.p(\"Previous values: \" + (\", \".join(map(str, history)) or \"-\")),\n    )\n
# TODO\n

Clicking twice records Previous values: 0, 1 \u2014 verified. The history name in the closure would still be the render-time value, which is exactly why the updater form matters here. For genuinely mutable, non-rendering values, use use_ref instead.

"},{"location":"reference/use-sync-external-store/","title":"Use sync external store","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"},{"location":"reference/use-transition/","title":"Use transition","text":"

Planned / Undeveloped

This feature is planned, but not yet developed.

"}]} \ No newline at end of file diff --git a/docs/src/about/changelog.md b/docs/src/about/changelog.md index cea79e0dd..1ecf88e28 100644 --- a/docs/src/about/changelog.md +++ b/docs/src/about/changelog.md @@ -5,16 +5,10 @@ hide:

-Notable changes to ReactPy, newest first. Read the `Unreleased` section for what has landed since the last release, and the versioned sections for what shipped in each. - -

- {% include-markdown "../../../CHANGELOG.md" start="" end="" %} -!!! abstract "Note" - - Entries use the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) categories — **Added**, **Changed**, **Deprecated**, **Removed**, **Fixed**, **Security** — and versions follow [Semantic Versioning](https://semver.org/). Pre-release versions carry a `bN` suffix, and behaviour can shift between betas without a major-version bump, so pin an exact version if you depend on one. +

- The authoritative list of shipped artefacts, with downloads and commit ranges, is [GitHub Releases](https://github.com/reactive-python/reactpy/releases); the published packages are on [PyPI](https://pypi.org/project/reactpy/). This page is the curated narrative of what changed, not the release log. +--- {% include-markdown "../../../CHANGELOG.md" start="" %} diff --git a/docs/src/about/code.md b/docs/src/about/code.md index 263623214..1f7d1fcc9 100644 --- a/docs/src/about/code.md +++ b/docs/src/about/code.md @@ -18,11 +18,8 @@ This documentation site and the library are separate bodies of work, and the com | Change | Repository / branch | | --- | --- | -| Library code, hooks, layout, backends | [`reactive-python/reactpy` → `main`](https://github.com/reactive-python/reactpy) | +| Documentation, library code, hooks, layout, backends | [`reactive-python/reactpy` → `main`](https://github.com/reactive-python/reactpy) | | JavaScript client (`src/js/`) | Same, `main` | -| These documentation pages (`docs/src/`, `docs/examples/`) | [`reactive-python/reactpy` → `new-docs`](https://github.com/reactive-python/reactpy/tree/new-docs) | - -The `new-docs` branch holds only `docs/` — the package and `tests/` were removed from it. A test command run there reports success without running anything. !!! warning "Pitfall" @@ -44,21 +41,10 @@ git checkout -b fix-my-change main Name the branch for the change, not for you. `fix-key-not-resetting-on-provider` beats `patch-1` and survives a month of review. -Match the convention of the branch you are landing on; they differ. Documentation commits on `new-docs` use [Conventional Commits](https://www.conventionalcommits.org/) subjects — `docs(learn): author X for ReactPy v2`, `docs(examples): fix include-marker cropping`. Library history on `main` instead uses plain sentence subjects, because pull requests there are squash-merged and the summary is written at merge time — `Fix PyScript root component not hiding after render error (#1347)`. In both cases the subject is the thing a reader scans to find the functional change in a long history, so write it for that reader. - -One commit per logical change. If a commit only makes sense next to a later one, squash them before you push rather than asking the reviewer to mentally reassemble them. - ## The checklist The repository ships a pull request template; fill it in rather than deleting it. -- [ ] Tests have been developed for bug fixes or new functionality. -- [ ] The changelog has been updated, if necessary. -- [ ] Documentation has been updated, if necessary. -- [ ] GitHub Issues closed by this pull request have been linked. - -By submitting, you agree that your contribution complies with the project's open source licenses. - Tests are not optional for a behaviour change. A bug fix should arrive with the test that reproduces the bug — write it first and watch it fail, then fix and watch it pass. A test that has never failed proves nothing about the thing it claims to protect. The changelog rule is narrower than it looks: `CHANGELOG.md` gets an entry under `Unreleased` for **user-facing** changes only. Test-only, docs-only, and CI-only changes get none. Adding one for those is a small annoyance to the person doing the release, who then has to notice and delete it. See [Changelog](./changelog.md). @@ -86,8 +72,6 @@ hatch test -k the_test_you_touched CI retries flaky tests, which is a mercy and a trap: a genuinely racy test can go green on the third attempt and stay in the tree. If a test fails once and passes on retry, that is a bug in the test — usually a bare `asyncio.sleep` where `poll(...)` belongs. -A pull request into `new-docs` gets no CI at all, for two independent reasons. GitHub runs `pull_request` workflows from the **base** branch, and the only workflow that exists on `new-docs` is the docs deploy, which triggers on `push` — so nothing matches a PR. And the `test-documentation` job in `check.yml` on `main` is disabled outright with `if: 0` (temporarily, while the Sphinx-to-MkDocs transition finishes), so even a PR targeting `main` runs lint and Python tests but never builds the docs. A docs change is verified by you or by nobody — run the `mkdocs build --strict` set from [Running Tests](./running-tests.md) and read its exit code. The one thing that does run: pushing to `new-docs` deploys with `mkdocs gh-deploy --force`, no `--strict` — expect a change to be live the moment it merges, whether or not it builds cleanly. - ## Working with reviewers Respond to every comment, including the ones you are not acting on. "Not taking this because X" is a complete reply and saves a round trip; silence invites a re-review of something you already considered. @@ -102,13 +86,13 @@ If a review asks for something you believe is wrong, say so once, with evidence, ## After merge -Documentation changes on `new-docs` deploy to the live site on push, so a mistake there is visible immediately rather than at the next release. That is a reason to run the build check locally, not a reason to be nervous about contributing. +Documentation changes deploy to the live site's `develop` documentation on push to `main`, so a mistake there is visible immediately rather than at the next release. That is a reason to run the build check locally, not a reason to be nervous about contributing. Library changes reach users only through a release, which the maintainers cut. Do not ask when your fix will ship, and do not close a closed-issue loop by demanding a release — the tag is a maintainer's call. ## Recap -- Library work on `main`; documentation work on `new-docs`. Check the branch first. +- Library and documentation work on `main`. - Start from a discussion; a one-sentence redirect beats a rejected pull request. - Conventional Commit subjects, one logical change per commit, branch named for the change. - Tests accompany behaviour changes, and they must have failed first. diff --git a/docs/src/about/community.md b/docs/src/about/community.md index 1cf3670a7..8ece3360d 100644 --- a/docs/src/about/community.md +++ b/docs/src/about/community.md @@ -28,16 +28,6 @@ The [ReactPy repository on GitHub](https://github.com/reactive-python/reactpy) i Discussions is the front door, not Issues. The repository's issue-template configuration disables blank issues and points new visitors at Discussions instead, so a question opened as an issue gets redirected rather than answered. Open an issue once a defect is confirmed and someone has reproduced it — the discussion that came first usually becomes the link inside it. -## Chat - -There is a [Discord server](https://discord.gg/uNb5P4hA9X) for ReactPy, announced in [Discussion #997](https://github.com/reactive-python/reactpy/discussions/997) and linked from the repository README. It is the fastest place to get an answer to a "why is this doing that" question, and quiet enough that nobody minds a beginner question. - -!!! abstract "Note" - - An invite URL is mutable third-party state. If `discord.gg/uNb5P4hA9X` ever stops resolving, read the current invite from the README badge or from Discussion #997 — those two are maintained together — rather than trusting a search result, which may point at a cloned server. - -Discord is good for conversation and bad for searchability. A solution that exists only in chat is invisible to the next person with the same problem. If a chat thread solves something real, post it to Discussions afterwards; if it exposes an error on this site, that is a [documentation contribution](./docs.md). - ## Code of conduct Participation is governed by the [Contributor Covenant code of conduct](https://github.com/reactive-python/reactpy/blob/main/CODE_OF_CONDUCT.md), which lives at the repository root. It applies everywhere the project is discussed — Discussions, Issues, pull requests, Discord, and any other space where ReactPy maintainers show up. diff --git a/docs/src/about/docs.md b/docs/src/about/docs.md index ca80e7fd6..2b990a936 100644 --- a/docs/src/about/docs.md +++ b/docs/src/about/docs.md @@ -8,7 +8,6 @@ Most of these pages were ported from the ReactJS documentation, and the port is !!! summary "You will learn" - - What makes a ported page "done", and how the `🚧` marker works - The page structure, admonitions, and link conventions this site uses - Why snippets live in `docs/examples/` instead of inside the markdown @@ -34,11 +33,7 @@ A page is authored, not migrated. Concretely: - Every code sample is ReactPy that runs. No JSX, no `className=` outside an attribute dict, no hook that does not exist. - Every behavioural claim has been checked against the package or its tests — by reading the source, and for anything about rendering or events, by running it. - React-only sections are removed or rewritten, not translated. -- The `🚧` badge is gone from its `nav:` entry. -!!! abstract "Note" - - `🚧` in the navigation means "this is still a ReactJS page with the React names swapped". It is a promise to the reader that the content is unreliable, so removing it is the meaningful act — the badge should never come off before the checks above are true. `đŸšĢ` means the page is blocked on something outside this project and is parked; commented-out `nav:` entries work the same way. ## Port, do not rename @@ -46,12 +41,12 @@ The pages derive from [react.dev](https://react.dev/learn). Editing means deleti Things that do not survive a rename: -- **Hooks that do not exist.** ReactPy has twelve hooks. There is no `use_id`, `use_transition`, `use_deferred_value`, `use_layout_effect`, `use_insertion_effect`, `use_imperative_handle`, or `use_sync_external_store`. Some of the react.dev pages for them are parked as `đŸšĢ`; `use_id` has no page at all, because the upstream issue was closed as not planned. Never port a page that depends on one as if it worked. +- **Hooks that do not exist.** ReactPy has twelve hooks. There is no `use_id`, `use_transition`, `use_deferred_value`, `use_layout_effect`, `use_insertion_effect`, `use_imperative_handle`, or `use_sync_external_store`. - **Naming rules.** Components are snake_case and lowercase — `my_button`, not `MyButton`. React's capital-letter rule does not apply, because ReactPy distinguishes a component from a tag by the call, not the case. Attribute keys stay camelCase (`className`, `onClick`), because those pass through to the DOM untouched. - **JSX mechanics.** Children are positional arguments after an optional attribute dict. There is no JSX, and no PSX in this release. - **StrictMode and `memo`.** Neither exists. React's double-invoke warnings and its "skip re-rendering unchanged children" optimisation have no ReactPy equivalent, so port those sections as a warning that ReactPy will not catch the problem for you. - **`&&` rendering.** `{cond && }` has no safe Python spelling — `False and X` leaks the word `False` into the page, since only `None` renders nothing. Rewrite such examples rather than transliterating them. -- **Dependency arrays.** `use_effect`, `use_memo`, and `use_callback` infer dependencies from the closure when you omit the argument. React's "omit the array to run after every render" is simply wrong here. +- **Dependency arrays.** `use_effect`, `use_memo`, and `use_callback` infer dependencies from the closure when you omit the argument. React's "omit the array to run after every render" is slightly different from ReactPy's behaviour. !!! warning "Pitfall" @@ -136,25 +131,12 @@ cd docs && mkdocs build --strict; echo "exit=$?" Then run the examples themselves and open the built page in a browser. `mkdocs build --strict` catches broken internal links and unresolvable includes — both abort the build with a nonzero exit. Two things it does **not** catch: a page missing from `nav:` (an `INFO` line, exit 0), and a link whose `#anchor` does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing. -You are the only gate. The single workflow on `new-docs` runs `mkdocs gh-deploy --force` on push, and `gh-deploy` does not pass `--strict` — a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live. - !!! warning "Pitfall" Do not trust a component that renders without error. Set `REACTPY_DEBUG=1` when probing: with debug off, a component that raised produces `{"tagName": "", "error": ""}` — a blank region and a 200 response. See [Running Tests](./running-tests.md) for the harness traps that make a broken example look green. -## Process - -Branch from `new-docs`, one page per branch, and use the same Conventional Commit subjects as the history there — `docs(learn): author X for ReactPy v2`. A commit that removes a `🚧` badge from `nav:` should be the same commit that authored the page, never a separate "mark done" commit that could be cherry-picked alone. - -No `CHANGELOG.md` entry: documentation work is not user-facing in the sense the changelog means. See [Changelog](./changelog.md). - -!!! abstract "Note" - - `docs/src/dictionary.txt` exists for the spellcheck plugin, which is currently commented out in `mkdocs.yml`. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off. - ## Recap -- A page is done when it runs and every claim is verified — then remove its `🚧` badge. - Port, do not rename: delete JSX, non-existent hooks, StrictMode, `memo`, `&&` rendering, and React's dependency-array semantics. - Fixed structure: Overview, intro paragraph, `You will learn`, sections, Recap, Related. - Relative `.md` links; admonitions instead of ReactJS's JSX tags. diff --git a/docs/src/about/running-tests.md b/docs/src/about/running-tests.md index e7394aee7..b1c71eaec 100644 --- a/docs/src/about/running-tests.md +++ b/docs/src/about/running-tests.md @@ -12,9 +12,6 @@ There are two test suites in this project, and they live in different places. Th - How to run the documentation build checks yourself, since no CI runs them for you - Which command failures are yours and which are environmental -!!! abstract "Note" - - The ReactPy package and its `tests/` directory live on the [`main`](https://github.com/reactive-python/reactpy) branch. This documentation site is authored on the [`new-docs`](https://github.com/reactive-python/reactpy/tree/new-docs) branch, which contains only `docs/` — running a test command there collects zero tests rather than failing, which reads like a pass and is not one. If `hatch test` reports `no tests ran`, you are on the wrong branch, not on a clean tree. ## Prerequisites @@ -102,11 +99,6 @@ Formatting is Ruff at a line length of 88. The rules that bite most in this repo Run these from the repository root before you commit. -!!! warning "Pitfall" - - Nothing checks the documentation for you. The only workflow on `new-docs` installs the requirements and runs `mkdocs gh-deploy --force` on push — which does *not* pass `--strict`, so a broken internal link exits 0 and deploys anyway. The commands below are the gate only because you run them; there is no CI to catch a miss, and the live site is where an uncaught one appears. - - ```bash hatch fmt --check docs/ # formatting and lint cd docs && mkdocs build --strict # build, fail on warnings diff --git a/pyproject.toml b/pyproject.toml index 88a645728..f6a6fd5df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,31 +134,28 @@ timeout = 120 ####################################### [tool.hatch.envs.docs] template = "docs" -dependencies = ["poetry"] -detached = true +dependencies = [ + "mkdocs", + "mkdocs-git-revision-date-localized-plugin", + "mkdocs-material", + "mkdocs-include-markdown-plugin", + "mkdocs-spellcheck[all]", + "mkdocs-git-authors-plugin", + "mkdocs-minify-plugin", + "mike", + "linkcheckmd", +] [tool.hatch.envs.docs.scripts] -build = [ - 'cd "{root}/docs" && poetry install', - 'cd "{root}/docs" && poetry run sphinx-build -a -T -W --keep-going -b doctest "{root}/docs/source" "{root}/docs/build"', -] -docker_build = [ - "hatch run docs:build", - 'docker build --file "{root}/docs/Dockerfile" --tag reactpy-docs:latest "{root}"', -] -docker_serve = [ - "hatch run docs:docker_build", - "docker run --rm -p 5000:5000 reactpy-docs:latest", -] -check = [ - 'cd "{root}/docs" && poetry install', - 'cd "{root}/docs" && poetry run sphinx-build -a -T -W --keep-going -b doctest "{root}/docs/source" "{root}/docs/build"', - 'docker build --file "{root}/docs/Dockerfile" "{root}"', -] -serve = [ - 'cd "{root}/docs" && poetry install', - 'cd "{root}/docs" && poetry run python "{root}/docs/main.py" --watch="{root}/src" --ignore=**/_auto/* --ignore=**/custom.js --ignore=**/node_modules/* --ignore=**/package-lock.json -a -E -b html "{root}/docs/source" "{root}/docs/build"', +serve = ["cd docs && mkdocs serve"] +build = ["cd docs && mkdocs build --strict"] +linkcheck = [ + "linkcheckMarkdown docs/ -v -r", + "linkcheckMarkdown README.md -v -r", + "linkcheckMarkdown CHANGELOG.md -v -r", ] +deploy_latest = ["cd docs && mike deploy --push --update-aliases {args} latest"] +deploy_develop = ["cd docs && mike deploy --push develop"] ################################ # >>> Hatch Python Scripts <<< # From ee1a4533c03f03bed177945884f6932004747068 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:45:40 -0700 Subject: [PATCH 158/177] docs linting workflow --- .github/workflows/check.yml | 2 +- .../changing/app.py | 6 +++--- .../reference_use_effect/async_clock/app.py | 6 +++--- .../render_and_commit/re_render/app.py | 6 +++--- .../hook_order_safe/app.py | 4 ++-- .../hook_order_violation/app.py | 2 +- docs/examples/thinking_in_react/use_state.py | 2 +- pyproject.toml | 18 ++++++++++++++++++ 8 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 66c9cc4de..662551d60 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -67,4 +67,4 @@ jobs: - name: Check docs build run: hatch run docs:build - name: Check docs examples - run: hatch fmt docs --check + run: hatch run docs:check diff --git a/docs/examples/passing_props_to_a_component/changing/app.py b/docs/examples/passing_props_to_a_component/changing/app.py index d0e4d02e1..b63062f09 100644 --- a/docs/examples/passing_props_to_a_component/changing/app.py +++ b/docs/examples/passing_props_to_a_component/changing/app.py @@ -1,5 +1,5 @@ import asyncio -from datetime import datetime, timezone +from datetime import UTC, datetime from reactpy import component, html, use_async_effect, use_state @@ -14,12 +14,12 @@ def clock(color, time): @component def app(): color, set_color = use_state("lightcoral") - now, set_now = use_state(datetime.now(timezone.utc)) + now, set_now = use_state(datetime.now(UTC)) async def tick(): while True: await asyncio.sleep(1) - set_now(datetime.now(timezone.utc)) + set_now(datetime.now(UTC)) use_async_effect(tick, dependencies=[]) diff --git a/docs/examples/reference_use_effect/async_clock/app.py b/docs/examples/reference_use_effect/async_clock/app.py index 255bf6679..376189dfe 100644 --- a/docs/examples/reference_use_effect/async_clock/app.py +++ b/docs/examples/reference_use_effect/async_clock/app.py @@ -1,18 +1,18 @@ # start import asyncio -from datetime import datetime, timezone +from datetime import UTC, datetime from reactpy import component, html, use_async_effect, use_state @component def clock(): - now, set_now = use_state(datetime.now(timezone.utc)) + now, set_now = use_state(datetime.now(UTC)) async def effect(): # Zero arguments. A long-running effect loops and honours cancellation. while True: - set_now(datetime.now(timezone.utc)) + set_now(datetime.now(UTC)) try: await asyncio.sleep(1) except asyncio.CancelledError: diff --git a/docs/examples/render_and_commit/re_render/app.py b/docs/examples/render_and_commit/re_render/app.py index a1d017ec4..0b754d151 100644 --- a/docs/examples/render_and_commit/re_render/app.py +++ b/docs/examples/render_and_commit/re_render/app.py @@ -1,5 +1,5 @@ import asyncio -from datetime import datetime, timezone +from datetime import UTC, datetime from reactpy import component, html, use_async_effect, use_state @@ -7,12 +7,12 @@ # start @component def clock(): - now, set_now = use_state(datetime.now(timezone.utc)) + now, set_now = use_state(datetime.now(UTC)) async def tick(): while True: await asyncio.sleep(1) - set_now(datetime.now(timezone.utc)) + set_now(datetime.now(UTC)) use_async_effect(tick, dependencies=[]) diff --git a/docs/examples/reusing_logic_with_custom_hooks/hook_order_safe/app.py b/docs/examples/reusing_logic_with_custom_hooks/hook_order_safe/app.py index 002392949..e5aaad45e 100644 --- a/docs/examples/reusing_logic_with_custom_hooks/hook_order_safe/app.py +++ b/docs/examples/reusing_logic_with_custom_hooks/hook_order_safe/app.py @@ -9,7 +9,7 @@ def use_profile(show_nickname): name, set_name = use_state("name") # the hook is called unconditionally; only the returned value is conditional - nickname, set_nickname = use_state("nickname") + nickname, _set_nickname = use_state("nickname") bio, set_bio = use_state("bio") return name, (nickname if show_nickname else "-"), bio, set_bio @@ -19,7 +19,7 @@ def profile(show_nickname): name, nickname, bio, set_bio = use_profile(show_nickname) async def extend_bio(event): - set_bio(bio + "!") + set_bio(f"{bio}!") return html.div( html.button({"onClick": extend_bio}, f"bio={bio}"), diff --git a/docs/examples/reusing_logic_with_custom_hooks/hook_order_violation/app.py b/docs/examples/reusing_logic_with_custom_hooks/hook_order_violation/app.py index e4c08e78a..bd910bd20 100644 --- a/docs/examples/reusing_logic_with_custom_hooks/hook_order_violation/app.py +++ b/docs/examples/reusing_logic_with_custom_hooks/hook_order_violation/app.py @@ -24,7 +24,7 @@ def profile(show_nickname): name, nickname, bio, set_bio = use_profile(show_nickname) async def extend_bio(event): - set_bio(bio + "!") + set_bio(f"{bio}!") return html.div( html.button({"onClick": extend_bio}, f"bio={bio}"), diff --git a/docs/examples/thinking_in_react/use_state.py b/docs/examples/thinking_in_react/use_state.py index 437554974..1844a9d66 100644 --- a/docs/examples/thinking_in_react/use_state.py +++ b/docs/examples/thinking_in_react/use_state.py @@ -4,5 +4,5 @@ # start @component def filterable_product_table(products): - filter_text, set_filter_text = use_state("") + _filter_text, _set_filter_text = use_state("") in_stock_only, set_in_stock_only = use_state(False) diff --git a/pyproject.toml b/pyproject.toml index f6a6fd5df..c840c5edd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,10 +144,17 @@ dependencies = [ "mkdocs-minify-plugin", "mike", "linkcheckmd", + "pyright", + "starlette", + "orjson", + "asgi_tools", + "servestatic", + "websockets" ] [tool.hatch.envs.docs.scripts] serve = ["cd docs && mkdocs serve"] +check = ["hatch check code docs/ && hatch check fmt docs/ && pyright docs/"] build = ["cd docs && mkdocs build --strict"] linkcheck = [ "linkcheckMarkdown docs/ -v -r", @@ -337,6 +344,17 @@ known-third-party = ["js"] "S102", # Allow print "T201", + # Docs render these files as teaching snippets, not importable modules. + # Relative imports (`from .my_button import my_button`) are what the docs + # deliberately teach, snippets unpack both halves of `use_state()` to show + # the full signature even when one half is unused, and each snippet file is + # standalone rather than part of a package. + "TID252", + "RUF059", + "INP001", + # "Keeping Components Pure" has to mutate a module-level variable with + # `global` in order to demonstrate why that is wrong. + "PLW0603", ] "scripts/**/*.py" = [ # Allow print From 49eb4a9b2c7e9dc1133ec4bc234ba2455538702f Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 18 Oct 2026 13:11:45 -0700 Subject: [PATCH 159/177] rm built docs --- docs/site/404.html | 1 - docs/site/about/changelog/index.html | 1 - docs/site/about/code/index.html | 18 - docs/site/about/community/index.html | 1 - docs/site/about/docs/index.html | 73 - docs/site/about/licenses/index.html | 18 - docs/site/about/running-tests/index.html | 32 - docs/site/assets/_mkdocstrings.css | 237 - docs/site/assets/css/admonition.css | 160 - docs/site/assets/css/banner.css | 15 - docs/site/assets/css/button.css | 44 - docs/site/assets/css/code.css | 116 - docs/site/assets/css/footer.css | 33 - docs/site/assets/css/home.css | 297 - docs/site/assets/css/main.css | 85 - docs/site/assets/css/navbar.css | 185 - docs/site/assets/css/sidebar.css | 103 - docs/site/assets/css/table-of-contents.css | 61 - docs/site/assets/images/add-interactivity.png | Bin 22900 -> 0 bytes .../assets/images/create-user-interfaces.png | Bin 12427 -> 0 bytes docs/site/assets/images/favicon.png | Bin 1870 -> 0 bytes .../assets/images/s_thinking-in-react_ui.png | Bin 25981 -> 0 bytes .../images/s_thinking-in-react_ui_outline.png | Bin 83418 -> 0 bytes .../images/write-components-with-python.png | Bin 16519 -> 0 bytes .../assets/javascripts/bundle.d7400e89.min.js | 16 - .../javascripts/bundle.d7400e89.min.js.map | 7 - .../javascripts/lunr/min/lunr.ar.min.js | 1 - .../javascripts/lunr/min/lunr.da.min.js | 18 - .../javascripts/lunr/min/lunr.de.min.js | 18 - .../javascripts/lunr/min/lunr.du.min.js | 18 - .../javascripts/lunr/min/lunr.el.min.js | 1 - .../javascripts/lunr/min/lunr.es.min.js | 18 - .../javascripts/lunr/min/lunr.fi.min.js | 18 - .../javascripts/lunr/min/lunr.fr.min.js | 18 - .../javascripts/lunr/min/lunr.he.min.js | 1 - .../javascripts/lunr/min/lunr.hi.min.js | 1 - .../javascripts/lunr/min/lunr.hu.min.js | 18 - .../javascripts/lunr/min/lunr.hy.min.js | 1 - .../javascripts/lunr/min/lunr.it.min.js | 18 - .../javascripts/lunr/min/lunr.ja.min.js | 1 - .../javascripts/lunr/min/lunr.jp.min.js | 1 - .../javascripts/lunr/min/lunr.kn.min.js | 1 - .../javascripts/lunr/min/lunr.ko.min.js | 1 - .../javascripts/lunr/min/lunr.multi.min.js | 1 - .../javascripts/lunr/min/lunr.nl.min.js | 18 - .../javascripts/lunr/min/lunr.no.min.js | 18 - .../javascripts/lunr/min/lunr.pt.min.js | 18 - .../javascripts/lunr/min/lunr.ro.min.js | 18 - .../javascripts/lunr/min/lunr.ru.min.js | 18 - .../javascripts/lunr/min/lunr.sa.min.js | 1 - .../lunr/min/lunr.stemmer.support.min.js | 1 - .../javascripts/lunr/min/lunr.sv.min.js | 18 - .../javascripts/lunr/min/lunr.ta.min.js | 1 - .../javascripts/lunr/min/lunr.te.min.js | 1 - .../javascripts/lunr/min/lunr.th.min.js | 1 - .../javascripts/lunr/min/lunr.tr.min.js | 18 - .../javascripts/lunr/min/lunr.vi.min.js | 1 - .../javascripts/lunr/min/lunr.zh.min.js | 1 - docs/site/assets/javascripts/lunr/tinyseg.js | 206 - docs/site/assets/javascripts/lunr/wordcut.js | 6708 ----------------- .../workers/search.2c215733.min.js | 42 - .../workers/search.2c215733.min.js.map | 7 - docs/site/assets/js/main.js | 19 - .../assets/stylesheets/main.ec1eaa64.min.css | 1 - .../stylesheets/main.ec1eaa64.min.css.map | 1 - .../stylesheets/palette.ab4e12ef.min.css | 1 - .../stylesheets/palette.ab4e12ef.min.css.map | 1 - docs/site/dictionary.txt | 28 - docs/site/index.html | 69 - .../index.html | 129 - .../choosing-the-state-structure/index.html | 1093 --- .../index.html | 173 - .../learn/conditional-rendering/index.html | 195 - .../convert-between-vdom-and-html/index.html | 116 - .../learn/creating-a-react-app/index.html | 44 - docs/site/learn/creating-backends/index.html | 291 - docs/site/learn/creating-html-tags/index.html | 162 - .../creating-vdom-event-handlers/index.html | 171 - docs/site/learn/editor-setup/index.html | 1 - .../index.html | 649 -- .../index.html | 154 - docs/site/learn/installation/index.html | 3 - .../learn/keeping-components-pure/index.html | 135 - .../lifecycle-of-reactive-effects/index.html | 447 -- .../manipulating-the-dom-with-refs/index.html | 234 - .../manually-register-a-client/index.html | 162 - .../index.html | 526 -- .../passing-props-to-a-component/index.html | 441 -- .../preserving-and-resetting-state/index.html | 672 -- .../index.html | 903 --- .../index.html | 157 - docs/site/learn/quick-start/index.html | 363 - .../learn/react-developer-tools/index.html | 90 - .../reacting-to-input-with-state/index.html | 426 -- .../referencing-values-with-refs/index.html | 158 - .../removing-effect-dependencies/index.html | 596 -- docs/site/learn/render-and-commit/index.html | 79 - docs/site/learn/rendering-lists/index.html | 477 -- .../learn/responding-to-events/index.html | 368 - .../index.html | 424 -- .../index.html | 556 -- .../separating-events-from-effects/index.html | 509 -- .../index.html | 236 - .../state-a-components-memory/index.html | 1024 --- .../site/learn/state-as-a-snapshot/index.html | 348 - .../synchronizing-with-effects/index.html | 650 -- docs/site/learn/thinking-in-react/index.html | 692 -- docs/site/learn/tools-and-packages/index.html | 1 - .../learn/tutorial-material-ui/index.html | 1 - .../learn/tutorial-react-bootstrap/index.html | 1 - .../learn/tutorial-tic-tac-toe/index.html | 2557 ------- .../learn/updating-arrays-in-state/index.html | 463 -- .../updating-objects-in-state/index.html | 291 - docs/site/learn/vdom-mutations/index.html | 149 - .../learn/writing-markup-with-psx/index.html | 446 -- .../you-might-not-need-an-effect/index.html | 739 -- .../learn/your-first-component/index.html | 114 - docs/site/objects.inv | Bin 129 -> 0 bytes docs/site/reference/client-api/index.html | 228 - docs/site/reference/common-props/index.html | 190 - .../index.html | 106 - docs/site/reference/jinja/index.html | 194 - docs/site/reference/overview/index.html | 1 - .../reference/protocol-structure/index.html | 403 - .../index.html | 34 - docs/site/reference/reactpy-csr/index.html | 43 - .../reference/reactpy-middleware/index.html | 42 - docs/site/reference/reactpy/index.html | 59 - docs/site/reference/rules-of-hooks/index.html | 183 - docs/site/reference/rules-of-react/index.html | 1 - docs/site/reference/usage/index.html | 158 - .../reference/use-async-effect/index.html | 82 - docs/site/reference/use-callback/index.html | 248 - docs/site/reference/use-connection/index.html | 58 - docs/site/reference/use-context/index.html | 193 - .../site/reference/use-debug-value/index.html | 104 - .../reference/use-deferred-value/index.html | 1657 ---- docs/site/reference/use-effect/index.html | 287 - .../use-imperative-handle/index.html | 462 -- .../reference/use-insertion-effect/index.html | 170 - .../reference/use-layout-effect/index.html | 1131 --- docs/site/reference/use-location/index.html | 76 - docs/site/reference/use-memo/index.html | 295 - docs/site/reference/use-reducer/index.html | 287 - docs/site/reference/use-ref/index.html | 233 - docs/site/reference/use-scope/index.html | 57 - docs/site/reference/use-state/index.html | 279 - .../use-sync-external-store/index.html | 613 -- docs/site/reference/use-transition/index.html | 2635 ------- docs/site/search/search_index.json | 1 - docs/site/sitemap.xml | 347 - docs/site/sitemap.xml.gz | Bin 1142 -> 0 bytes 152 files changed, 38346 deletions(-) delete mode 100644 docs/site/404.html delete mode 100644 docs/site/about/changelog/index.html delete mode 100644 docs/site/about/code/index.html delete mode 100644 docs/site/about/community/index.html delete mode 100644 docs/site/about/docs/index.html delete mode 100644 docs/site/about/licenses/index.html delete mode 100644 docs/site/about/running-tests/index.html delete mode 100644 docs/site/assets/_mkdocstrings.css delete mode 100644 docs/site/assets/css/admonition.css delete mode 100644 docs/site/assets/css/banner.css delete mode 100644 docs/site/assets/css/button.css delete mode 100644 docs/site/assets/css/code.css delete mode 100644 docs/site/assets/css/footer.css delete mode 100644 docs/site/assets/css/home.css delete mode 100644 docs/site/assets/css/main.css delete mode 100644 docs/site/assets/css/navbar.css delete mode 100644 docs/site/assets/css/sidebar.css delete mode 100644 docs/site/assets/css/table-of-contents.css delete mode 100644 docs/site/assets/images/add-interactivity.png delete mode 100644 docs/site/assets/images/create-user-interfaces.png delete mode 100644 docs/site/assets/images/favicon.png delete mode 100644 docs/site/assets/images/s_thinking-in-react_ui.png delete mode 100644 docs/site/assets/images/s_thinking-in-react_ui_outline.png delete mode 100644 docs/site/assets/images/write-components-with-python.png delete mode 100644 docs/site/assets/javascripts/bundle.d7400e89.min.js delete mode 100644 docs/site/assets/javascripts/bundle.d7400e89.min.js.map delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.ar.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.da.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.de.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.du.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.el.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.es.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.fi.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.fr.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.he.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.hi.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.hu.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.hy.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.it.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.ja.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.jp.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.kn.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.ko.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.multi.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.nl.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.no.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.pt.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.ro.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.ru.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.sa.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.sv.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.ta.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.te.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.th.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.tr.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.vi.min.js delete mode 100644 docs/site/assets/javascripts/lunr/min/lunr.zh.min.js delete mode 100644 docs/site/assets/javascripts/lunr/tinyseg.js delete mode 100644 docs/site/assets/javascripts/lunr/wordcut.js delete mode 100644 docs/site/assets/javascripts/workers/search.2c215733.min.js delete mode 100644 docs/site/assets/javascripts/workers/search.2c215733.min.js.map delete mode 100644 docs/site/assets/js/main.js delete mode 100644 docs/site/assets/stylesheets/main.ec1eaa64.min.css delete mode 100644 docs/site/assets/stylesheets/main.ec1eaa64.min.css.map delete mode 100644 docs/site/assets/stylesheets/palette.ab4e12ef.min.css delete mode 100644 docs/site/assets/stylesheets/palette.ab4e12ef.min.css.map delete mode 100644 docs/site/dictionary.txt delete mode 100644 docs/site/index.html delete mode 100644 docs/site/learn/add-react-to-an-existing-project/index.html delete mode 100644 docs/site/learn/choosing-the-state-structure/index.html delete mode 100644 docs/site/learn/communicate-data-between-server-and-client/index.html delete mode 100644 docs/site/learn/conditional-rendering/index.html delete mode 100644 docs/site/learn/convert-between-vdom-and-html/index.html delete mode 100644 docs/site/learn/creating-a-react-app/index.html delete mode 100644 docs/site/learn/creating-backends/index.html delete mode 100644 docs/site/learn/creating-html-tags/index.html delete mode 100644 docs/site/learn/creating-vdom-event-handlers/index.html delete mode 100644 docs/site/learn/editor-setup/index.html delete mode 100644 docs/site/learn/extracting-state-logic-into-a-reducer/index.html delete mode 100644 docs/site/learn/importing-and-exporting-components/index.html delete mode 100644 docs/site/learn/installation/index.html delete mode 100644 docs/site/learn/keeping-components-pure/index.html delete mode 100644 docs/site/learn/lifecycle-of-reactive-effects/index.html delete mode 100644 docs/site/learn/manipulating-the-dom-with-refs/index.html delete mode 100644 docs/site/learn/manually-register-a-client/index.html delete mode 100644 docs/site/learn/passing-data-deeply-with-context/index.html delete mode 100644 docs/site/learn/passing-props-to-a-component/index.html delete mode 100644 docs/site/learn/preserving-and-resetting-state/index.html delete mode 100644 docs/site/learn/python-in-psx-with-curly-braces/index.html delete mode 100644 docs/site/learn/queueing-a-series-of-state-updates/index.html delete mode 100644 docs/site/learn/quick-start/index.html delete mode 100644 docs/site/learn/react-developer-tools/index.html delete mode 100644 docs/site/learn/reacting-to-input-with-state/index.html delete mode 100644 docs/site/learn/referencing-values-with-refs/index.html delete mode 100644 docs/site/learn/removing-effect-dependencies/index.html delete mode 100644 docs/site/learn/render-and-commit/index.html delete mode 100644 docs/site/learn/rendering-lists/index.html delete mode 100644 docs/site/learn/responding-to-events/index.html delete mode 100644 docs/site/learn/reusing-logic-with-custom-hooks/index.html delete mode 100644 docs/site/learn/scaling-up-with-reducer-and-context/index.html delete mode 100644 docs/site/learn/separating-events-from-effects/index.html delete mode 100644 docs/site/learn/sharing-state-between-components/index.html delete mode 100644 docs/site/learn/state-a-components-memory/index.html delete mode 100644 docs/site/learn/state-as-a-snapshot/index.html delete mode 100644 docs/site/learn/synchronizing-with-effects/index.html delete mode 100644 docs/site/learn/thinking-in-react/index.html delete mode 100644 docs/site/learn/tools-and-packages/index.html delete mode 100644 docs/site/learn/tutorial-material-ui/index.html delete mode 100644 docs/site/learn/tutorial-react-bootstrap/index.html delete mode 100644 docs/site/learn/tutorial-tic-tac-toe/index.html delete mode 100644 docs/site/learn/updating-arrays-in-state/index.html delete mode 100644 docs/site/learn/updating-objects-in-state/index.html delete mode 100644 docs/site/learn/vdom-mutations/index.html delete mode 100644 docs/site/learn/writing-markup-with-psx/index.html delete mode 100644 docs/site/learn/you-might-not-need-an-effect/index.html delete mode 100644 docs/site/learn/your-first-component/index.html delete mode 100644 docs/site/objects.inv delete mode 100644 docs/site/reference/client-api/index.html delete mode 100644 docs/site/reference/common-props/index.html delete mode 100644 docs/site/reference/components-and-hooks-must-be-pure/index.html delete mode 100644 docs/site/reference/jinja/index.html delete mode 100644 docs/site/reference/overview/index.html delete mode 100644 docs/site/reference/protocol-structure/index.html delete mode 100644 docs/site/reference/react-calls-components-and-hooks/index.html delete mode 100644 docs/site/reference/reactpy-csr/index.html delete mode 100644 docs/site/reference/reactpy-middleware/index.html delete mode 100644 docs/site/reference/reactpy/index.html delete mode 100644 docs/site/reference/rules-of-hooks/index.html delete mode 100644 docs/site/reference/rules-of-react/index.html delete mode 100644 docs/site/reference/usage/index.html delete mode 100644 docs/site/reference/use-async-effect/index.html delete mode 100644 docs/site/reference/use-callback/index.html delete mode 100644 docs/site/reference/use-connection/index.html delete mode 100644 docs/site/reference/use-context/index.html delete mode 100644 docs/site/reference/use-debug-value/index.html delete mode 100644 docs/site/reference/use-deferred-value/index.html delete mode 100644 docs/site/reference/use-effect/index.html delete mode 100644 docs/site/reference/use-imperative-handle/index.html delete mode 100644 docs/site/reference/use-insertion-effect/index.html delete mode 100644 docs/site/reference/use-layout-effect/index.html delete mode 100644 docs/site/reference/use-location/index.html delete mode 100644 docs/site/reference/use-memo/index.html delete mode 100644 docs/site/reference/use-reducer/index.html delete mode 100644 docs/site/reference/use-ref/index.html delete mode 100644 docs/site/reference/use-scope/index.html delete mode 100644 docs/site/reference/use-state/index.html delete mode 100644 docs/site/reference/use-sync-external-store/index.html delete mode 100644 docs/site/reference/use-transition/index.html delete mode 100644 docs/site/search/search_index.json delete mode 100644 docs/site/sitemap.xml delete mode 100644 docs/site/sitemap.xml.gz diff --git a/docs/site/404.html b/docs/site/404.html deleted file mode 100644 index b8a5129c5..000000000 --- a/docs/site/404.html +++ /dev/null @@ -1 +0,0 @@ - ReactPy

404 - Not found

\ No newline at end of file diff --git a/docs/site/about/changelog/index.html b/docs/site/about/changelog/index.html deleted file mode 100644 index 27097ec4f..000000000 --- a/docs/site/about/changelog/index.html +++ /dev/null @@ -1 +0,0 @@ - Changelog - ReactPy

Changelog

Notable changes to ReactPy, newest first. Read the Unreleased section for what has landed since the last release, and the versioned sections for what shipped in each.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Note

Entries use the Keep a Changelog categories — Added, Changed, Deprecated, Removed, Fixed, Security — and versions follow Semantic Versioning. Pre-release versions carry a bN suffix, and behaviour can shift between betas without a major-version bump, so pin an exact version if you depend on one.

The authoritative list of shipped artefacts, with downloads and commit ranges, is GitHub Releases; the published packages are on PyPI. This page is the curated narrative of what changed, not the release log.

Unreleased

Added

  • Added support for Python 3.12, 3.13, and 3.14.
  • Added type hints to reactpy.html attributes.
  • Added support for nested components in web modules
  • Added support for inline JavaScript as event handlers or other attributes that expect a callable via reactpy.types.InlineJavaScript
  • Event functions can now call event.preventDefault(), event.stopPropagation(), and event.stopImmediatePropagation() methods directly on the event data object, rather than using the @event decorator.
  • Event data now supports accessing properties via dot notation (ex. await event.target.value).
  • Added support for partial functions in EventHandler
  • Added reactpy.types.Event to provide type hints for the standard data function argument (for example async def on_click(event: Event): ...).
  • Added asgi and jinja installation extras (for example pip install reactpy[asgi, jinja]).
  • Added reactpy.executors.asgi.ReactPy that can be used to run ReactPy in standalone mode via ASGI.
  • Added reactpy.executors.asgi.ReactPyCsr that can be used to run ReactPy in standalone mode via ASGI, but rendered entirely client-sided.
  • Added reactpy.executors.asgi.ReactPyMiddleware that can be used to utilize ReactPy within any ASGI compatible framework.
  • Added reactpy.templatetags.ReactPyJinja that can be used alongside ReactPyMiddleware to embed several ReactPy components into your existing application. This includes the following template tags: {% component %}, {% pyscript_component %}, and {% pyscript_setup %}.
  • Added reactpy.pyscript_component that can be used to embed ReactPy components into your existing application.
  • Added reactpy.use_async_effect hook.
  • Added reactpy.Vdom primitive interface for creating VDOM dictionaries.
  • Added reactpy.reactjs.component_from_file to import ReactJS components from a file.
  • Added reactpy.reactjs.component_from_url to import ReactJS components from a URL.
  • Added reactpy.reactjs.component_from_string to import ReactJS components from a string.
  • Added reactpy.reactjs.component_from_npm to import ReactJS components from NPM.
  • Added reactpy.h as a shorthand alias for reactpy.html.
  • Added reactpy.config.REACTPY_MAX_QUEUE_SIZE to configure the maximum size of all ReactPy asyncio queues (e.g. receive buffer, send buffer, event buffer) before ReactPy begins waiting until a slot frees up. This can be used to constraint memory usage.
  • Events now support debounce and throttle, configurable per event via event.debounce = <milliseconds> and EventHandler(fn, throttle=<milliseconds>) respectively.
    • debounce waits until activity stops, then fires once. Default is 200 ms on input/select/textarea, 0 ms elsewhere.
    • throttle caps the rate how often an event is allowed to execute. No default; opt in per event.
  • Added a live event proxy which lets event handlers read properties from, and call methods on, the browser objects which triggered them (ex. await event.target.value, await event.target.focus()) rather than being limited to whatever was serializable at the moment the event was dispatched.
    • Only an id and the event's type travel with the event. Whatever a handler reaches past is fetched from the browser on demand, one round trip at a time, and resolved against the object itself.
    • Properties which are in flight at the same moment go out as a single message, and await event.read("target.value", "key") reads several at once. Two handlers which want the same property at the same moment share that one message, and one which walks away from an answer it only borrowed leaves the message in place for the one which asked.
    • A read belongs to the moment it is awaited: it reports the current state of the browser object, not the state it held when the event was dispatched.
    • Whatever a read found is remembered against the event, so reading one property twice costs one round trip - and a handler which wants what the browser holds by now asks again with await event.read("target.value", refresh=True).
    • Whatever comes back out of a method call can be walked too: the page keeps what the call answered for as long as it keeps the event, so a reference into a result resolves against that answer rather than against the method.
    • Unanswered requests are bounded by reactpy.config.REACTPY_EVENT_PROXY_TIMEOUT, reactpy.config.REACTPY_EVENT_PROXY_MAX_TIMEOUTS, and reactpy.config.REACTPY_EVENT_PROXY_MAX_DEPTH.
    • What that budget counts is unanswered messages rather than properties: a batch which asked for four things and got no answer at all costs one of it, not four. The same depth bounds an argument handed to a client side method as well as the data which comes back.
    • A client which reconnects is a fresh chance at live access: binding a new sender brings it back if an earlier one was switched off for going quiet.

Changed

  • The key attribute is now stored within attributes in the VDOM spec.
  • Substitute client-side usage of react with preact.
  • Script elements no longer support behaving like effects. They now strictly behave like plain HTML scripts.
  • The reactpy.html module has been modified to allow for auto-creation of any HTML nodes. For example, you can create a <data-table> element by calling html.data_table().
  • Change set_state comparison method to check equality with == more consistently.
  • Add support for rendering @component children within vdom_to_html.
  • Renamed the use_location hook's search attribute to query_string.
  • Renamed the use_location hook's pathname attribute to path.
  • Renamed reactpy.config.REACTPY_DEBUG_MODE to reactpy.config.REACTPY_DEBUG.
  • ReactPy no longer auto-converts snake_case props to camelCase. It is now the responsibility of the user to ensure that props are in the correct format.
  • Custom JS components will now automatically assume you are using ReactJS in the absence of a bind function.
  • Refactor layout rendering logic to improve readability and maintainability.
  • The JavaScript package @reactpy/client now exports React and ReactDOM, which allows third-party components to re-use the same React instance as ReactPy.
  • reactpy.html will now automatically flatten lists recursively (ex. reactpy.html(["child1", ["child2"]]))
  • reactpy.utils.reactpy_to_string will now retain the user's original casing for data-* and aria-* attributes.
  • reactpy.utils.string_to_reactpy has been upgraded to handle more complex scenarios without causing ReactJS rendering errors.
  • reactpy.core.vdom._CustomVdomDictConstructor has been moved to reactpy.types.CustomVdomConstructor.
  • reactpy.core.vdom._EllipsisRepr has been moved to reactpy.types.EllipsisRepr.
  • reactpy.types.VdomDictConstructor has been renamed to reactpy.types.VdomConstructor.
  • REACTPY_ASYNC_RENDERING can now de-duplicate renders where necessary.
  • REACTPY_ASYNC_RENDERING is now defaulted to True for up to 40x performance improvements in environments with high concurrency.
  • Event handlers must be coroutines. Whatever is handed to EventHandler, @event, or an on_* attribute has to be awaitable, and anything which is not is refused with a TypeError as it is made into a handler. This is what makes it safe for a handler to read live event data without stalling the layout, and to call set_state() while it waits.

Deprecated

  • reactpy.web.module_from_file is deprecated. Use reactpy.reactjs.component_from_file instead.
  • reactpy.web.module_from_url is deprecated. Use reactpy.reactjs.component_from_url instead.
  • reactpy.web.module_from_string is deprecated. Use reactpy.reactjs.component_from_string instead.
  • reactpy.web.export is deprecated. Use reactpy.reactjs.component_from_* instead.
  • reactpy.web.* is deprecated. Use reactpy.reactjs.* instead.

Removed

  • Removed support for Python 3.9 and 3.10.
  • Removed the ability to import reactpy.html.* elements directly. You must now call html.* to access the elements.
  • Removed backend specific installation extras (such as pip install reactpy[starlette]).
  • Removed support for async functions within reactpy.use_effect hook. Use reactpy.use_async_effect instead.
  • Removed deprecated function module_from_template.
  • Removed deprecated exception type reactpy.core.serve.Stop.
  • Removed deprecated component reactpy.widgets.hotswap.
  • Removed the event-to-object package. The client no longer serializes an event when it dispatches one, so nothing was producing that snapshot any more (see the live event proxy above).
  • Removed reactpy.sample module.
  • Removed reactpy.svg module. Contents previously within reactpy.svg.* can now be accessed via reactpy.html.svg.*.
  • Removed reactpy.html._ function. Use reactpy.html(...) or reactpy.html.fragment(...) instead.
  • Removed reactpy.run. See the documentation for the new method to run ReactPy applications.
  • Removed reactpy.backend.*. See the documentation for the new method to run ReactPy applications.
  • Removed reactpy.core.types module. Use reactpy.types instead.
  • Removed reactpy.utils.str_to_bool.
  • Removed reactpy.utils.html_to_vdom. Use reactpy.utils.string_to_reactpy instead.
  • Removed reactpy.utils.vdom_to_html. Use reactpy.utils.reactpy_to_string instead.
  • Removed reactpy.vdom. Use reactpy.Vdom instead.
  • Removed reactpy.core.make_vdom_constructor. Use reactpy.Vdom instead.
  • Removed reactpy.core.custom_vdom_constructor. Use reactpy.Vdom instead.
  • Removed reactpy.Layout top-level re-export. Use reactpy.core.layout.Layout instead.
  • Removed reactpy.types.LayoutType. Use reactpy.types.BaseLayout instead.
  • Removed reactpy.types.ContextProviderType. Use reactpy.types.ContextProvider instead.
  • Removed reactpy.core.hooks._ContextProvider. Use reactpy.types.ContextProvider instead.
  • Removed reactpy.web.utils. Use reactpy.reactjs.utils instead.

Fixed

  • Fixed a bug where script elements would not render to the DOM as plain text.
  • Fixed a bug where the key property provided within server-side ReactPy code was failing to propagate to the front-end JavaScript components.
  • Fixed a bug where RuntimeError("Hook stack is in an invalid state") errors could be generated when using a webserver that reuses threads.
  • Fixed a bug where events on controlled inputs (e.g. html.input({"onChange": ...})) could be lost during rapid actions.
  • Fixed a bug where a render which happened while an event handler was still running could write a stale value back over a controlled input, because the event which prompted it had been acknowledged before the handler was out of the way.
  • Fixed a bug where event-proxy-response messages were dropped by reactpy.executors.asgi.ReactPyMiddleware rather than being routed to the layout which asked for them, leaving every live event read to hang until it timed out.
  • Fixed a bug where a component could stop re-rendering for the rest of its life after a scheduled render was cancelled, since the internal 'a render is already scheduled' latch was never released.
  • Allow for ReactPy and ReactJS components to be arbitrarily inserted onto the page with any possible hierarchy.

1.1.0 - 2024-11-24

Fixed

  • Fixed broken module_from_template due to a recent release of requests.
  • Fixed module_from_template not working when using Flask backend.
  • Fixed UnicodeDecodeError when using reactpy.web.export.
  • Fixed needless unmounting of JavaScript components during each ReactPy render.
  • Fixed missing event["target"]["checked"] on checkbox inputs.
  • Fixed missing static files on sdist Python distribution.

Added

  • Allow concurrently rendering discrete component trees - enable this experimental feature by setting REACTPY_ASYNC_RENDERING=true. This improves the overall responsiveness of your app in situations where larger renders would otherwise block smaller renders from executing.

Changed

  • Previously None, when present in an HTML element, would render as the string "None". Now None will not render at all. This is now equivalent to how None is handled when returned from components.
  • Move hooks from reactpy.backend.hooks into reactpy.core.hooks.

Deprecated

  • The Stop exception. Recent releases of anyio have made this exception difficult to use since it now raises an ExceptionGroup. This exception was primarily used for internal testing purposes and so is now deprecated.
  • Deprecate reactpy.backend.hooks since the hooks have been moved into reactpy.core.hooks.

1.0.2 - 2023-07-03

Fixed

  • Fix rendering bug when children change positions.

1.0.1 - 2023-06-16

Changed

  • Warn and attempt to fix missing mime types, which can result in reactpy.run not working as expected.
  • Rename reactpy.backend.BackendImplementation to reactpy.backend.BackendType.
  • Allow reactpy.run to fail in more predictable ways.

Fixed

  • Better traceback for JSON serialization errors.
  • Explain that JS component attributes must be JSON.
  • Fix reactpy.run port assignment sometimes attaching to in-use ports on Windows.
  • Fix reactpy.run not recognizing fastapi.

1.0.0 - 2023-03-14

Changed

  • Reverts PR 841 as per the conclusion in discussion 916, but preserves the ability to declare attributes with snake_case.
  • Reverts PR 886 due to issue 896.
  • Revamped element constructor interface. Now instead of passing a dictionary of attributes to element constructors, attributes are declared using keyword arguments. For example, instead of writing:

Deprecated

  • Declaration of keys via keyword arguments in standard elements. A script has been added to automatically convert old usages where possible.

Removed

  • Accidental import of reactpy.testing.

Fixed

  • Minor issues with camelCase rewrite CLI utility.
  • Minor type hint issue with VdomDictConstructor.
  • Stale event handlers after disconnect/reconnect cycle.
  • Fixed CLI not registered as entry point.
  • Unification of component and VDOM constructor interfaces.

0.44.0 - 2023-01-27

Deprecated

  • reactpy.widgets.hotswap. The function has no clear uses outside of some internal applications.

Removed

  • Ability to access element value from events via event['value'] key. Use event['target']['value'] instead.
  • Old misspelled option REACTPY_WED_MODULES_DIR.

0.43.0 - 2023-01-09

Deprecated

  • ComponentType.(). This method was implemented based on reading the React/Preact source code.

Fixed

  • Nested context does not update value if outer context should not render.
  • Detached model state on render of context consumer if unmounted and context value does not change.

0.42.0 - 2022-12-02

Added

  • Ability to customize the <head> element of ReactPy's built-in client.
  • vdom_to_html utility function.
  • Ability to subscribe to changes that are made to mutable options.
  • del_html_head_body_transform to remove <html>, <head>, and <body> while preserving children.
  • Support for form element serialization

Fixed

  • REACTPY_DEBUG_MODE is now mutable and can be changed at runtime.
  • Fix html_to_vdom improperly removing <html>, <head>, and <body> nodes.

Removed

  • Removed reactpy.html.body as it is currently unusable due to technological limitations.
  • Removed REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY option.
  • Removed serve_static_files option from backend configuration.

Deprecated

  • module_from_template.

0.41.0 - 2022-11-01

Changed

  • The hooks use_location and use_scope are no longer implementation specific and are now available as top-level imports.
  • Backend implementations now strip any URL prefix in the pathname for use_location.
  • use_state now returns a named tuple with value and set_value fields.

Added

  • New use_connection hook which returns a Connection object containing location, scope, and carrier.

0.40.2 - 2022-09-13

Changed

  • Avoid the use of JSON patch for diffing models.

0.40.1 - 2022-09-11

Fixed

  • Child models after a component fail to render.

0.40.0 - 2022-08-13

Fixed

  • Fix edge cases where html_to_vdom can fail to convert HTML.
  • Conditionally rendered components cannot use contexts.
  • Use strict equality check for text, numeric, and binary types in hooks.
  • Accidental mutation of old model causes invalid JSON Patch.

Changed

  • Set default timeout on Playwright page for testing.
  • Track contexts in hooks as state.
  • Remove non-standard name argument from create_context.

Added

  • asgiref as a dependency.
  • lxml as a dependency.

0.39.0 - 2022-06-20

Fixed

  • No module named 'reactpy.server' from reactpy.run.
  • Setting appropriate MIME type for web modules in sanic server implementation.

Changed

  • Renamed various: reactpy.testing.server to reactpy.testing.backend, ServerFixture to BackendFixture, DisplayFixture.server to DisplayFixture.backend.
  • Removed exports_default parameter from module_from_template.

Added

  • Ability to specify versions with module templates (e.g. module_from_template("react@^17.0.0", ...)).

0.38.1 - 2022-04-15

Fixed

  • Missing file extension was causing a problem with WebPack.

0.38.0 - 2022-04-15

No changes.

0.37.2 - 2022-03-27

Changed

  • Renamed proto modules to types and added a top-level reactpy.types module.

Fixed

  • Fixed a typo that caused ReactPy to use the insecure ws web-socket protocol on pages loaded with https instead of the secure wss protocol.

0.37.1 - 2022-03-05

No changes.

0.37.0 - 2022-02-27

Added

  • Support for keys in HTML fragments.
  • Use Context Hook.

Fixed

  • React warning about set state in unmounted component.
  • Missing reset of schedule_render_later flag.

0.36.3 - 2022-02-18

Fixed

  • All child states wiped upon any child key change.
  • Allow NoneType returns within components.

0.36.2 - 2022-02-02

Fixed

  • Hot fix for newly introduced DeprecatedOption.

0.36.1 - 2022-02-02

Fixed

  • Fix Key Error when Cleaning Up Event Handlers.
  • Update Script Tag Behavior.

Changed

  • Renamed configuration option REACTPY_WED_MODULES_DIR to REACTPY_WEB_MODULES_DIR.

0.36.0 - 2022-01-30

Added

  • New http.script element which can behave similarly to a standard HTML <script> or, if no attributes are given, operate similarly to an effect.

Fixed

  • State mismatch during component update.
  • Implement a script tag.

0.35.4 - 2022-01-27

Fixed

  • Keys for elements at the root of a component were not being tracked. Thus key changes for elements at the root did not trigger unmounts.

0.35.3 - 2022-01-27

Changed

  • Elements which changed type are now always deeply unmounted.

0.35.2 - 2022-01-26

Fixed

  • Allow children with the same key to vary in type.
  • Client always looks for server at "/".
  • Web modules get double file extensions with v0.35.x.

0.35.1 - 2022-01-18

Fixed

  • Re-add accidentally deleted py.typed file to distribution.

0.35.0 - 2022-01-18

Added

  • Default key for all elements and components is now their index amongst their siblings.
  • use_linked_inputs widget.
  • Starlette server implementation.

Changed

  • REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY now defaults to 1.

Fixed

  • Support Starlette Server.
  • Fix unhandled case in module_from_template.
  • Hide "Children" within REACTPY_DEBUG_MODE key warnings.
  • Bug in Element Key Identity.
  • Add iFrame to reactpy.html.
  • React warning from module_from_template.

0.34.0 - 2021-12-16

Changed

  • Element value must now be accessed via event['target']['value'] instead of event['value'].
  • Added event["currentTarget"] and event["relatedTarget"] keys to the event dictionary.

Fixed

  • Move target attributes to event['target'].

0.33.3 - 2021-10-08

Added

  • Warning stating that REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY=1 will become the default in a future release.
  • Ability to use the default export from a JavaScript module when calling module_from_template by specifying exports_default=True.

Fixed

  • Memory leak in SharedClientStateServer.
  • Cannot use default export in react template.

0.33.2 - 2021-09-05

Fixed

  • Memory leak caused by event handlers that were not being removed when components updated.

0.33.1 - 2021-09-02

Fixed

  • Regression where the root element of the layout could not be updated.

0.33.0 - 2021-09-02

Fixed

  • Client-side error in mount-01d35dc3.js.
  • Style cannot be updated.
  • Displaying error messages in the client via __error__ tag can leak secrets.
  • Examples broken in docs.

Changed

  • Modified the Custom JavaScript Component interface to add a create() function to the bind() interface.

0.32.0 - 2021-08-20

Changed

  • Custom component interface now expects a single bind() function that returns an object with render() and unmount() functions.

Fixed

  • Docs broken on Firefox.
  • URL resolution for web modules does not consider urls starting with /.
  • Query params in package name for module_from_template not stripped.
  • Search broken in docs.
  • Move src/reactpy/client out of Python package.

0.31.0 - 2021-07-14

Changed

  • Layout is now a prototype and Layout.update is no longer a public API.
  • Refactored the relationship between LifeCycleHook and Layout.
  • ComponentTypes no longer needs a unique id attribute.

0.30.1 - 2021-07-13

Fixed

  • Warning if key is parameter of component render function.

0.30.0 - 2021-06-28

Changed

  • Removed all runtime reliance on NPM.
  • reactpy.client is removed in favor of a stripped down reactpy.web module.
  • REACTPY_CLIENT_BUILD_DIR config option is replaced with REACTPY_WEB_MODULES_DIR.

0.29.0 - 2021-06-20

Changed

  • Moved the runtime client build directory to a "user data" directory.
  • Custom JS component interface has been reworked.

0.28.0 - 2021-06-01

Added

  • Support for currentTime attribute of audio/video elements.
  • Support for the files attribute from the target of input elements.
  • Model children are now passed to the JavaScript mount() function.
  • mountLayoutWithWebSocket function to @reactpy/client.

Changed

  • Refactored existing server implementations as functions adhering to a protocol.
  • Switched to a monorepo-style structure for JavaScript.
  • Use a loadImportSource() function instead of trying to infer the path to dynamic modules.

0.27.0 - 2021-05-14

Added

  • mount(element, component, props) function which can be used to bind new elements to the DOM.

0.26.0 - 2021-05-07

Fixed

  • Fix for situations where a Layout can raise an error when a component whose state has been deleted is rendered.

0.25.0 - 2021-04-30

Changed

  • Refactored layout dispatcher by switching from a class-based approach to one that leverages pure functions.

0.24.0 - 2021-04-18

Added

  • Components and elements can now have "identity" - their state can be preserved across updates.
  • REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY feature flag.

0.23.1 - 2021-04-02

Fixed

  • Non-deterministic return order in install().

0.23.0 - 2021-04-01

Added

  • Changelog to docs.
  • Automatically reconnect to server.
  • Allow no reconnect in client.
  • Cleaner way to specify import sources.
  • FastAPI render server.

0.22.1 - 2021-01-02

Fixed

  • Fix missing find_available_port arg.

0.22.0 - 2020-12-24

Fixed

  • Fix doc build.
  • Better server error capture in tests.
  • Fix type annotations.
  • Fix dark mode widget view.

Changed

  • Use Nox to run tests.
  • Add .nox to gitignore.

0.21.0 - 2020-12-16

Added

  • Tornado render server.
  • Render server documentation.

Changed

  • Renamed element to component.

0.20.1 - 2020-11-13

Fixed

  • Avoid sanic import.

0.20.0 - 2020-11-12

Added

  • Flask render server.

Changed

  • Moved idom_client_react into separate repo.
  • Removed mountLayoutWithWebSocket.
  • Removed more client implementation docs.

Fixed

  • Fix missing server event.

0.19.0 - 2020-10-19

Fixed

  • No cover last server error.

Changed

  • Refined test tooling.
  • Added all to testing module.

0.18.1 - 2020-09-25

Fixed

  • Fix missing find_available_port arg.

0.18.0 - 2020-09-23

Added

  • Module components should be accessible as attrs.

Fixed

  • Fix doc import source URL.
  • Minor JS fixes.
  • Fix idom install.
  • Better URL join logic in client.

Changed

  • Strip down test tooling.
  • Avoid issues with bundlers.

0.17.1 - 2020-08-14

Fixed

  • Fix idom install.

0.17.0 - 2020-08-08

Added

  • Import source base URL with vdom fallback.

Fixed

  • Fix types.
  • Fix mypy testing.

Changed

  • Bumped idom_client_react version.
  • Use universal wheel.

0.16.0 - 2020-06-26

Fixed

  • Put setuptools_scm back in setup.py.
  • Fix pyproject.toml.

Changed

  • Remove framework classifier.
  • Only check last error in display context.
  • Refactor and cleanup tests.
  • Add public test tooling.

0.15.0 - 2020-05-02

Added

  • More docs.
  • pyproject.toml support.

Fixed

  • Fix coverage.
  • Fix misc bugs.
  • Fix mypy.

Changed

  • Improve docs.
  • Improve log messages.
  • Rename config "item" to "entry".
  • Upgrade snowpack to max possible.
  • More tests for client management.

0.14.3 - 2020-04-10

Fixed

  • Fix JSON patch in-place (broke some components).
  • Revert bad JSON patch usage.

0.14.2 - 2020-03-30

Fixed

  • Fix displayed version in docs.

0.14.1 - 2020-03-20

Fixed

  • Remove extra spaces in element children.

0.14.0 - 2020-03-20

Added

  • idom.run utility.

Fixed

  • Fix types.
  • Fix doc examples.
  • Simplify server constructor.

0.13.0 - 2020-02-15

Added

  • Well-defined client implementation layer.

Fixed

  • Fix types.
  • Fix tests.

Changed

  • Remove ability to install modules at runtime via idom.Module.

0.12.0 - 2020-01-27

Added

  • Utility methods for LayoutUpdate (start, stop).
  • useStateRef hook.

Changed

  • Move Jupyter out of base idom into idom-jupyter package.

0.11.3 - 2020-01-14

Fixed

  • Add utility method tests to LayoutUpdate.

0.11.2 - 2020-01-14

Added

  • Test dispatcher start and stop.

0.11.1 - 2019-12-20

Changed

  • Bump JS to 0.4.2.

0.11.0 - 2019-12-18

Added

  • MountLayout utility function.

Fixed

  • Fix issues.
  • Correct package.json for idom-client-react.

Changed

  • Rename StateDispatcher to ViewDispatcher.
  • Remove private=true from imports.
  • Require anyio>=2.0.
  • Rename static/ directory to app/.

0.10.4 - 2019-12-01

Fixed

  • Fix docs.

0.10.3 - 2019-12-01

Fixed

  • Publish package before docs.

0.10.2 - 2019-11-20

Fixed

  • Fix up tests.

Added

  • Material UI example.
  • Concept of build cache.

0.10.1 - 2019-11-08

Fixed

  • Upgrade anyio (see https://github.com/agronholm/anyio/issues/155).

0.10.0 - 2019-11-07

Added

  • Fallbacks to imported JS in examples.

Fixed

  • Fix doc updates.
  • Better URL join logic.

Changed

  • Removed async event handler.
  • Renamed Renderer to Dispatcher.
  • Removed AbstractElement.id.

0.9.2 - 2019-10-14

Fixed

  • Fix docs and improve doc example runner.

0.9.1 - 2019-10-10

Fixed

  • Add setuptools_scm as docs requirement.

0.9.0 - 2019-10-06

Added

  • Documentation for async effects.
  • Fix effect cleanup timing.
  • Test async effect.

Fixed

  • Fix layout test.
  • Fix type annotations.

Changed

  • Element render functions don't need to be async.
  • Dropped Python 3.6 support.

0.7.0 - 2020-08-09

Added

  • use_reducer hook.
  • use_effect tests.
  • Tests for use_ref and use_callback.
  • HookCatcher utility.
  • Layout tests.

Fixed

  • Fix bug in use_memo.
  • Fix types for render and layout.
  • Fix flake8 ignore doc examples.

Changed

  • Rename top level examples dir to notebooks.
  • Remove unused test script.
  • Effects run just before and after full render.
  • Simplified rendering queue.
  • Removed abstract layout (renamed to Layout.dispatch).

Removed

  • Drag-drop example.

0.6.0 - 2020-06-20

Fixed

  • Various bug fixes from early development.

Added

  • Core infrastructure for component rendering.

0.5.1 - 2019-10-24

Fixed

  • Fix tests and event handlers.

0.5.0 - 2019-06-13

Added

  • use_effect hook.
  • Initial idom-jupyter support.
  • Basic client-side rendering.
  • createContext support.
  • useCallback and useMemo.
  • Initial testing infrastructure.

Fixed

  • Fix various rendering and state management bugs.

Removed

  • Legacy state management.

0.4.2 - 2019-03-20

Fixed

  • Revert "make vdom a simple dict".

0.4.1 - 2019-02-26

Fixed

  • Fix tests.

0.4.0 - 2019-02-02

Added

  • Core functionality for idom.

Changed

  • Restructure around idom-core and idom-legacy.

0.3.0 - 2018-12-18

Added

  • Core functionality with hooks (useState, useEffect, etc.).

Fixed

  • Fix initial rendering issues.

0.2.0 - 2018-10-22

Added

  • Basic functionality.

Fixed

  • Initial bug fixes.

0.1.2 - 2018-08-03

Fixed

  • Fix import issues.

0.1.0 - 2018-07-30

Added

  • Initial release.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/about/code/index.html b/docs/site/about/code/index.html deleted file mode 100644 index 172bb4a99..000000000 --- a/docs/site/about/code/index.html +++ /dev/null @@ -1,18 +0,0 @@ - Contributing Code - ReactPy

Contributing Code

Overview

ReactPy takes pull requests from anyone, and most of what gets rejected is not wrong — it is unreviewable. A short branch, a focused diff, and the checks run before you open the pull request is what makes a change easy to say yes to. This page covers the mechanics: where the code lives, how to branch, and what CI will run against you.

You will learn

  • Which branch a change belongs on, and why that matters here more than elsewhere
  • The commit and pull request conventions this repository enforces
  • What CI checks, before CI tells you

Know which repository you are in

This documentation site and the library are separate bodies of work, and the commands that work in one do nothing in the other.

Change Repository / branch
Library code, hooks, layout, backends reactive-python/reactpy → main
JavaScript client (src/js/) Same, main
These documentation pages (docs/src/, docs/examples/) reactive-python/reactpy → new-docs

The new-docs branch holds only docs/ — the package and tests/ were removed from it. A test command run there reports success without running anything.

Pitfall

Confirm the branch before you start typing, not after. A library fix committed onto the docs branch looks fine locally and becomes a rebase chore later. git branch --show-current costs nothing and settles it.

Before you start

Look for an open discussion or issue that matches what you intend to do. If one exists, comment there before writing code — a maintainer who has already thought about the problem will usually redirect you in one sentence, and that sentence is cheaper than a rejected pull request. If none exists, open a discussion first.

For anything larger than a bug fix, expect the answer to be "show me the design first". A draft pull request is the right medium for that: it runs CI, it is reviewable, and it says plainly that it is not ready to merge.

Branch, commit, push

Fork the repository if you do not have write access, then branch from an up-to-date main:

1
git checkout -b fix-my-change main
-

Name the branch for the change, not for you. fix-key-not-resetting-on-provider beats patch-1 and survives a month of review.

Match the convention of the branch you are landing on; they differ. Documentation commits on new-docs use Conventional Commits subjects — docs(learn): author X for ReactPy v2, docs(examples): fix include-marker cropping. Library history on main instead uses plain sentence subjects, because pull requests there are squash-merged and the summary is written at merge time — Fix PyScript root component not hiding after render error (#1347). In both cases the subject is the thing a reader scans to find the functional change in a long history, so write it for that reader.

One commit per logical change. If a commit only makes sense next to a later one, squash them before you push rather than asking the reviewer to mentally reassemble them.

The checklist

The repository ships a pull request template; fill it in rather than deleting it.

  • [ ] Tests have been developed for bug fixes or new functionality.
  • [ ] The changelog has been updated, if necessary.
  • [ ] Documentation has been updated, if necessary.
  • [ ] GitHub Issues closed by this pull request have been linked.

By submitting, you agree that your contribution complies with the project's open source licenses.

Tests are not optional for a behaviour change. A bug fix should arrive with the test that reproduces the bug — write it first and watch it fail, then fix and watch it pass. A test that has never failed proves nothing about the thing it claims to protect.

The changelog rule is narrower than it looks: CHANGELOG.md gets an entry under Unreleased for user-facing changes only. Test-only, docs-only, and CI-only changes get none. Adding one for those is a small annoyance to the person doing the release, who then has to notice and delete it. See Changelog.

What CI runs

Understanding the gate is cheaper than discovering it in a failed run.

1
-2
-3
-4
-5
-6
hatch test --parallel --cover --retries 10          # coverage job
-hatch test --parallel --retries 10                  # ubuntu, macos, windows × 3.11–3.14
-hatch fmt src/reactpy --check                       # lint + format
-hatch run python:type_check                         # pyright
-hatch run javascript:test
-hatch run javascript:check
-

Run the equivalents locally before you push — the same commands, not approximations of them:

1
-2
-3
hatch fmt --check
-hatch run python:type_check
-hatch test -k the_test_you_touched
-

CI retries flaky tests, which is a mercy and a trap: a genuinely racy test can go green on the third attempt and stay in the tree. If a test fails once and passes on retry, that is a bug in the test — usually a bare asyncio.sleep where poll(...) belongs.

A pull request into new-docs gets no CI at all, for two independent reasons. GitHub runs pull_request workflows from the base branch, and the only workflow that exists on new-docs is the docs deploy, which triggers on push — so nothing matches a PR. And the test-documentation job in check.yml on main is disabled outright with if: 0 (temporarily, while the Sphinx-to-MkDocs transition finishes), so even a PR targeting main runs lint and Python tests but never builds the docs. A docs change is verified by you or by nobody — run the mkdocs build --strict set from Running Tests and read its exit code. The one thing that does run: pushing to new-docs deploys with mkdocs gh-deploy --force, no --strict — expect a change to be live the moment it merges, whether or not it builds cleanly.

Working with reviewers

Respond to every comment, including the ones you are not acting on. "Not taking this because X" is a complete reply and saves a round trip; silence invites a re-review of something you already considered.

Push fixes as new commits rather than force-pushing over the reviewed range. Reviewers read the delta between your last push and this one; a force-push that rewrites history they already read makes them re-read the whole diff, and they will notice when they skip it.

If a review asks for something you believe is wrong, say so once, with evidence, and then defer if the maintainer still wants it. Review is a conversation with an asymmetry: they own the file after you leave.

Note

When a review is posted by an automated tool, the maintainers still own the decision. Do not treat "the bot did not complain" as approval, and do not treat a bot comment as a demand — respond on the merits, the same as any other comment.

After merge

Documentation changes on new-docs deploy to the live site on push, so a mistake there is visible immediately rather than at the next release. That is a reason to run the build check locally, not a reason to be nervous about contributing.

Library changes reach users only through a release, which the maintainers cut. Do not ask when your fix will ship, and do not close a closed-issue loop by demanding a release — the tag is a maintainer's call.

Recap

  • Library work on main; documentation work on new-docs. Check the branch first.
  • Start from a discussion; a one-sentence redirect beats a rejected pull request.
  • Conventional Commit subjects, one logical change per commit, branch named for the change.
  • Tests accompany behaviour changes, and they must have failed first.
  • Changelog entries for user-facing changes only.
  • Run CI's exact commands locally before pushing; treat a flaky test as a bug.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/about/community/index.html b/docs/site/about/community/index.html deleted file mode 100644 index 17596558d..000000000 --- a/docs/site/about/community/index.html +++ /dev/null @@ -1 +0,0 @@ - Community - ReactPy

Community

Overview

ReactPy is built in the open. Questions, bug reports, and ideas all have a home, and the same places are where you get help when an example on this site stops behaving. This page is a map of those places and what belongs in each.

You will learn

  • Where to ask a question, report a bug, and propose a feature
  • Which channels are official, and which are just links people repeat
  • What the code of conduct expects, and what to include in a report

Where everything happens

The ReactPy repository on GitHub is the centre of gravity. Everything else points back to it.

You want to Go to
Ask how to do something Discussions → Question
Report something broken Discussions → Problem
Propose a feature Discussions → Ideas
Show off what you built Discussions → Share
Publish a package you made Discussions → Downloadable Packages
Track a confirmed defect Issues
Chat in real time Discord

Discussions is the front door, not Issues. The repository's issue-template configuration disables blank issues and points new visitors at Discussions instead, so a question opened as an issue gets redirected rather than answered. Open an issue once a defect is confirmed and someone has reproduced it — the discussion that came first usually becomes the link inside it.

Chat

There is a Discord server for ReactPy, announced in Discussion #997 and linked from the repository README. It is the fastest place to get an answer to a "why is this doing that" question, and quiet enough that nobody minds a beginner question.

Note

An invite URL is mutable third-party state. If discord.gg/uNb5P4hA9X ever stops resolving, read the current invite from the README badge or from Discussion #997 — those two are maintained together — rather than trusting a search result, which may point at a cloned server.

Discord is good for conversation and bad for searchability. A solution that exists only in chat is invisible to the next person with the same problem. If a chat thread solves something real, post it to Discussions afterwards; if it exposes an error on this site, that is a documentation contribution.

Code of conduct

Participation is governed by the Contributor Covenant code of conduct, which lives at the repository root. It applies everywhere the project is discussed — Discussions, Issues, pull requests, Discord, and any other space where ReactPy maintainers show up.

The practical version: assume good faith, criticise code rather than people, and accept that "this is obvious" is rarely true of the person asking. The enforcement section of the document names the contact route for escalating a problem.

Packages outside this repository

ReactPy's core deliberately stays small, so much of what you might expect to find here lives in sibling repositories. Each has its own issue tracker — do not file a Django problem against the core repo.

Community-built packages are announced in Discussions → Downloadable Packages. Those are third-party: they are not reviewed before being posted, they are not pinned against a ReactPy release, and an unanswered thread there says nothing about the library. See Tools, Libraries, and Packages for how to evaluate one.

Supporting the project

ReactPy is maintained by volunteers on their own time. GitHub Sponsors is the route the project uses, matching the maintainers listed in the repository's funding configuration.

Sponsoring buys maintenance, not priority. It does not queue your issue and does not entitle anyone to a feature. If you need a specific behaviour on a deadline, the reliable path is to write it — or fund someone to write it — and open a pull request.

What to include when you ask

Most unanswered questions are unanswered because they cannot be reproduced. Four things turn a vague report into one someone can act on:

  • The exact ReactPy version. Run pip show reactpy and paste it. Behaviour shifts between pre-releases, so 2.0.0b13 and 2.0.0 are different answers.
  • The Python version and operating system.
  • The smallest snippet that still misbehaves. Cutting an app down to one component usually finds the bug before you post it.
  • Both sides of the comparison: what happened, and what you expected. For a rendering problem, include what the browser showed, not only what the code says.

Pitfall

"It doesn't work" with no traceback is not a question anyone can answer, and the error you think is irrelevant often is not the relevant one. Include the full traceback and the server-side console output. ReactPy logs failing event handlers and effects on the server while the browser stays silent, so a UI that merely stops responding usually has a server line that explains it — see Creating VDOM Event Handlers.

Recap

  • Ask in Discussions; open an issue only for a confirmed defect.
  • Real-time chat is on Discord; follow up anything durable in Discussions so it stays searchable.
  • The code of conduct governs every one of those spaces.
  • Integration packages live in sibling repos; file bugs against the repo that owns the behaviour.
  • Reproducible reports get answers: versions, smallest failing snippet, and the server log.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/about/docs/index.html b/docs/site/about/docs/index.html deleted file mode 100644 index cbb612556..000000000 --- a/docs/site/about/docs/index.html +++ /dev/null @@ -1,73 +0,0 @@ - Contributing Documentation - ReactPy

Contributing Documentation

Overview

Most of these pages were ported from the ReactJS documentation, and the port is the whole job: a page is done when every claim in it is true of ReactPy and every snippet in it runs. That is a lower bound on effort and an upper bound on trustworthiness, which is the trade this project made deliberately. This page is how to do the work and how to know it is finished.

You will learn

  • What makes a ported page "done", and how the 🚧 marker works
  • The page structure, admonitions, and link conventions this site uses
  • Why snippets live in docs/examples/ instead of inside the markdown

Where things live

1
-2
-3
-4
-5
-6
-7
-8
docs/
-├── mkdocs.yml          # nav: — every page must be listed here
-├── src/                # the pages, in markdown
-│   ├── learn/          #   tutorials and concepts
-│   ├── reference/      #   API reference
-│   └── about/          #   this section
-├── examples/           # runnable Python, one directory per page slug
-└── overrides/          # template overrides
-

Page files are kebab-case.md; example directories are the same name with underscores (learn/scaling-up-with-reducer-and-context.md → examples/scaling_up_with_reducer_and_context/). A new page must be added to nav: in mkdocs.yml, but the build will not tell you when you forget: a page missing from nav: is still built and served, and mkdocs build --strict exits 0. The only trace is an INFO line listing the unlisted pages. Read it: every page currently on that list carries a Planned / Undeveloped banner in its body and can be ignored, which is exactly how a page you did mean to publish would hide among them. Adding the entry is what puts the page in the site's navigation, which is the actual reason to do it.

What "done" means

A page is authored, not migrated. Concretely:

  • Every code sample is ReactPy that runs. No JSX, no className= outside an attribute dict, no hook that does not exist.
  • Every behavioural claim has been checked against the package or its tests — by reading the source, and for anything about rendering or events, by running it.
  • React-only sections are removed or rewritten, not translated.
  • The 🚧 badge is gone from its nav: entry.

Note

🚧 in the navigation means "this is still a ReactJS page with the React names swapped". It is a promise to the reader that the content is unreliable, so removing it is the meaningful act — the badge should never come off before the checks above are true. đŸšĢ means the page is blocked on something outside this project and is parked; commented-out nav: entries work the same way.

Port, do not rename

The pages derive from react.dev. Editing means deleting what is false as much as replacing what is named differently, and the failure mode is a page that reads fluently and is wrong.

Things that do not survive a rename:

  • Hooks that do not exist. ReactPy has twelve hooks. There is no use_id, use_transition, use_deferred_value, use_layout_effect, use_insertion_effect, use_imperative_handle, or use_sync_external_store. Some of the react.dev pages for them are parked as đŸšĢ; use_id has no page at all, because the upstream issue was closed as not planned. Never port a page that depends on one as if it worked.
  • Naming rules. Components are snake_case and lowercase — my_button, not MyButton. React's capital-letter rule does not apply, because ReactPy distinguishes a component from a tag by the call, not the case. Attribute keys stay camelCase (className, onClick), because those pass through to the DOM untouched.
  • JSX mechanics. Children are positional arguments after an optional attribute dict. There is no JSX, and no PSX in this release.
  • StrictMode and memo. Neither exists. React's double-invoke warnings and its "skip re-rendering unchanged children" optimisation have no ReactPy equivalent, so port those sections as a warning that ReactPy will not catch the problem for you.
  • && rendering. {cond && <X/>} has no safe Python spelling — False and X leaks the word False into the page, since only None renders nothing. Rewrite such examples rather than transliterating them.
  • Dependency arrays. use_effect, use_memo, and use_callback infer dependencies from the closure when you omit the argument. React's "omit the array to run after every render" is simply wrong here.

Pitfall

A page can pass every automated check while teaching something false, because the checks are syntactic. The ReactJS FIXME that sat in thinking-in-react.md claiming checked was not delivered was wrong — a checkbox onChange handler reads it back as await event.target.checked. The only defence is running the example against a real layout and reading what the client actually answered.

Page structure

Every authored page opens the same way: an ## Overview heading, an intro paragraph in a <p class="intro"> block, then a You will learn summary.

 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
## Overview
-
-<p class="intro" markdown>
-
-One or two paragraphs that say what the page solves and why it matters in ReactPy specifically.
-
-</p>
-
-!!! summary "You will learn"
-
-    -   Bullet
-    -   Bullet
-

Then the body as ## and ### sections, closing with ## Recap (bulleted, factual) and ## Related (links with a dash and a phrase explaining each).

Admonitions come from the Material theme plus assets/css/admonition.css. The set in use:

Admonition For
!!! summary "You will learn" Objectives, once, after the intro
!!! abstract "Note" Clarification that is not a warning
!!! warning "Pitfall" A mistake the reader is likely to make
!!! info "Deep Dive" Deeper mechanics a reader can skip
!!! example "Terminal" A shell session

ReactJS's <Pitfall>, <DeepDive>, <Recap>, <Challenges>, <Solution>, and <Hint> JSX tags have no MkDocs equivalent. Convert them to admonitions or headings; never copy them through, since an unrecognised tag renders as literal text.

Links are relative .md paths from the current file — ../reference/use-state.md, ./extracting-state-logic-into-a-reducer.md#the-rule-reactpy-adds — not site-absolute paths like /learn/..., which break under versioned builds and make --strict unable to verify them.

Code fences: ```python linenums="0" for fragments, bare ```python for full-file tabs, and hl_lines="3-4 7" to mark the lines under discussion. Inline code that is Python gets the #!python prefix (\#!python use_state``) so it highlights.

Examples are the source of truth

Snippets are not typed into the markdown. They are real files under docs/examples/<page_slug>/, pulled in by the include plugin:

 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
=== "app.py"
-
-    ```python
-
-    @component
-    def my_button():
-        return html.button("I'm a button!")
-
-    ```
-
-=== ":material-play: Run"
-
-    ```python
-    # TODO
-    ```
-

The rules that make this work:

  • The file is a complete, importable module — black-formatted at 88 columns, imports at the top.
  • A # start marker on its own line is what the include begins after; # end stops it. Crop at the marker so the marker itself never renders on the page.
  • A file with no marker is included whole.
  • Multiple variants of one idea go in sibling directories (task_state_props/, task_context/, task_wiring/), one tabbed group per example.
  • The :material-play: Run tab is a standing placeholder. Keep it.

Because the file is included, a snippet cannot drift from a runnable example — but nothing in the build proves the example behaves. Mount it and check the DOM before you write "this renders a list". When a page compares several versions, drive all of them through the same click sequence and assert the same observable output; "it looks the same" is how two versions of an example quietly disagree.

Also list children of a component with key= when they come from a list — html.ul([row(item, key=item["id"]) for item in items]). Omitting it renders fine and emits a debug-mode warning, which is exactly the lesson the page about keys is trying to teach.

Verifying a page

From the repository root:

1
-2
-3
hatch fmt --check docs/
-pyright --pythonpath <your-env>/bin/python docs/
-cd docs && mkdocs build --strict; echo "exit=$?"
-

Then run the examples themselves and open the built page in a browser. mkdocs build --strict catches broken internal links and unresolvable includes — both abort the build with a nonzero exit. Two things it does not catch: a page missing from nav: (an INFO line, exit 0), and a link whose #anchor does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing.

You are the only gate. The single workflow on new-docs runs mkdocs gh-deploy --force on push, and gh-deploy does not pass --strict — a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live.

Pitfall

Do not trust a component that renders without error. Set REACTPY_DEBUG=1 when probing: with debug off, a component that raised produces {"tagName": "", "error": ""} — a blank region and a 200 response. See Running Tests for the harness traps that make a broken example look green.

Process

Branch from new-docs, one page per branch, and use the same Conventional Commit subjects as the history there — docs(learn): author X for ReactPy v2. A commit that removes a 🚧 badge from nav: should be the same commit that authored the page, never a separate "mark done" commit that could be cherry-picked alone.

No CHANGELOG.md entry: documentation work is not user-facing in the sense the changelog means. See Changelog.

Note

docs/src/dictionary.txt exists for the spellcheck plugin, which is currently commented out in mkdocs.yml. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off.

Recap

  • A page is done when it runs and every claim is verified — then remove its 🚧 badge.
  • Port, do not rename: delete JSX, non-existent hooks, StrictMode, memo, && rendering, and React's dependency-array semantics.
  • Fixed structure: Overview, intro paragraph, You will learn, sections, Recap, Related.
  • Relative .md links; admonitions instead of ReactJS's JSX tags.
  • Snippets are includes from docs/examples/, cropped at # start, formatted at 88 columns.
  • --strict checks links and nothing about behaviour. Run the example.
  • Running Tests — the exact commands and how to read their failures
  • Contributing Code — branch and pull request conventions
  • Changelog — what does and does not deserve an entry
  • Community — where to ask about a page before rewriting it
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/about/licenses/index.html b/docs/site/about/licenses/index.html deleted file mode 100644 index c9857f104..000000000 --- a/docs/site/about/licenses/index.html +++ /dev/null @@ -1,18 +0,0 @@ - Licenses - ReactPy

Licenses

Overview

ReactPy's source is MIT-licensed. Much of the prose you are reading is derived from the ReactJS documentation, which carries a separate Creative Commons licence, and the two do not cover the same things. This page states which licence applies to what, and what you owe when you reuse either.

You will learn

  • Which licence covers the code, and which covers these pages
  • Why the distinction matters when you copy from one side
  • Where the third-party components in a ReactPy install come from

Source code

The ReactPy package is released under the MIT licence, copyright Reactive Python and affiliates. The full text lives in the license file at the repository root and is reproduced below.

MIT is permissive: use, copy, modify, merge, publish, distribute, sublicense, and sell, subject to keeping the copyright notice and licence text in copies of the software. It comes with no warranty, and liability is excluded.

1
-2
-3
-4
-5
-6
-7
-8
-9
## The MIT License (MIT)
-
-#### Copyright (c) Reactive Python and affiliates.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-

Documentation

These pages are a different body of work from the code, and they are separately encumbered.

Much of this documentation — including its layout and much of its content — was created with heavy influence from react.dev, which is licensed under the Creative Commons Attribution 4.0 International licence. ReactPy's behaviour often mirrors React's closely enough that the documents paraphrase, and in places copy, React's language and examples.

Note

CC BY 4.0 is also permissive, but its condition is attribution rather than keeping a licence block. If you copy substantial prose from these pages into your own documentation, credit ReactPy and, where the text is close to React's original, acknowledge the ReactJS documentation as the further source. Attributing to ReactPy alone is not sufficient for the passages that came from React.

The practical boundary: code you write against ReactPy is yours, unencumbered by either licence. These pages are text under a credit-bearing licence. A snippet in docs/examples/ is code that happens to be inside a document — treat it as MIT, since its purpose is to be pasted into a project.

Third-party components

A working ReactPy install bundles more than this repository's source: the browser-side client, the vendored JavaScript dependencies, and, for client-side rendering, PyScript. Each keeps its own upstream licence, shipped with the distribution.

Check what a release actually contains rather than trusting a summary — the package metadata and the static/ directory of the release are authoritative, and the licence files travel inside the wheel.

Licensing your own contribution

The pull request template ends with an agreement that contributions comply with the project's open source licences. It is a checkbox, but it is the whole of the provenance control this project has, so it is worth taking literally:

  • Do not paste code you do not have the right to redistribute. Code copied from a licence-incompatible project, or from an answer on a forum, is the common failure.
  • Do not copy substantial prose from react.dev into these pages without acknowledging the source on the page or in the commit.
  • If you adapted something, say what and from where in the pull request description. It costs you a sentence and saves the maintainer an investigation.

Recap

  • Source code: MIT, in LICENSE.md at the repository root.
  • These pages: derived from the ReactJS docs under CC BY 4.0 — attribution is the obligation.
  • Snippets in docs/examples/ exist to be pasted; treat them as MIT code.
  • Bundled JavaScript and PyScript keep their upstream licences, shipped with the release.
  • Contributions must be yours to license; say what you adapted and from where.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/about/running-tests/index.html b/docs/site/about/running-tests/index.html deleted file mode 100644 index 6927b80d1..000000000 --- a/docs/site/about/running-tests/index.html +++ /dev/null @@ -1,32 +0,0 @@ - Running Tests - ReactPy

Running Tests

Overview

There are two test suites in this project, and they live in different places. The Python and JavaScript suites exercise ReactPy itself and run through hatch; the documentation checks exercise the pages you are reading and run through mkdocs. Knowing which one a failing command belongs to is most of debugging it.

You will learn

  • How to run the Python test suite, and why it needs a browser installed
  • How to run the documentation build checks yourself, since no CI runs them for you
  • Which command failures are yours and which are environmental

Note

The ReactPy package and its tests/ directory live on the main branch. This documentation site is authored on the new-docs branch, which contains only docs/ — running a test command there collects zero tests rather than failing, which reads like a pass and is not one. If hatch test reports no tests ran, you are on the wrong branch, not on a clean tree.

Prerequisites

The development environment is managed by Hatch, which creates and maintains its own virtual environments. Install Hatch, then clone:

1
-2
git clone https://github.com/reactive-python/reactpy.git
-cd reactpy
-

You also need Bun for the JavaScript packages. Python must be 3.11 or newer — requires-python in pyproject.toml is >=3.11, and the test matrix runs 3.11 through 3.14.

Pitfall

Do not install the project's dependencies with pip install -r into your own environment. Hatch owns those environments, and a hand-built one drifts from CI in ways that look like your fault. The one exception is the documentation requirements, which are installed separately below.

Python tests

The suite is pytest-based and runs under Hatch:

1
-2
-3
hatch test                                    # current Python version
-hatch test --all                              # every version in the matrix
-hatch test -k test_use_connection             # a single test by name
-

CI runs hatch test --parallel --retries 10 across three operating systems and four Python versions, and a separate coverage job runs hatch test --parallel --cover --retries 10.

The suite needs a browser. Each test run installs Playwright's Chromium, builds the JavaScript client, and builds a wheel before it invokes pytest — that is what the hatch-test environment's run script does. A first run is therefore slow, and a failure inside Chromium installation looks identical to a failure in the test itself. Read the top of the output, not the bottom.

1
-2
playwright install chromium
-playwright install-deps chromium   # optional; fails harmlessly on some systems
-

Tests are session-scoped around one browser and one backend server, defined in tests/conftest.py, and that file also turns on REACTPY_DEBUG and REACTPY_ASYNC_RENDERING for the whole session. Two consequences:

  • A test that passes alone and fails in the suite may be reading state a previous test left behind. Reproduce with -k before you chase it.
  • Because debug mode is on in tests and off by default in production, some errors surface only in the suite. Debug-mode checks such as REACTPY_CHECK_VDOM_SPEC are exactly this.

Configuration lives under [tool.pytest.ini_options] in pyproject.toml: --strict-config, --strict-markers, xfail_strict, asyncio_mode = "auto", testpaths = "tests", and a 120-second per-test timeout. xfail_strict means an expected failure that unexpectedly passes is itself a failure, so an xfail you fixed must be deleted rather than left alone.

What the helpers are for

reactpy.testing exists because a UI is only correct once it renders in a browser. The acceptance bar is that the real artefact behaves, not that a function returned the expected shape.

  • DisplayFixture — serves a component and gives you a Playwright page to drive it: await display.show(my_component), then display.page.
  • BackendFixture — the server side, usually paired with DisplayFixture as in conftest.py.
  • poll(...) — retries a condition until it holds or the timeout expires. Use it instead of asyncio.sleep, which passes vacuously on a slow machine and flakes on a fast one.
  • HookCatcher and StaticEventHandler — capture a component's life-cycle hook and an event handler's target, for delivering events without a click.
  • capture_reactpy_logs, assert_reactpy_did_log, assert_reactpy_did_not_log — assert on server-side log output, which is where ReactPy puts swallowed handler and effect errors.

conftest.py also installs an autouse fixture that fails a test if any ReactPy log record carries an exception. That is deliberate: an error logged server-side while the browser stays quiet is the most common way a ReactPy bug hides. If a new test trips it, the exception is real even when the assertion you wrote passes.

JavaScript tests

The client packages are tested with Bun:

1
-2
-3
-4
hatch run javascript:test     # run the JavaScript tests
-hatch run javascript:check    # lint and format check
-hatch run javascript:fix      # lint and write fixes to disk
-hatch run javascript:build    # rebuild the client bundles
-

Changes under src/js/ need a rebuild before Python tests can see them. The test script does this itself, but a manual run against a stale bundle will not reflect your edit.

Linting and type checking

1
-2
-3
hatch fmt                     # format and lint, writing fixes
-hatch fmt --check             # the CI form: report, do not write
-hatch run python:type_check   # pyright over src/reactpy
-

CI runs hatch fmt src/reactpy --check followed by hatch run python:type_check.

Pitfall

Run hatch fmt --check before you commit, not hatch fmt. Bare hatch fmt rewrites files, so a run that "passes" has already edited your working tree — including files you did not intend to touch. Scope it (hatch fmt docs/) if you only want part of the tree.

Formatting is Ruff at a line length of 88. The rules that bite most in this repository are set in pyproject.toml; docs/**/*.py already ignores E402, S102, T201, TID252, RUF059, and INP001, because example files are teaching snippets rather than importable modules.

Documentation checks

Run these from the repository root before you commit.

Pitfall

Nothing checks the documentation for you. The only workflow on new-docs installs the requirements and runs mkdocs gh-deploy --force on push — which does not pass --strict, so a broken internal link exits 0 and deploys anyway. The commands below are the gate only because you run them; there is no CI to catch a miss, and the live site is where an uncaught one appears.

1
-2
hatch fmt --check docs/             # formatting and lint
-cd docs && mkdocs build --strict    # build, fail on warnings
-

Plus the type check over the example files, which are real Python:

1
pyright --pythonpath <your-env>/bin/python docs/
-

mkdocs build --strict is the important one: it turns broken internal links and unresolvable includes into build failures, both of which abort with a nonzero exit. It does not fail on two things you would expect it to: a page missing from nav: (only an INFO line, and the build still exits 0 while serving the orphaned page), and a link whose #anchor does not exist inside an otherwise valid target file (also exit 0). Read that nav: list yourself and check anchors by eye. Nor can it tell you whether a snippet works: the examples in docs/examples/ are the source of truth for every snippet on the site, and a snippet that does not run is a docs bug the build will not catch on its own. That part is on you.

Pitfall

Do not judge a build by its log output alone. A grep-style check on the output hides a nonzero exit:

1
cd docs && mkdocs build --strict; echo "exit=$?"
-

In a bash pipeline, use echo "${PIPESTATUS[0]}" instead — a trailing | grep that finds a match makes the pipeline's exit code meaningless.

Running the site locally

1
-2
pip install -r docs/requirements.txt
-cd docs && mkdocs serve
-

Then follow the printed URL. Editing any file under docs/src/ or docs/examples/ reloads the page.

The requirements file pins mkdocs-material==9.7.7. Earlier revisions pinned 9.4.0, which pulled in regex~=2022.4 and resolved to a release shipping wheels only through CPython 3.11 — on newer Pythons that meant a source build and a hard failure without a C compiler. The current pin dropped the regex dependency altogether, so the install works on 3.12 and up. If you are on an older branch and the docs install dies inside regex, that is the reason: use Python 3.11 or raise the pin.

Checking your work

Before calling anything done, run the gates in this order and read every failure:

  1. hatch fmt --check docs/ — formatting and lint
  2. pyright docs/ — types in the examples
  3. cd docs && mkdocs build --strict — links, includes, nav
  4. The relevant tests, for code changes
  5. The real user scenario, by hand — open the page, click the control

Steps 1–4 can all pass while the thing you built does not work. A status is not an acceptance test: run the artefact. For a docs example that means mounting it in a browser and confirming the DOM changed, because a ReactPy component can render, error server-side, and show a blank region while the HTTP response still reads 200.

Deep Dive

Two harness traps have made broken examples look green more than once. First, the decorated function is a constructor: counter itself has no .render() and Layout(counter) raises TypeError: Expected a ReactPy component, so you must pass an instance. The instance does have .render(), but calling it outside a Layout raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? for any component that uses a hook — which silently skips the behaviour you meant to test. Render through async with Layout(counter()) as layout: await layout.render(). Second, ReactPy formats exception text into the model only when REACTPY_DEBUG is on; with it off, a component that raised renders as {"tagName": "", "error": ""} — an empty region and no message. Set the variable when you are probing failures, and unset it when you are checking what a user would actually see.

Recap

  • Two suites: hatch test for ReactPy (on main), mkdocs build --strict for the docs.
  • hatch test reporting no tests ran means the suite is not there, not that it passed.
  • Python tests need Playwright's Chromium and build a wheel before pytest starts.
  • conftest.py forces debug mode and fails any test that logs an exception.
  • Run hatch fmt --check, never bare hatch fmt, before committing.
  • Check exit codes, not log text; then verify the real behaviour by hand.
Authors: Archmonger
\ No newline at end of file diff --git a/docs/site/assets/_mkdocstrings.css b/docs/site/assets/_mkdocstrings.css deleted file mode 100644 index 854048ca1..000000000 --- a/docs/site/assets/_mkdocstrings.css +++ /dev/null @@ -1,237 +0,0 @@ - -/* Avoid breaking parameter names, etc. in table cells. */ -.doc-contents td code { - word-break: normal !important; -} - -/* No line break before first paragraph of descriptions. */ -.doc-md-description, -.doc-md-description>p:first-child { - display: inline; -} - -/* No text transformation from Material for MkDocs for H5 headings. */ -.md-typeset h5 .doc-object-name { - text-transform: none; -} - -/* Max width for docstring sections tables. */ -.doc .md-typeset__table, -.doc .md-typeset__table table { - display: table !important; - width: 100%; -} - -.doc .md-typeset__table tr { - display: table-row; -} - -/* Defaults in Spacy table style. */ -.doc-param-default, -.doc-type_param-default { - float: right; -} - -/* Parameter headings must be inline, not blocks. */ -.doc-heading-parameter, -.doc-heading-type_parameter { - display: inline; -} - -/* Default font size for parameter headings. */ -.md-typeset .doc-heading-parameter { - font-size: inherit; -} - -/* Prefer space on the right, not the left of parameter permalinks. */ -.doc-heading-parameter .headerlink, -.doc-heading-type_parameter .headerlink { - margin-left: 0 !important; - margin-right: 0.2rem; -} - -/* Backward-compatibility: docstring section titles in bold. */ -.doc-section-title { - font-weight: bold; -} - -/* Backlinks crumb separator. */ -.doc-backlink-crumb { - display: inline-flex; - gap: .2rem; - white-space: nowrap; - align-items: center; - vertical-align: middle; -} -.doc-backlink-crumb:not(:first-child)::before { - background-color: var(--md-default-fg-color--lighter); - content: ""; - display: inline; - height: 1rem; - --md-path-icon: url('data:image/svg+xml;charset=utf-8,'); - -webkit-mask-image: var(--md-path-icon); - mask-image: var(--md-path-icon); - width: 1rem; -} -.doc-backlink-crumb.last { - font-weight: bold; -} - -/* Symbols in Navigation and ToC. */ -:root, :host, -[data-md-color-scheme="default"] { - --doc-symbol-parameter-fg-color: #df50af; - --doc-symbol-type_parameter-fg-color: #df50af; - --doc-symbol-attribute-fg-color: #953800; - --doc-symbol-function-fg-color: #8250df; - --doc-symbol-method-fg-color: #8250df; - --doc-symbol-class-fg-color: #0550ae; - --doc-symbol-type_alias-fg-color: #0550ae; - --doc-symbol-module-fg-color: #5cad0f; - - --doc-symbol-parameter-bg-color: #df50af1a; - --doc-symbol-type_parameter-bg-color: #df50af1a; - --doc-symbol-attribute-bg-color: #9538001a; - --doc-symbol-function-bg-color: #8250df1a; - --doc-symbol-method-bg-color: #8250df1a; - --doc-symbol-class-bg-color: #0550ae1a; - --doc-symbol-type_alias-bg-color: #0550ae1a; - --doc-symbol-module-bg-color: #5cad0f1a; -} - -[data-md-color-scheme="slate"] { - --doc-symbol-parameter-fg-color: #ffa8cc; - --doc-symbol-type_parameter-fg-color: #ffa8cc; - --doc-symbol-attribute-fg-color: #ffa657; - --doc-symbol-function-fg-color: #d2a8ff; - --doc-symbol-method-fg-color: #d2a8ff; - --doc-symbol-class-fg-color: #79c0ff; - --doc-symbol-type_alias-fg-color: #79c0ff; - --doc-symbol-module-fg-color: #baff79; - - --doc-symbol-parameter-bg-color: #ffa8cc1a; - --doc-symbol-type_parameter-bg-color: #ffa8cc1a; - --doc-symbol-attribute-bg-color: #ffa6571a; - --doc-symbol-function-bg-color: #d2a8ff1a; - --doc-symbol-method-bg-color: #d2a8ff1a; - --doc-symbol-class-bg-color: #79c0ff1a; - --doc-symbol-type_alias-bg-color: #79c0ff1a; - --doc-symbol-module-bg-color: #baff791a; -} - -code.doc-symbol { - border-radius: .1rem; - font-size: .85em; - padding: 0 .3em; - font-weight: bold; -} - -code.doc-symbol-parameter, -a code.doc-symbol-parameter { - color: var(--doc-symbol-parameter-fg-color); - background-color: var(--doc-symbol-parameter-bg-color); -} - -code.doc-symbol-parameter::after { - content: "param"; -} - -code.doc-symbol-type_parameter, -a code.doc-symbol-type_parameter { - color: var(--doc-symbol-type_parameter-fg-color); - background-color: var(--doc-symbol-type_parameter-bg-color); -} - -code.doc-symbol-type_parameter::after { - content: "type-param"; -} - -code.doc-symbol-attribute, -a code.doc-symbol-attribute { - color: var(--doc-symbol-attribute-fg-color); - background-color: var(--doc-symbol-attribute-bg-color); -} - -code.doc-symbol-attribute::after { - content: "attr"; -} - -code.doc-symbol-function, -a code.doc-symbol-function { - color: var(--doc-symbol-function-fg-color); - background-color: var(--doc-symbol-function-bg-color); -} - -code.doc-symbol-function::after { - content: "func"; -} - -code.doc-symbol-method, -a code.doc-symbol-method { - color: var(--doc-symbol-method-fg-color); - background-color: var(--doc-symbol-method-bg-color); -} - -code.doc-symbol-method::after { - content: "meth"; -} - -code.doc-symbol-class, -a code.doc-symbol-class { - color: var(--doc-symbol-class-fg-color); - background-color: var(--doc-symbol-class-bg-color); -} - -code.doc-symbol-class::after { - content: "class"; -} - - -code.doc-symbol-type_alias, -a code.doc-symbol-type_alias { - color: var(--doc-symbol-type_alias-fg-color); - background-color: var(--doc-symbol-type_alias-bg-color); -} - -code.doc-symbol-type_alias::after { - content: "type"; -} - -code.doc-symbol-module, -a code.doc-symbol-module { - color: var(--doc-symbol-module-fg-color); - background-color: var(--doc-symbol-module-bg-color); -} - -code.doc-symbol-module::after { - content: "mod"; -} - -.doc-signature .autorefs { - color: inherit; - border-bottom: 1px dotted currentcolor; -} - -/* Source code blocks (admonitions). */ -:root { - --md-admonition-icon--mkdocstrings-source: url('data:image/svg+xml;charset=utf-8,') -} -.md-typeset .admonition.mkdocstrings-source, -.md-typeset details.mkdocstrings-source { - border: none; - padding: 0; -} -.md-typeset .admonition.mkdocstrings-source:focus-within, -.md-typeset details.mkdocstrings-source:focus-within { - box-shadow: none; -} -.md-typeset .mkdocstrings-source > .admonition-title, -.md-typeset .mkdocstrings-source > summary { - background-color: inherit; -} -.md-typeset .mkdocstrings-source > .admonition-title::before, -.md-typeset .mkdocstrings-source > summary::before { - background-color: var(--md-default-fg-color); - -webkit-mask-image: var(--md-admonition-icon--mkdocstrings-source); - mask-image: var(--md-admonition-icon--mkdocstrings-source); -} diff --git a/docs/site/assets/css/admonition.css b/docs/site/assets/css/admonition.css deleted file mode 100644 index 24b33a3c2..000000000 --- a/docs/site/assets/css/admonition.css +++ /dev/null @@ -1,160 +0,0 @@ -[data-md-color-scheme="slate"] { - --admonition-border-color: transparent; - --admonition-expanded-border-color: rgba(255, 255, 255, 0.1); - --note-bg-color: rgba(43, 110, 98, 0.2); - --terminal-bg-color: #0c0c0c; - --terminal-title-bg-color: #000; - --deep-dive-bg-color: rgba(43, 52, 145, 0.2); - --you-will-learn-bg-color: #353a45; - --pitfall-bg-color: rgba(182, 87, 0, 0.2); -} -[data-md-color-scheme="default"] { - --admonition-border-color: rgba(0, 0, 0, 0.08); - --admonition-expanded-border-color: var(--admonition-border-color); - --note-bg-color: rgb(244, 251, 249); - --terminal-bg-color: rgb(64, 71, 86); - --terminal-title-bg-color: rgb(35, 39, 47); - --deep-dive-bg-color: rgb(243, 244, 253); - --you-will-learn-bg-color: rgb(246, 247, 249); - --pitfall-bg-color: rgb(254, 245, 231); -} - -.md-typeset details, -.md-typeset .admonition { - border-color: var(--admonition-border-color) !important; - box-shadow: none; -} - -.md-typeset :is(.admonition, details) { - margin: 0.55em 0; -} - -.md-typeset .admonition { - font-size: 0.7rem; -} - -.md-typeset .admonition:focus-within, -.md-typeset details:focus-within { - box-shadow: none !important; -} - -.md-typeset details[open] { - border-color: var(--admonition-expanded-border-color) !important; -} - -/* -Admonition: "summary" -React Name: "You will learn" -*/ -.md-typeset .admonition.summary { - background: var(--you-will-learn-bg-color); - padding: 0.8rem 1.4rem; - border-radius: 0.8rem; -} - -.md-typeset .summary .admonition-title { - font-size: 1rem; - background: transparent; - padding-left: 0.6rem; - padding-bottom: 0; -} - -.md-typeset .summary .admonition-title:before { - display: none; -} - -.md-typeset .admonition.summary { - border-color: #ffffff17 !important; -} - -/* -Admonition: "abstract" -React Name: "Note" -*/ -.md-typeset .admonition.abstract { - background: var(--note-bg-color); - padding: 0.8rem 1.4rem; - border-radius: 0.8rem; -} - -.md-typeset .abstract .admonition-title { - font-size: 1rem; - background: transparent; - padding-bottom: 0; - color: rgb(68, 172, 153); -} - -.md-typeset .abstract .admonition-title:before { - font-size: 1.1rem; - background: rgb(68, 172, 153); -} - -/* -Admonition: "warning" -React Name: "Pitfall" -*/ -.md-typeset .admonition.warning { - background: var(--pitfall-bg-color); - padding: 0.8rem 1.4rem; - border-radius: 0.8rem; -} - -.md-typeset .warning .admonition-title { - font-size: 1rem; - background: transparent; - padding-bottom: 0; - color: rgb(219, 125, 39); -} - -.md-typeset .warning .admonition-title:before { - font-size: 1.1rem; - background: rgb(219, 125, 39); -} - -/* -Admonition: "info" -React Name: "Deep Dive" -*/ -.md-typeset .admonition.info { - background: var(--deep-dive-bg-color); - padding: 0.8rem 1.4rem; - border-radius: 0.8rem; -} - -.md-typeset .info .admonition-title { - font-size: 1rem; - background: transparent; - padding-bottom: 0; - color: rgb(136, 145, 236); -} - -.md-typeset .info .admonition-title:before { - font-size: 1.1rem; - background: rgb(136, 145, 236); -} - -/* -Admonition: "example" -React Name: "Terminal" -*/ -.md-typeset .admonition.example { - background: var(--terminal-bg-color); - border-radius: 0.4rem; - overflow: hidden; - border: none; -} - -.md-typeset .example .admonition-title { - background: var(--terminal-title-bg-color); - color: rgb(246, 247, 249); -} - -.md-typeset .example .admonition-title:before { - background: rgb(246, 247, 249); -} - -.md-typeset .admonition.example code { - background: transparent; - color: #fff; - box-shadow: none; -} diff --git a/docs/site/assets/css/banner.css b/docs/site/assets/css/banner.css deleted file mode 100644 index b6d5a90d0..000000000 --- a/docs/site/assets/css/banner.css +++ /dev/null @@ -1,15 +0,0 @@ -body[data-md-color-scheme="slate"] { - --md-banner-bg-color: rgb(55, 81, 78); - --md-banner-font-color: #fff; -} - -body[data-md-color-scheme="default"] { - --md-banner-bg-color: #ff9; - --md-banner-font-color: #000; -} - -.md-banner--warning { - background-color: var(--md-banner-bg-color); - color: var(--md-banner-font-color); - text-align: center; -} diff --git a/docs/site/assets/css/button.css b/docs/site/assets/css/button.css deleted file mode 100644 index 2563f6b42..000000000 --- a/docs/site/assets/css/button.css +++ /dev/null @@ -1,44 +0,0 @@ -[data-md-color-scheme="slate"] { - --md-button-font-color: #fff; - --md-button-border-color: #404756; -} - -[data-md-color-scheme="default"] { - --md-button-font-color: #000; - --md-button-border-color: #8d8d8d; -} - -.md-typeset .md-button { - border-width: 1px; - border-color: var(--md-button-border-color); - border-radius: 9999px; - color: var(--md-button-font-color); - transition: - color 125ms, - background 125ms, - border-color 125ms, - transform 125ms; -} - -.md-typeset .md-button:focus, -.md-typeset .md-button:hover { - border-color: var(--md-button-border-color); - color: var(--md-button-font-color); - background: rgba(78, 87, 105, 0.05); -} - -.md-typeset .md-button.md-button--primary { - color: #fff; - border-color: transparent; - background: var(--reactpy-color-dark); -} - -.md-typeset .md-button.md-button--primary:focus, -.md-typeset .md-button.md-button--primary:hover { - border-color: transparent; - background: var(--reactpy-color-darker); -} - -.md-typeset .md-button:focus { - transform: scale(0.98); -} diff --git a/docs/site/assets/css/code.css b/docs/site/assets/css/code.css deleted file mode 100644 index 25745a044..000000000 --- a/docs/site/assets/css/code.css +++ /dev/null @@ -1,116 +0,0 @@ -:root { - --code-max-height: 17.25rem; - --md-code-backdrop: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0.03) 0px 0.8px 2px 0px, rgba(0, 0, 0, 0.047) 0px 2.7px 6.7px 0px, rgba(0, 0, 0, 0.08) 0px 12px 30px 0px; -} -[data-md-color-scheme="slate"] { - --md-code-hl-color: #ffffcf1c; - --md-code-bg-color: #16181d; - --md-code-hl-comment-color: hsla(var(--md-hue), 75%, 90%, 0.43); - --code-tab-color: rgb(52, 58, 70); - --md-code-hl-name-color: #aadafc; - --md-code-hl-string-color: hsl(21 49% 63% / 1); - --md-code-hl-keyword-color: hsl(289.67deg 35% 60%); - --md-code-hl-constant-color: hsl(213.91deg 68% 61%); - --md-code-hl-number-color: #bfd9ab; - --func-and-decorator-color: #dcdcae; - --module-import-color: #60c4ac; -} -[data-md-color-scheme="default"] { - --md-code-hl-color: #ffffcf1c; - --md-code-bg-color: rgba(208, 211, 220, 0.4); - --md-code-fg-color: rgb(64, 71, 86); - --code-tab-color: #fff; - --func-and-decorator-color: var(--md-code-hl-function-color); - --module-import-color: #e153e5; -} -[data-md-color-scheme="default"] .md-typeset .highlight > pre > code, -[data-md-color-scheme="default"] .md-typeset .highlight > table.highlighttable { - --md-code-bg-color: #fff; -} - -/* All code blocks */ -.md-typeset pre > code { - max-height: var(--code-max-height); -} - -/* Code blocks with no line number */ -.md-typeset .highlight > pre > code { - border-radius: 16px; - max-height: var(--code-max-height); - box-shadow: var(--md-code-backdrop); -} - -/* Code blocks with line numbers */ -.md-typeset .highlighttable .linenos { - max-height: var(--code-max-height); - overflow: hidden; -} -.md-typeset .highlighttable { - box-shadow: var(--md-code-backdrop); - border-radius: 8px; - overflow: hidden; -} - -/* Tabbed code blocks */ -.md-typeset .tabbed-set { - box-shadow: var(--md-code-backdrop); - border-radius: 8px; - overflow: hidden; - border: 1px solid var(--md-default-fg-color--lightest); -} -.md-typeset .tabbed-set .tabbed-block { - overflow: hidden; -} -.js .md-typeset .tabbed-set .tabbed-labels { - background: var(--code-tab-color); - margin: 0; - padding-left: 0.8rem; -} -.md-typeset .tabbed-set .tabbed-labels > label { - font-weight: 400; - font-size: 0.7rem; - padding-top: 0.55em; - padding-bottom: 0.35em; -} -.md-typeset .tabbed-set .highlighttable { - border-radius: 0; -} -.md-typeset .tabbed-labels--linked>label>a { - padding: 0em 1.25em; -} - -/* Code hightlighting colors */ - -/* Module imports */ -.highlight .nc, -.highlight .ne, -.highlight .nn, -.highlight .nv { - color: var(--module-import-color); -} - -/* Function def name and decorator */ -.highlight .nd, -.highlight .nf { - color: var(--func-and-decorator-color); -} - -/* None type */ -.highlight .kc { - color: var(--md-code-hl-constant-color); -} - -/* Keywords such as def and return */ -.highlight .k { - color: var(--md-code-hl-constant-color); -} - -/* HTML tags */ -.highlight .nt { - color: var(--md-code-hl-constant-color); -} - -/* Copy text button */ -.md-code__nav { - background: transparent; -} diff --git a/docs/site/assets/css/footer.css b/docs/site/assets/css/footer.css deleted file mode 100644 index 2f9e2687f..000000000 --- a/docs/site/assets/css/footer.css +++ /dev/null @@ -1,33 +0,0 @@ -[data-md-color-scheme="slate"] { - --md-footer-bg-color: var(--md-default-bg-color); - --md-footer-bg-color--dark: var(--md-default-bg-color); - --md-footer-border-color: var(--md-header-border-color); -} - -[data-md-color-scheme="default"] { - --md-footer-fg-color: var(--md-typeset-color); - --md-footer-fg-color--light: var(--md-typeset-color); - --md-footer-bg-color: var(--md-default-bg-color); - --md-footer-bg-color--dark: var(--md-default-bg-color); - --md-footer-border-color: var(--md-header-border-color); -} - -.md-footer { - border-top: 1px solid var(--md-footer-border-color); -} - -.md-copyright { - width: 100%; -} - -.md-copyright__highlight { - width: 100%; -} - -.legal-footer-right { - float: right; -} - -.md-copyright__highlight div { - display: inline; -} diff --git a/docs/site/assets/css/home.css b/docs/site/assets/css/home.css deleted file mode 100644 index b13eec86b..000000000 --- a/docs/site/assets/css/home.css +++ /dev/null @@ -1,297 +0,0 @@ -img.home-logo { - height: 120px; -} - -.home .row { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - padding: 6rem 0.8rem; -} - -.home .row:not(.first, .stripe) { - background: var(--row-bg-color); -} - -.home .row.stripe { - background: var(--row-stripe-bg-color); - border: 0 solid var(--stripe-border-color); - border-top-width: 1px; - border-bottom-width: 1px; -} - -.home .row.first { - text-align: center; -} - -.home .row h1 { - max-width: 28rem; - line-height: 1.15; - font-weight: 500; - margin-bottom: 0.55rem; - margin-top: -1rem; -} - -.home .row.first h1 { - margin-top: 0.55rem; - margin-bottom: -0.75rem; -} - -.home .row > p { - max-width: 35rem; - line-height: 1.5; - font-weight: 400; -} - -.home .row.first > p { - font-size: 32px; - font-weight: 500; -} - -/* Code blocks */ -.home .row .tabbed-set { - background: var(--home-tabbed-set-bg-color); - margin: 0; -} - -.home .row .tabbed-content { - padding: 20px 18px; - overflow-x: auto; -} - -.home .row .tabbed-content img { - user-select: none; - -moz-user-select: none; - -webkit-user-drag: none; - -webkit-user-select: none; - -ms-user-select: none; - max-width: 580px; -} - -.home .row .tabbed-content { - -webkit-filter: var(--code-block-filter); - filter: var(--code-block-filter); -} - -/* Code examples */ -.home .example-container { - background: - radial-gradient(circle at 0% 100%, rgb(41 84 147 / 11%) 0%, rgb(22 89 189 / 4%) 70%, rgb(48 99 175 / 0%) 80%), - radial-gradient(circle at 100% 100%, rgb(24 87 45 / 55%) 0%, rgb(29 61 12 / 4%) 70%, rgb(94 116 93 / 0%) 80%), - radial-gradient(circle at 100% 0%, rgba(54, 66, 84, 0.55) 0%, rgb(102 111 125 / 4%) 70%, rgba(54, 66, 84, 0) 80%), - radial-gradient(circle at 0% 0%, rgba(91, 114, 135, 0.55) 0%, rgb(45 111 171 / 4%) 70%, rgb(5 82 153 / 0%) 80%), - rgb(0, 0, 0) center center/cover no-repeat fixed; - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - align-items: center; - border-radius: 16px; - margin: 30px 0; - max-width: 100%; - grid-column-gap: 20px; - padding-left: 20px; - padding-right: 20px; -} - -.home .demo .white-bg { - background: #fff; - border-radius: 16px; - display: flex; - flex-direction: column; - max-width: 590px; - min-width: -webkit-min-content; - min-width: -moz-min-content; - min-width: min-content; - row-gap: 1rem; - padding: 1rem; -} - -.home .demo .vid-row { - display: flex; - flex-direction: row; - -moz-column-gap: 12px; - column-gap: 12px; -} - -.home .demo { - color: #000; -} - -.home .demo .vid-thumbnail { - background: - radial-gradient(circle at 0% 100%, rgb(41 84 147 / 55%) 0%, rgb(22 89 189 / 4%) 70%, rgb(48 99 175 / 0%) 80%), - radial-gradient(circle at 100% 100%, rgb(24 63 87 / 55%) 0%, rgb(29 61 12 / 4%) 70%, rgb(94 116 93 / 0%) 80%), - radial-gradient(circle at 100% 0%, rgba(54, 66, 84, 0.55) 0%, rgb(102 111 125 / 4%) 70%, rgba(54, 66, 84, 0) 80%), - radial-gradient(circle at 0% 0%, rgba(91, 114, 135, 0.55) 0%, rgb(45 111 171 / 4%) 70%, rgb(5 82 153 / 0%) 80%), - rgb(0, 0, 0) center center/cover no-repeat fixed; - width: 9rem; - aspect-ratio: 16 / 9; - border-radius: 8px; - display: flex; - justify-content: center; - align-items: center; -} - -.home .demo .vid-text { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - width: 100%; -} - -.home .demo h2 { - font-size: 18px; - line-height: 1.375; - margin: 0; - text-align: left; - font-weight: 700; -} - -.home .demo h3 { - font-size: 16px; - line-height: 1.25; - margin: 0; -} - -.home .demo p { - font-size: 14px; - line-height: 1.375; - margin: 0; -} - -.home .demo .browser-nav-url { - background: rgba(153, 161, 179, 0.2); - border-radius: 9999px; - font-size: 14px; - color: grey; - display: flex; - align-items: center; - justify-content: center; - -moz-column-gap: 5px; - column-gap: 5px; -} - -.home .demo .browser-navbar { - margin: -1rem; - margin-bottom: 0; - padding: 0.75rem 1rem; - border-bottom: 1px solid darkgrey; -} - -.home .demo .browser-viewport { - background: #fff; - border-radius: 16px; - display: flex; - flex-direction: column; - row-gap: 1rem; - height: 400px; - overflow-y: scroll; - margin: -1rem; - padding: 1rem; -} - -.home .demo .browser-viewport .search-header > h1 { - color: #000; - text-align: left; - font-size: 24px; - margin: 0; -} - -.home .demo .browser-viewport .search-header > p { - text-align: left; - font-size: 16px; - margin: 10px 0; -} - -.home .demo .search-bar input { - width: 100%; - background: rgba(153, 161, 179, 0.2); - border-radius: 9999px; - padding-left: 40px; - padding-right: 40px; - height: 40px; - color: #000; -} - -.home .demo .search-bar svg { - height: 40px; - position: absolute; - transform: translateX(75%); -} - -.home .demo .search-bar { - position: relative; -} - -/* Desktop Styling */ -@media screen and (min-width: 60em) { - .home .row { - text-align: center; - } - .home .row > p { - font-size: 21px; - } - .home .row > h1 { - font-size: 52px; - } - .home .row .pop-left { - margin-left: -20px; - margin-right: 0; - margin-top: -20px; - margin-bottom: -20px; - } - .home .row .pop-right { - margin-left: 0px; - margin-right: 0px; - margin-top: -20px; - margin-bottom: -20px; - } -} - -/* Mobile Styling */ -@media screen and (max-width: 60em) { - .home .row { - padding: 4rem 0.8rem; - } - .home .row > h1, - .home .row > p { - padding-left: 1rem; - padding-right: 1rem; - } - .home .row.first { - padding-top: 2rem; - } - .home-btns { - width: 100%; - display: grid; - grid-gap: 0.5rem; - gap: 0.5rem; - } - .home .example-container { - display: flex; - flex-direction: column; - row-gap: 20px; - width: 100%; - justify-content: center; - border-radius: 0; - padding: 1rem 0; - } - .home .row { - padding-left: 0; - padding-right: 0; - } - .home .tabbed-set { - width: 100%; - border-radius: 0; - } - .home .demo { - width: 100%; - display: flex; - justify-content: center; - } - .home .demo > .white-bg { - width: 80%; - max-width: 80%; - } -} diff --git a/docs/site/assets/css/main.css b/docs/site/assets/css/main.css deleted file mode 100644 index 6ca94b195..000000000 --- a/docs/site/assets/css/main.css +++ /dev/null @@ -1,85 +0,0 @@ -/* Variable overrides */ -:root { - --reactpy-color: #58b962; - --reactpy-color-dark: #42914a; - --reactpy-color-darker: #34743b; - --reactpy-color-opacity-10: rgba(88, 185, 98, 0.1); -} - -[data-md-color-accent="red"] { - --md-primary-fg-color--light: var(--reactpy-color); - --md-primary-fg-color--dark: var(--reactpy-color-dark); -} - -[data-md-color-scheme="slate"] { - --md-default-bg-color: rgb(35, 39, 47); - --md-default-bg-color--light: hsla(var(--md-hue), 15%, 16%, 0.54); - --md-default-bg-color--lighter: hsla(var(--md-hue), 15%, 16%, 0.26); - --md-default-bg-color--lightest: hsla(var(--md-hue), 15%, 16%, 0.07); - --md-primary-fg-color: var(--md-default-bg-color); - --md-default-fg-color: hsla(var(--md-hue), 75%, 95%, 1); - --md-default-fg-color--light: #fff; - --md-typeset-a-color: var(--reactpy-color); - --md-accent-fg-color: var(--reactpy-color-dark); -} - -[data-md-color-scheme="default"] { - --md-primary-fg-color: var(--md-default-bg-color); - --md-default-fg-color--light: #000; - --md-default-fg-color--lighter: #0000007e; - --md-default-fg-color--lightest: #00000029; - --md-typeset-color: rgb(35, 39, 47); - --md-typeset-a-color: var(--reactpy-color); - --md-accent-fg-color: var(--reactpy-color-dark); -} - -/* Font changes */ -.md-typeset { - font-weight: 300; -} - -.md-typeset h1 { - font-weight: 600; - margin: 0; - font-size: 2.5em; -} - -.md-typeset h2 { - font-weight: 500; -} - -.md-typeset h3 { - font-weight: 400; -} - -/* Intro section styling */ -p.intro { - font-size: 0.9rem; - font-weight: 500; -} - -/* Hide "Overview" jump selector */ -h2#overview { - visibility: hidden; - height: 0; - margin: 0; - padding: 0; -} - -/* Reduce size of the outdated banner */ -.md-banner__inner { - margin: 0.45rem auto; -} - -/* Desktop Styles */ -@media screen and (min-width: 60em) { - /* Remove max width on desktop */ - .md-grid { - max-width: none; - } -} - -/* Max size of page content */ -.md-content { - max-width: 56rem; -} diff --git a/docs/site/assets/css/navbar.css b/docs/site/assets/css/navbar.css deleted file mode 100644 index 8d47684b2..000000000 --- a/docs/site/assets/css/navbar.css +++ /dev/null @@ -1,185 +0,0 @@ -[data-md-color-scheme="slate"] { - --md-header-border-color: rgb(255 255 255 / 5%); - --md-version-bg-color: #ffffff0d; -} - -[data-md-color-scheme="default"] { - --md-header-border-color: rgb(0 0 0 / 7%); - --md-version-bg-color: #ae58ee2e; -} - -.md-header { - border: 0 solid transparent; - border-bottom-width: 1px; -} - -.md-header--shadow { - box-shadow: none; - border-color: var(--md-header-border-color); - transition: border-color 0.35s cubic-bezier(0.1, 0.7, 0.1, 1); -} - -/* Version selector */ -.md-header__topic .md-ellipsis, -.md-header__title [data-md-component="header-topic"] { - display: none; -} - -[dir="ltr"] .md-version__current { - margin: 0; -} - -.md-version__list { - margin: 0; - left: 0; - right: 0; - top: 2.5rem; -} - -.md-version { - background: var(--md-version-bg-color); - border-radius: 999px; - padding: 0 0.8rem; - margin: 0.3rem 0; - height: 1.8rem; - display: flex; - font-size: 0.7rem; -} - -/* Mobile Styling */ -@media screen and (max-width: 60em) { - label.md-header__button.md-icon[for="__drawer"] { - order: 1; - } - .md-header__button.md-logo { - display: initial; - order: 2; - margin-right: auto; - } - .md-header__title { - order: 3; - } - .md-header__button[for="__search"] { - order: 4; - } - .md-header__option[data-md-component="palette"] { - order: 5; - } - .md-header__source { - display: initial; - order: 6; - } - .md-header__source .md-source__repository { - display: none; - } -} - -/* Desktop Styling */ -@media screen and (min-width: 60em) { - /* Nav container */ - nav.md-header__inner { - display: contents; - } - header.md-header { - display: flex; - align-items: center; - } - - /* Logo */ - .md-header__button.md-logo { - order: 1; - padding-right: 0.4rem; - padding-top: 0; - padding-bottom: 0; - } - .md-header__button.md-logo img { - height: 2rem; - } - - /* Version selector */ - [dir="ltr"] .md-header__title { - order: 2; - margin: 0; - margin-right: 0.8rem; - margin-left: 0.2rem; - flex-grow: 0; - } - .md-header__topic { - position: relative; - } - .md-header__title--active .md-header__topic { - transform: none; - opacity: 1; - pointer-events: auto; - z-index: 4; - } - - /* Search */ - .md-search { - order: 3; - width: 100%; - margin-right: 0.6rem; - } - .md-search__inner { - width: 100%; - float: unset !important; - } - .md-search__form { - border-radius: 9999px; - } - [data-md-toggle="search"]:checked ~ .md-header .md-header__option { - max-width: unset; - opacity: unset; - transition: unset; - } - - /* Tabs */ - .md-tabs { - order: 4; - min-width: -webkit-fit-content; - min-width: -moz-fit-content; - min-width: fit-content; - width: -webkit-fit-content; - width: -moz-fit-content; - width: fit-content; - z-index: -1; - overflow: visible; - border: none !important; - } - li.md-tabs__item.md-tabs__item--active { - background: var(--reactpy-color-opacity-10); - border-radius: 9999px; - color: var(--md-typeset-a-color); - } - .md-tabs__link { - margin: 0; - } - .md-tabs__item { - height: 1.8rem; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - } - - /* Dark/Light Selector */ - .md-header__option[data-md-component="palette"] { - order: 5; - } - - /* GitHub info */ - .md-header__source { - order: 6; - margin-left: 0 !important; - } -} - -/* Ultrawide Desktop Styles */ -@media screen and (min-width: 1919px) { - .md-search { - order: 2; - width: 100%; - max-width: 34.4rem; - margin: 0 auto; - } -} diff --git a/docs/site/assets/css/sidebar.css b/docs/site/assets/css/sidebar.css deleted file mode 100644 index 3297966ee..000000000 --- a/docs/site/assets/css/sidebar.css +++ /dev/null @@ -1,103 +0,0 @@ -:root { - --sizebar-font-size: 0.75rem; - --sizebar-font-size-2: 0.62rem; -} - -.md-nav__link { - word-break: break-word; -} - -/* Center the docs content */ -.md-content { - justify-self: center; - width: 100%; - margin: 0 auto; -} - -/* Desktop Styling */ -@media screen and (min-width: 76.1875em) { - /* Move the sidebar and TOC to the edge of the page */ - .md-main__inner.md-grid { - margin-left: 0; - margin-right: 0; - max-width: unset; - display: grid; - grid-template-columns: auto 1fr auto; - } - [dir="ltr"] .md-nav--primary > .md-nav__list { - padding-left: 0; - } - - /* Btn section (title) */ - .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link { - text-transform: uppercase; - color: rgb(133, 142, 159); - background: transparent; - box-shadow: none; - } - - /* Btn hover styling */ - .md-nav__item .md-nav__link { - position: relative; - } - .md-nav__link:is(:focus, :hover):not(.md-nav__link--active) { - color: unset; - } - .md-nav__item .md-nav__link:is(:focus, :hover):not(.md-nav__link--active):before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0.2; - z-index: -1; - background: grey; - } - - /* Btn currently selected */ - .md-nav__item .md-nav__link--active:before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - background: var(--reactpy-color-opacity-10); - } - - /* Btn shape & font */ - .md-nav__link { - padding: 0.5rem 0.5rem 0.5rem 1rem; - margin: 0; - border-radius: 0 10px 10px 0; - font-weight: 600; - overflow: hidden; - font-size: var(--sizebar-font-size); - } - .md-sidebar__scrollwrap { - margin: 0; - } - .md-nav__item--nested .md-nav__item .md-nav__item .md-nav__link { - font-weight: 300; - font-size: var(--sizebar-font-size-2); - } - [dir="ltr"] .md-nav--primary .md-nav__list { - padding: 0; - } - [dir="ltr"] .md-nav--lifted > .md-nav__list > .md-nav__item > .md-nav:not(.md-nav--secondary) { - margin-left: 0; - } - .md-nav { - margin: 0; - } - .md-nav__toggle ~ .md-nav { - grid-template-rows: minmax(0rem, 0fr); - } - - /* Collapse icon */ - .md-nav__icon:hover { - background: transparent; - } -} diff --git a/docs/site/assets/css/table-of-contents.css b/docs/site/assets/css/table-of-contents.css deleted file mode 100644 index b524a197a..000000000 --- a/docs/site/assets/css/table-of-contents.css +++ /dev/null @@ -1,61 +0,0 @@ -/* Table of Contents styling */ -@media screen and (min-width: 60em) { - .md-nav__title[for="__toc"] { - text-transform: uppercase; - color: rgb(133, 142, 159); - } - [data-md-component="sidebar"] .md-nav__title[for="__toc"] { - text-transform: uppercase; - margin: 0.5rem; - margin-left: 0; - font-size: var(--sizebar-font-size); - } - - [data-md-component="toc"] .md-nav__item .md-nav__link--active { - position: relative; - } - - [data-md-component="toc"] .md-nav__item .md-nav__link--active:before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0.15; - z-index: -1; - background: var(--md-typeset-a-color); - } - - [data-md-component="toc"] .md-nav__link { - padding: 0.5rem 0.5rem; - padding-right: 0; - margin: 0; - border-radius: 10px 0 0 10px; - font-weight: 400; - } - - [dir="ltr"] .md-nav--secondary .md-nav__item > .md-nav__link { - margin-right: 0; - padding-right: 0.4rem; - } - - [data-md-component="toc"] .md-nav__item .md-nav__list .md-nav__item .md-nav__link { - padding-left: 1.25rem; - } - - [dir="ltr"] .md-sidebar__inner { - padding: 0; - } - - .md-nav__item { - padding: 0; - } -} - -/* Hide the toc as soon as the left sidebar is collapsed into the hamburger menu */ -@media screen and (max-width: 76.1875em) { - .md-sidebar--secondary:not([hidden]) { - display: none; - } -} diff --git a/docs/site/assets/images/add-interactivity.png b/docs/site/assets/images/add-interactivity.png deleted file mode 100644 index c32431905252537a5387e25c4ec58a3f90f87f6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22900 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 LmSPFiBJ_U%+FQ}0 diff --git a/docs/site/assets/images/create-user-interfaces.png b/docs/site/assets/images/create-user-interfaces.png deleted file mode 100644 index 06f6ea0cb6fdd4e17a6af5e374ced578cc0728f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12427 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 IM6N<$f(jP3KmY&$ diff --git a/docs/site/assets/images/s_thinking-in-react_ui.png b/docs/site/assets/images/s_thinking-in-react_ui.png deleted file mode 100644 index a3249d52647848841221c1355042c19d8852e5e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25981 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 m>lF+d>w46&AMpIS}9ujU| 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�wQwLK+5Z%JTb!YxBy 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 Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT Š Zeno Rocha - */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: -`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.textContent=a.join("")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{a.type==="ArrowRight"&&e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText)}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); -//# sourceMappingURL=bundle.d7400e89.min.js.map - diff --git a/docs/site/assets/javascripts/bundle.d7400e89.min.js.map b/docs/site/assets/javascripts/bundle.d7400e89.min.js.map deleted file mode 100644 index bd88dc979..000000000 --- a/docs/site/assets/javascripts/bundle.d7400e89.min.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], - "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an