Showing preview only (1,077K chars total). Download the full file or copy to clipboard to get everything.
Repository: reactive-python/reactpy
Branch: main
Commit: 613b256abc1b
Files: 339
Total size: 987.3 KB
Directory structure:
gitextract_qt3yaq80/
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── config.yml
│ │ └── issue-form.yml
│ ├── copilot-instructions.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── .hatch-run.yml
│ ├── check.yml
│ ├── codeql-analysis.yml
│ └── publish.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .prettierrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs/
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── docs_app/
│ │ ├── __init__.py
│ │ ├── app.py
│ │ ├── dev.py
│ │ ├── examples.py
│ │ └── prod.py
│ ├── main.py
│ ├── pyproject.toml
│ └── source/
│ ├── _custom_js/
│ │ ├── README.md
│ │ ├── bun.lockb
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ └── src/
│ │ └── index.js
│ ├── _exts/
│ │ ├── async_doctest.py
│ │ ├── autogen_api_docs.py
│ │ ├── build_custom_js.py
│ │ ├── copy_vdom_json_schema.py
│ │ ├── custom_autosectionlabel.py
│ │ ├── patched_html_translator.py
│ │ ├── reactpy_example.py
│ │ └── reactpy_view.py
│ ├── _static/
│ │ └── css/
│ │ ├── furo-theme-overrides.css
│ │ ├── larger-api-margins.css
│ │ ├── larger-headings.css
│ │ ├── reactpy-view.css
│ │ ├── sphinx-design-overrides.css
│ │ └── widget-output-css-overrides.css
│ ├── about/
│ │ ├── changelog.rst
│ │ ├── contributor-guide.rst
│ │ └── credits-and-licenses.rst
│ ├── conf.py
│ ├── guides/
│ │ ├── adding-interactivity/
│ │ │ ├── components-with-state/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── adding_state_variable/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ ├── isolated_state/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ ├── multiple_state_variables/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ └── when_variables_are_not_enough/
│ │ │ │ │ ├── data.json
│ │ │ │ │ └── main.py
│ │ │ │ └── index.rst
│ │ │ ├── dangers-of-mutability/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── dict_remove.py
│ │ │ │ │ ├── dict_update.py
│ │ │ │ │ ├── list_insert.py
│ │ │ │ │ ├── list_re_order.py
│ │ │ │ │ ├── list_remove.py
│ │ │ │ │ ├── list_replace.py
│ │ │ │ │ ├── moving_dot.py
│ │ │ │ │ ├── moving_dot_broken.py
│ │ │ │ │ ├── set_remove.py
│ │ │ │ │ └── set_update.py
│ │ │ │ └── index.rst
│ │ │ ├── index.rst
│ │ │ ├── multiple-state-updates/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── delay_before_count_updater.py
│ │ │ │ │ ├── delay_before_set_count.py
│ │ │ │ │ ├── set_color_3_times.py
│ │ │ │ │ └── set_state_function.py
│ │ │ │ └── index.rst
│ │ │ ├── responding-to-events/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── audio_player.py
│ │ │ │ │ ├── button_async_handlers.py
│ │ │ │ │ ├── button_does_nothing.py
│ │ │ │ │ ├── button_handler_as_arg.py
│ │ │ │ │ ├── button_prints_event.py
│ │ │ │ │ ├── button_prints_message.py
│ │ │ │ │ ├── prevent_default_event_actions.py
│ │ │ │ │ └── stop_event_propagation.py
│ │ │ │ └── index.rst
│ │ │ └── state-as-a-snapshot/
│ │ │ ├── _examples/
│ │ │ │ ├── delayed_print_after_set.py
│ │ │ │ ├── print_chat_message.py
│ │ │ │ ├── print_count_after_set.py
│ │ │ │ ├── send_message.py
│ │ │ │ └── set_counter_3_times.py
│ │ │ └── index.rst
│ │ ├── creating-interfaces/
│ │ │ ├── html-with-reactpy/
│ │ │ │ └── index.rst
│ │ │ ├── index.rst
│ │ │ ├── rendering-data/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── sorted_and_filtered_todo_list.py
│ │ │ │ │ ├── todo_from_list.py
│ │ │ │ │ └── todo_list_with_keys.py
│ │ │ │ └── index.rst
│ │ │ └── your-first-components/
│ │ │ ├── _examples/
│ │ │ │ ├── bad_conditional_todo_list.py
│ │ │ │ ├── good_conditional_todo_list.py
│ │ │ │ ├── nested_photos.py
│ │ │ │ ├── parametrized_photos.py
│ │ │ │ ├── simple_photo.py
│ │ │ │ ├── todo_list.py
│ │ │ │ ├── wrap_in_div.py
│ │ │ │ └── wrap_in_fragment.py
│ │ │ └── index.rst
│ │ ├── escape-hatches/
│ │ │ ├── _examples/
│ │ │ │ ├── material_ui_button_no_action.py
│ │ │ │ ├── material_ui_button_on_click.py
│ │ │ │ └── super_simple_chart/
│ │ │ │ ├── main.py
│ │ │ │ └── super-simple-chart.js
│ │ │ ├── distributing-javascript.rst
│ │ │ ├── index.rst
│ │ │ ├── javascript-components.rst
│ │ │ ├── using-a-custom-backend.rst
│ │ │ └── using-a-custom-client.rst
│ │ ├── getting-started/
│ │ │ ├── _examples/
│ │ │ │ ├── debug_error_example.py
│ │ │ │ ├── hello_world.py
│ │ │ │ ├── run_fastapi.py
│ │ │ │ ├── run_flask.py
│ │ │ │ ├── run_sanic.py
│ │ │ │ ├── run_starlette.py
│ │ │ │ ├── run_tornado.py
│ │ │ │ └── sample_app.py
│ │ │ ├── _static/
│ │ │ │ ├── embed-doc-ex.html
│ │ │ │ └── embed-reactpy-view/
│ │ │ │ ├── index.html
│ │ │ │ └── main.py
│ │ │ ├── index.rst
│ │ │ ├── installing-reactpy.rst
│ │ │ └── running-reactpy.rst
│ │ ├── managing-state/
│ │ │ ├── combining-contexts-and-reducers/
│ │ │ │ └── index.rst
│ │ │ ├── deeply-sharing-state-with-contexts/
│ │ │ │ └── index.rst
│ │ │ ├── how-to-structure-state/
│ │ │ │ └── index.rst
│ │ │ ├── index.rst
│ │ │ ├── sharing-component-state/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── filterable_list/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ └── synced_inputs/
│ │ │ │ │ └── main.py
│ │ │ │ └── index.rst
│ │ │ ├── simplifying-updates-with-reducers/
│ │ │ │ └── index.rst
│ │ │ └── when-and-how-to-reset-state/
│ │ │ └── index.rst
│ │ └── understanding-reactpy/
│ │ ├── index.rst
│ │ ├── layout-render-servers.rst
│ │ ├── representing-html.rst
│ │ ├── the-rendering-pipeline.rst
│ │ ├── the-rendering-process.rst
│ │ ├── what-are-components.rst
│ │ ├── why-reactpy-needs-keys.rst
│ │ └── writing-tests.rst
│ ├── index.rst
│ └── reference/
│ ├── _examples/
│ │ ├── character_movement/
│ │ │ └── main.py
│ │ ├── click_count.py
│ │ ├── material_ui_switch.py
│ │ ├── matplotlib_plot.py
│ │ ├── network_graph.py
│ │ ├── pigeon_maps.py
│ │ ├── simple_dashboard.py
│ │ ├── slideshow.py
│ │ ├── snake_game.py
│ │ ├── todo.py
│ │ ├── use_reducer_counter.py
│ │ ├── use_state_counter.py
│ │ └── victory_chart.py
│ ├── _static/
│ │ └── vdom-json-schema.json
│ ├── browser-events.rst
│ ├── hooks-api.rst
│ ├── html-attributes.rst
│ ├── javascript-api.rst
│ └── specifications.rst
├── pyproject.toml
├── src/
│ ├── build_scripts/
│ │ ├── build_js_app.py
│ │ ├── build_js_client.py
│ │ ├── build_js_event_to_object.py
│ │ ├── clean_js_dir.py
│ │ ├── copy_dir.py
│ │ ├── delete_old_coverage.py
│ │ └── install_playwright.py
│ ├── js/
│ │ ├── .gitignore
│ │ ├── bun.lockb
│ │ ├── eslint.config.mjs
│ │ ├── package.json
│ │ ├── packages/
│ │ │ ├── @reactpy/
│ │ │ │ ├── app/
│ │ │ │ │ ├── bun.lockb
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── preact-dom.ts
│ │ │ │ │ │ ├── preact-jsx-runtime.ts
│ │ │ │ │ │ └── preact.ts
│ │ │ │ │ └── tsconfig.json
│ │ │ │ └── client/
│ │ │ │ ├── README.md
│ │ │ │ ├── bun.lockb
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ ├── bind.tsx
│ │ │ │ │ ├── client.ts
│ │ │ │ │ ├── components.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── logger.ts
│ │ │ │ │ ├── mount.tsx
│ │ │ │ │ ├── types.ts
│ │ │ │ │ ├── vdom.tsx
│ │ │ │ │ └── websocket.ts
│ │ │ │ └── tsconfig.json
│ │ │ └── event-to-object/
│ │ │ ├── README.md
│ │ │ ├── bun.lockb
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ └── index.ts
│ │ │ ├── tests/
│ │ │ │ ├── event-to-object.test.ts
│ │ │ │ └── tooling/
│ │ │ │ ├── check.ts
│ │ │ │ ├── mock.ts
│ │ │ │ └── setup.js
│ │ │ ├── tsconfig.json
│ │ │ └── vitest.config.ts
│ │ └── tsconfig.json
│ └── reactpy/
│ ├── __init__.py
│ ├── _console/
│ │ ├── __init__.py
│ │ ├── ast_utils.py
│ │ ├── cli.py
│ │ ├── rewrite_keys.py
│ │ └── rewrite_props.py
│ ├── _html.py
│ ├── _option.py
│ ├── _warnings.py
│ ├── config.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── _f_back.py
│ │ ├── _life_cycle_hook.py
│ │ ├── _thread_local.py
│ │ ├── component.py
│ │ ├── events.py
│ │ ├── hooks.py
│ │ ├── layout.py
│ │ ├── serve.py
│ │ └── vdom.py
│ ├── executors/
│ │ ├── __init__.py
│ │ ├── asgi/
│ │ │ ├── __init__.py
│ │ │ ├── middleware.py
│ │ │ ├── pyscript.py
│ │ │ ├── standalone.py
│ │ │ └── types.py
│ │ ├── pyscript/
│ │ │ ├── __init__.py
│ │ │ ├── component_template.py
│ │ │ ├── components.py
│ │ │ ├── layout_handler.py
│ │ │ └── utils.py
│ │ └── utils.py
│ ├── logging.py
│ ├── py.typed
│ ├── reactjs/
│ │ ├── __init__.py
│ │ ├── module.py
│ │ ├── types.py
│ │ └── utils.py
│ ├── static/
│ │ └── pyscript-hide-debug.css
│ ├── templatetags/
│ │ ├── __init__.py
│ │ └── jinja.py
│ ├── testing/
│ │ ├── __init__.py
│ │ ├── backend.py
│ │ ├── common.py
│ │ ├── display.py
│ │ └── logs.py
│ ├── transforms.py
│ ├── types.py
│ ├── utils.py
│ ├── web/
│ │ ├── __init__.py
│ │ ├── module.py
│ │ └── utils.py
│ └── widgets.py
└── tests/
├── __init__.py
├── conftest.py
├── sample.py
├── templates/
│ ├── index.html
│ ├── jinja_bad_kwargs.html
│ └── pyscript.html
├── test_asgi/
│ ├── __init__.py
│ ├── pyscript_components/
│ │ ├── load_first.py
│ │ ├── load_second.py
│ │ └── root.py
│ ├── test_init.py
│ ├── test_middleware.py
│ ├── test_pyscript.py
│ ├── test_standalone.py
│ └── test_utils.py
├── test_client.py
├── test_config.py
├── test_console/
│ ├── __init__.py
│ ├── test_rewrite_keys.py
│ └── test_rewrite_props.py
├── test_core/
│ ├── __init__.py
│ ├── test_component.py
│ ├── test_events.py
│ ├── test_hooks.py
│ ├── test_layout.py
│ ├── test_serve.py
│ └── test_vdom.py
├── test_html.py
├── test_option.py
├── test_pyscript/
│ ├── __init__.py
│ ├── pyscript_components/
│ │ ├── custom_root_name.py
│ │ └── root.py
│ ├── test_components.py
│ └── test_utils.py
├── test_reactjs/
│ ├── __init__.py
│ ├── js_fixtures/
│ │ ├── callable-prop.js
│ │ ├── component-can-have-child.js
│ │ ├── export-resolution/
│ │ │ ├── index.js
│ │ │ ├── one.js
│ │ │ └── two.js
│ │ ├── exports-syntax.js
│ │ ├── exports-two-components.js
│ │ ├── generic-module.js
│ │ ├── keys-properly-propagated.js
│ │ ├── nest-custom-under-web.js
│ │ ├── set-flag-when-unmount-is-called.js
│ │ ├── simple-button.js
│ │ └── subcomponent-notation.js
│ ├── test_modules.py
│ ├── test_modules_from_npm.py
│ └── test_utils.py
├── test_sample.py
├── test_testing.py
├── test_utils.py
├── test_web/
│ ├── __init__.py
│ ├── js_fixtures/
│ │ ├── callable-prop.js
│ │ ├── component-can-have-child.js
│ │ ├── export-resolution/
│ │ │ ├── index.js
│ │ │ ├── one.js
│ │ │ └── two.js
│ │ ├── exports-syntax.js
│ │ ├── exports-two-components.js
│ │ ├── generic-module.js
│ │ ├── keys-properly-propagated.js
│ │ ├── set-flag-when-unmount-is-called.js
│ │ ├── simple-button.js
│ │ └── subcomponent-notation.js
│ └── test_module.py
├── test_widgets.py
└── tooling/
├── __init__.py
├── aio.py
├── common.py
├── hooks.py
├── layout.py
└── select.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
end_of_line = lf
[*.py]
indent_size = 4
max_line_length = 120
[*.md]
indent_size = 4
[*.yml]
indent_size = 4
[*.html]
max_line_length = off
[*.js]
max_line_length = off
[*.css]
indent_size = 4
max_line_length = off
# Tests can violate line width restrictions in the interest of clarity.
[**/test_*.py]
max_line_length = off
================================================
FILE: .github/CODEOWNERS
================================================
@maintainers
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [archmonger, rmorshea]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Start a Discussion
url: https://github.com/reactive-python/reactpy/discussions
about: Report issues, request features, ask questions, and share ideas
================================================
FILE: .github/ISSUE_TEMPLATE/issue-form.yml
================================================
name: Plan a Task
description: Create a detailed plan of action (ONLY START AFTER DISCUSSION PLEASE 🙏).
labels: ["flag-triage"]
body:
- type: textarea
attributes:
label: Current Situation
description: Discuss how things currently are, why they require action, and any relevant prior discussion/context.
validations:
required: false
- type: textarea
attributes:
label: Proposed Actions
description: Describe what ought to be done, and why that will address the reasons for action mentioned above.
validations:
required: false
================================================
FILE: .github/copilot-instructions.md
================================================
# ReactPy Development Instructions
ReactPy is a Python library for building user interfaces without JavaScript. It creates React-like components that render to web pages using a Python-to-JavaScript bridge.
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
**IMPORTANT**: This package uses modern Python tooling with Hatch for all development workflows. Always use Hatch commands for development tasks.
**BUG INVESTIGATION**: When investigating whether a bug was already resolved in a previous version, always prioritize searching through `docs/source/about/changelog.rst` first before using Git history. Only search through Git history when no relevant changelog entries are found.
## Working Effectively
### Bootstrap, Build, and Test the Repository
**Prerequisites:**
- Install Python 3.9+ from https://www.python.org/downloads/
- Install Hatch: `pip install hatch`
- Install Bun JavaScript runtime: `curl -fsSL https://bun.sh/install | bash && source ~/.bashrc`
- Install Git
**Initial Setup:**
```bash
git clone https://github.com/reactive-python/reactpy.git
cd reactpy
```
**Install Dependencies for Development:**
```bash
# Install core ReactPy dependencies
pip install fastjsonschema requests lxml anyio typing-extensions
# Install ASGI dependencies for server functionality
pip install orjson asgiref asgi-tools servestatic uvicorn fastapi
# Optional: Install additional servers
pip install flask sanic tornado
```
**Build JavaScript Packages:**
- `hatch run javascript:build` -- takes 15 seconds. NEVER CANCEL. Set timeout to 60+ minutes for safety.
- This builds three packages: event-to-object, @reactpy/client, and @reactpy/app
**Build Python Package:**
- `hatch build --clean` -- takes 10 seconds. NEVER CANCEL. Set timeout to 60+ minutes for safety.
**Run Python Tests:**
- `hatch test --parallel` -- takes 10-30 seconds for basic tests. NEVER CANCEL. Set timeout to 2 minutes for full test suite. **All tests must always pass - failures are never expected or allowed.**
- `hatch test --parallel --cover` -- run tests with coverage reporting (used in CI)
- `hatch test --parallel -k test_name` -- run specific tests
- `hatch test --parallel tests/test_config.py` -- run specific test files
**Run Python Linting and Formatting:**
- `hatch fmt` -- Run all linters and formatters (~1 second)
- `hatch fmt --check` -- Check formatting without making changes (~1 second)
- `hatch fmt --linter` -- Run only linters
- `hatch fmt --formatter` -- Run only formatters
- `hatch run python:type_check` -- Run Python type checker (~10 seconds)
**Run JavaScript Tasks:**
- `hatch run javascript:check` -- Lint and type-check JavaScript (10 seconds). NEVER CANCEL. Set timeout to 30+ minutes.
- `hatch run javascript:fix` -- Format JavaScript code
- `hatch run javascript:test` -- Run JavaScript tests
**Interactive Development Shell:**
- `hatch shell` -- Enter an interactive shell environment with all dependencies installed
- `hatch shell default` -- Enter the default development environment
- Use the shell for interactive debugging and development tasks
## Validation
Always manually validate any new code changes through these steps:
**Basic Functionality Test:**
```python
# Add src to path if not installed
import sys, os
sys.path.insert(0, os.path.join("/path/to/reactpy", "src"))
# Test that imports and basic components work
import reactpy
from reactpy import component, html, use_state
@component
def test_component():
return html.div([
html.h1("Test"),
html.p("ReactPy is working")
])
# Verify component renders
vdom = test_component()
print(f"Component rendered: {type(vdom)}")
```
**Server Functionality Test:**
```python
# Test ASGI server creation (most common deployment)
from reactpy import component, html
from reactpy.executors.asgi.standalone import ReactPy
import uvicorn
@component
def hello_world():
return html.div([
html.h1("Hello, ReactPy!"),
html.p("Server is working!")
])
# Create ASGI app (don't run to avoid hanging)
app = ReactPy(hello_world)
print("✓ ASGI server created successfully")
# To actually run: uvicorn.run(app, host="127.0.0.1", port=8000)
```
**Hooks and State Test:**
```python
from reactpy import component, html, use_state
@component
def counter_component(initial=0):
count, set_count = use_state(initial)
return html.div([
html.h1(f"Count: {count}"),
html.button({
"onClick": lambda event: set_count(count + 1)
}, "Increment")
])
# Test component with hooks
counter = counter_component(5)
print(f"✓ Hook-based component: {type(counter)}")
```
**Always run these validation steps before completing work:**
- `hatch fmt --check` -- Ensure code is properly formatted (never expected to fail)
- `hatch run python:type_check` -- Ensure no type errors (never expected to fail)
- `hatch run javascript:check` -- Ensure JavaScript passes linting (never expected to fail)
- Test basic component creation and rendering as shown above
- Test server creation if working on server-related features
- Run relevant tests with `hatch test --parallel` -- **All tests must always pass - failures are never expected or allowed**
**Integration Testing:**
- ReactPy can be deployed with FastAPI, Flask, Sanic, Tornado via ASGI
- For browser testing, Playwright is used but requires additional setup
- Test component VDOM rendering directly when browser testing isn't available
- Validate that JavaScript builds are included in Python package after changes
## Repository Structure and Navigation
### Key Directories:
- `src/reactpy/` -- Main Python package source code
- `core/` -- Core ReactPy functionality (components, hooks, VDOM)
- `web/` -- Web module management and exports
- `executors/` -- Server integration modules (ASGI, etc.)
- `testing/` -- Testing utilities and fixtures
- `pyscript/` -- PyScript integration
- `static/` -- Bundled JavaScript files
- `_html.py` -- HTML element factory functions
- `src/js/` -- JavaScript packages that get bundled with Python
- `packages/event-to-object/` -- Event serialization package
- `packages/@reactpy/client/` -- Client-side React integration
- `packages/@reactpy/app/` -- Application framework
- `src/build_scripts/` -- Build automation scripts
- `tests/` -- Python test suite with comprehensive coverage
- `docs/` -- Documentation source (MkDocs-based, transitioning setup)
### Important Files:
- `pyproject.toml` -- Python project configuration and Hatch environments
- `src/js/package.json` -- JavaScript development dependencies
- `tests/conftest.py` -- Test configuration and fixtures
- `docs/source/about/changelog.rst` -- Version history and changes
- `.github/workflows/check.yml` -- CI/CD pipeline configuration
## Common Tasks
### Build Time Expectations:
- JavaScript build: 15 seconds
- Python package build: 10 seconds
- Python linting: 1 second
- JavaScript linting: 10 seconds
- Type checking: 10 seconds
- Full CI pipeline: 5-10 minutes
### Running ReactPy Applications:
**ASGI Standalone (Recommended):**
```python
from reactpy import component, html
from reactpy.executors.asgi.standalone import ReactPy
import uvicorn
@component
def my_app():
return html.h1("Hello World")
app = ReactPy(my_app)
uvicorn.run(app, host="127.0.0.1", port=8000)
```
**With FastAPI:**
```python
from fastapi import FastAPI
from reactpy import component, html
from reactpy.executors.asgi.middleware import ReactPyMiddleware
@component
def my_component():
return html.h1("Hello from ReactPy!")
app = FastAPI()
app.add_middleware(ReactPyMiddleware, component=my_component)
```
### Creating Components:
```python
from reactpy import component, html, use_state
@component
def my_component(initial_value=0):
count, set_count = use_state(initial_value)
return html.div([
html.h1(f"Count: {count}"),
html.button({
"onClick": lambda event: set_count(count + 1)
}, "Increment")
])
```
### Working with JavaScript:
- JavaScript packages are in `src/js/packages/`
- Three main packages: event-to-object, @reactpy/client, @reactpy/app
- Built JavaScript gets bundled into `src/reactpy/static/`
- Always rebuild JavaScript after changes: `hatch run javascript:build`
## Common Hatch Commands
The following are key commands for daily development:
### Development Commands
```bash
hatch test --parallel # Run all tests (**All tests must always pass**)
hatch test --parallel --cover # Run tests with coverage (used in CI)
hatch test --parallel -k test_name # Run specific tests
hatch fmt # Format code with all formatters
hatch fmt --check # Check formatting without changes
hatch run python:type_check # Run Python type checker
hatch run javascript:build # Build JavaScript packages (15 seconds)
hatch run javascript:check # Lint JavaScript code (10 seconds)
hatch run javascript:fix # Format JavaScript code
hatch build --clean # Build Python package (10 seconds)
```
### Environment Management
```bash
hatch env show # Show all environments
hatch shell # Enter default shell
hatch shell default # Enter development shell
```
### Build Timing Expectations
- **NEVER CANCEL**: All commands complete within 60 seconds in normal operation
- **JavaScript build**: 15 seconds (hatch run javascript:build)
- **Python package build**: 10 seconds (hatch build --clean)
- **Python linting**: 1 second (hatch fmt)
- **JavaScript linting**: 10 seconds (hatch run javascript:check)
- **Type checking**: 10 seconds (hatch run python:type_check)
- **Unit tests**: 10-30 seconds (varies by test selection)
- **Full CI pipeline**: 5-10 minutes
## Development Workflow
Follow this step-by-step process for effective development:
1. **Bootstrap environment**: Ensure you have Python 3.9+ and run `pip install hatch`
2. **Make your changes** to the codebase
3. **Run formatting**: `hatch fmt` to format code (~1 second)
4. **Run type checking**: `hatch run python:type_check` for type checking (~10 seconds)
5. **Run JavaScript linting** (if JavaScript was modified): `hatch run javascript:check` (~10 seconds)
6. **Run relevant tests**: `hatch test --parallel` with specific test selection if needed. **All tests must always pass - failures are never expected or allowed.**
7. **Validate component functionality** manually using validation tests above
8. **Build JavaScript** (if modified): `hatch run javascript:build` (~15 seconds)
9. **Update documentation** when making changes to Python source code (required)
10. **Add changelog entry** for all significant changes to `docs/source/about/changelog.rst`
**IMPORTANT**: Documentation must be updated whenever changes are made to Python source code. This is enforced as part of the development workflow.
**IMPORTANT**: Significant changes must always include a changelog entry in `docs/source/about/changelog.rst` under the appropriate version section.
## Troubleshooting
### Build Issues:
- If JavaScript build fails, try: `hatch run "src/build_scripts/clean_js_dir.py"` then rebuild
- If Python build fails, ensure all dependencies in pyproject.toml are available
- Network timeouts during pip install are common in CI environments
- Missing dependencies error: Install ASGI dependencies with `pip install orjson asgiref asgi-tools servestatic`
### Import Issues:
- ReactPy must be installed or src/ must be in Python path
- Main imports: `from reactpy import component, html, use_state`
- Server imports: `from reactpy.executors.asgi.standalone import ReactPy`
- Web functionality: `from reactpy.web import export, module_from_url`
### Server Issues:
- Missing ASGI dependencies: Install with `pip install orjson asgiref asgi-tools servestatic uvicorn`
- For FastAPI integration: `pip install fastapi uvicorn`
- For Flask integration: `pip install flask` (requires additional backend package)
- For development servers, use ReactPy ASGI standalone for simplest setup
## Package Dependencies
Modern dependency management via pyproject.toml:
**Core Runtime Dependencies:**
- `fastjsonschema >=2.14.5` -- JSON schema validation
- `requests >=2` -- HTTP client library
- `lxml >=4` -- XML/HTML processing
- `anyio >=3` -- Async I/O abstraction
- `typing-extensions >=3.10` -- Type hints backport
**Optional Dependencies (install via extras):**
- `asgi` -- ASGI server support: `orjson`, `asgiref`, `asgi-tools`, `servestatic`, `pip`
- `jinja` -- Template integration: `jinja2-simple-tags`, `jinja2 >=3`
- `uvicorn` -- ASGI server: `uvicorn[standard]`
- `testing` -- Browser automation: `playwright`
- `all` -- All optional dependencies combined
**Development Dependencies (managed by Hatch):**
- **JavaScript tooling**: Bun runtime for building packages
- **Python tooling**: Hatch environments handle all dev dependencies automatically
## CI/CD Information
The repository uses GitHub Actions with these key jobs:
- `test-python-coverage` -- Python test coverage with `hatch test --parallel --cover`
- `lint-python` -- Python linting and type checking via `hatch fmt --check` and `hatch run python:type_check`
- `test-python` -- Cross-platform Python testing across Python 3.10-3.13 and Ubuntu/macOS/Windows
- `lint-javascript` -- JavaScript linting and type checking
The CI workflow is defined in `.github/workflows/check.yml` and uses the reusable workflow in `.github/workflows/.hatch-run.yml`.
**Build Matrix:**
- **Python versions**: 3.10, 3.11, 3.12, 3.13
- **Operating systems**: Ubuntu, macOS, Windows
- **Test execution**: Hatch-managed environments ensure consistency across platforms
Always ensure your changes pass local validation before pushing, as the CI pipeline will run the same checks.
## Important Notes
- **This is a Python-to-JavaScript bridge library**, not a traditional web framework - it enables React-like components in Python
- **Component rendering uses VDOM** - components return virtual DOM objects that get serialized to JavaScript
- **All builds and tests run quickly** - if something takes more than 60 seconds, investigate the issue
- **Hatch environments provide full isolation** - no need to manage virtual environments manually
- **JavaScript packages are bundled into Python** - the build process combines JS and Python into a single distribution
- **Documentation updates are required** when making changes to Python source code
- **Always update this file** when making changes to the development workflow, build process, or repository structure
- **All tests must always pass** - failures are never expected or allowed in a healthy development environment
================================================
FILE: .github/pull_request_template.md
================================================
## Description
<!-- A summary of the changes. -->
## Checklist
Please update this checklist as you complete each item:
- [ ] 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 PR have been linked.
<sub>By submitting this pull request I agree that all contributions comply with this project's open source license(s).</sub>
================================================
FILE: .github/workflows/.hatch-run.yml
================================================
name: hatch-run
on:
workflow_call:
inputs:
job-name:
required: true
type: string
run-cmd:
required: true
type: string
runs-on:
required: false
type: string
default: '["ubuntu-latest"]'
python-version:
required: false
type: string
default: '["3.x"]'
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) }}
runs-on: ${{ fromJson(inputs.runs-on) }}
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
- if: runner.os == 'Windows'
name: Cache Playwright Install
uses: actions/cache@v5
with:
path: C:\Users\runneradmin\AppData\Local\ms-playwright\
key: ${{ runner.os }}-playwright
# FIXME: Temporarily added setup-node to fix lack of "Trusted Publishing" in Bun
# Ref: https://github.com/oven-sh/bun/issues/15601
- uses: actions/setup-node@v6
with:
node-version: 24
registry-url: https://registry.npmjs.org/
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Use Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Install Python Dependencies
run: pip install hatch
- name: Run Scripts
env:
NPM_CONFIG_TOKEN: ${{ secrets.node-auth-token }}
HATCH_INDEX_USER: ${{ secrets.pypi-username }}
HATCH_INDEX_AUTH: ${{ secrets.pypi-password }}
run: ${{ inputs.run-cmd }}
================================================
FILE: .github/workflows/check.yml
================================================
name: check
on:
push:
branches:
- main
pull_request:
branches:
- "*"
schedule:
- cron: "0 0 * * 0"
jobs:
test-python-coverage:
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "python-{0}"
# Retries needed because GitHub workers sometimes lag enough to crash parallel workers
run-cmd: "hatch test --parallel --cover --retries 10"
lint-python:
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "python-{0}"
run-cmd: "hatch fmt src/reactpy --check && hatch run python:type_check"
test-python:
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "python-{0} {1}"
run-cmd: "hatch test --parallel --retries 10"
runs-on: '["ubuntu-latest", "macos-latest", "windows-latest"]'
python-version: '["3.11", "3.12", "3.13", "3.14"]'
test-documentation:
# Temporarily disabled while we transition from Sphinx to MkDocs
# https://github.com/reactive-python/reactpy/pull/1052
if: 0
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "python-{0}"
run-cmd: "hatch run docs:check"
python-version: '["3.11"]'
test-javascript:
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "{1}"
run-cmd: "hatch run javascript:test"
lint-javascript:
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "{1}"
run-cmd: "hatch run javascript:check"
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# 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
================================================
FILE: .github/workflows/publish.yml
================================================
name: publish
on:
release:
types: [published]
permissions:
contents: read # Required to checkout the code
id-token: write # Required to sign the NPM publishing statements
jobs:
publish-reactpy:
if: startsWith(github.event.release.name, 'reactpy ') || startsWith(github.event.release.tag_name, 'reactpy-')
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "Publish to PyPI"
run-cmd: "hatch run javascript:build && hatch build --clean && hatch publish --yes"
secrets:
pypi-username: ${{ secrets.PYPI_USERNAME }}
pypi-password: ${{ secrets.PYPI_PASSWORD }}
publish-reactpy-client:
if: startsWith(github.event.release.name, '@reactpy/client ') || startsWith(github.event.release.tag_name, '@reactpy/client-')
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "Publish to NPM"
run-cmd: "hatch run javascript:publish_client"
publish-event-to-object:
if: startsWith(github.event.release.name, 'event-to-object ') || startsWith(github.event.release.tag_name, 'event-to-object-')
uses: ./.github/workflows/.hatch-run.yml
with:
job-name: "Publish to NPM"
run-cmd: "hatch run javascript:publish_event_to_object"
================================================
FILE: .gitignore
================================================
# --- Build Artifacts ---
src/reactpy/static/*.js*
src/reactpy/static/morphdom/
src/reactpy/static/pyscript/
src/js/**/*.tgz
src/js/**/LICENSE
# --- Jupyter ---
*.ipynb_checkpoints
*Untitled*.ipynb
# --- Jupyter Repo 2 Docker ---
.local
.ipython
.cache
.bash_history
.python_history
.jupyter
# --- Python ---
.hatch
.venv*
venv*
MANIFEST
build
dist
.eggs
*.egg-info
__pycache__/
*.py[cod]
.tox
.nox
pip-wheel-metadata
# --- PyEnv ---
.python-version
# -- Python Tests ---
.coverage.*
*.coverage
*.pytest_cache
*.mypy_cache
# --- IDE ---
.idea
.vscode
# --- JS ---
node_modules
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: local
hooks:
- id: lint-py-fix
name: Fix Python Lint
entry: hatch run lint-py
language: system
args: [--fix]
pass_filenames: false
files: \.py$
- repo: local
hooks:
- id: lint-js-fix
name: Fix JS Lint
entry: hatch run lint-js --fix
language: system
pass_filenames: false
files: \.(js|jsx|ts|tsx)$
- repo: local
hooks:
- id: lint-py-check
name: Check Python Lint
entry: hatch run lint-py
language: system
pass_filenames: false
files: \.py$
- repo: local
hooks:
- id: lint-js-check
name: Check JS Lint
entry: hatch run lint-py
language: system
pass_filenames: false
files: \.(js|jsx|ts|tsx)$
================================================
FILE: .prettierrc
================================================
{
"proseWrap": "never",
"trailingComma": "all",
"endOfLine": "auto"
}
================================================
FILE: CHANGELOG.md
================================================
# 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).
<!--
Using the following categories, list your changes in this order:
[Added, Changed, Deprecated, Removed, Fixed, Security]
Don't forget to remove deprecated code on each major release!
-->
## [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()` 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`.
### 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.
- 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 and cascade renders where necessary.
- `REACTPY_ASYNC_RENDERING` is now defaulted to `True` for up to 40x performance improvements in environments with high concurrency.
### 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 `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.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.
- 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.
[Unreleased]: https://github.com/reactive-python/reactpy/compare/reactpy-v1.1.0...HEAD
[1.1.0]: https://github.com/reactive-python/reactpy/compare/reactpy-v1.0.2...reactpy-v1.1.0
[1.0.2]: https://github.com/reactive-python/reactpy/compare/reactpy-v1.0.1...reactpy-v1.0.2
[1.0.1]: https://github.com/reactive-python/reactpy/compare/reactpy-v1.0.0...reactpy-v1.0.1
[1.0.0]: https://github.com/reactive-python/reactpy/compare/0.44.0...reactpy-v1.0.0
[0.44.0]: https://github.com/reactive-python/reactpy/compare/0.43.0...0.44.0
[0.43.0]: https://github.com/reactive-python/reactpy/compare/0.42.0...0.43.0
[0.42.0]: https://github.com/reactive-python/reactpy/compare/0.41.0...0.42.0
[0.41.0]: https://github.com/reactive-python/reactpy/compare/0.40.2...0.41.0
[0.40.2]: https://github.com/reactive-python/reactpy/compare/0.40.1...0.40.2
[0.40.1]: https://github.com/reactive-python/reactpy/compare/0.40.0...0.40.1
[0.40.0]: https://github.com/reactive-python/reactpy/compare/0.39.0...0.40.0
[0.39.0]: https://github.com/reactive-python/reactpy/compare/0.38.1...0.39.0
[0.38.1]: https://github.com/reactive-python/reactpy/compare/0.38.0...0.38.1
[0.38.0]: https://github.com/reactive-python/reactpy/compare/0.37.2...0.38.0
[0.37.2]: https://github.com/reactive-python/reactpy/compare/0.37.1...0.37.2
[0.37.1]: https://github.com/reactive-python/reactpy/compare/0.37.0...0.37.1
[0.37.0]: https://github.com/reactive-python/reactpy/compare/0.36.3...0.37.0
[0.36.3]: https://github.com/reactive-python/reactpy/compare/0.36.2...0.36.3
[0.36.2]: https://github.com/reactive-python/reactpy/compare/0.36.1...0.36.2
[0.36.1]: https://github.com/reactive-python/reactpy/compare/0.36.0...0.36.1
[0.36.0]: https://github.com/reactive-python/reactpy/compare/0.35.4...0.36.0
[0.35.4]: https://github.com/reactive-python/reactpy/compare/0.35.3...0.35.4
[0.35.3]: https://github.com/reactive-python/reactpy/compare/0.35.2...0.35.3
[0.35.2]: https://github.com/reactive-python/reactpy/compare/0.35.1...0.35.2
[0.35.1]: https://github.com/reactive-python/reactpy/compare/0.35.0...0.35.1
[0.35.0]: https://github.com/reactive-python/reactpy/compare/0.34.0...0.35.0
[0.34.0]: https://github.com/reactive-python/reactpy/compare/0.33.3...0.34.0
[0.33.3]: https://github.com/reactive-python/reactpy/compare/0.33.2...0.33.3
[0.33.2]: https://github.com/reactive-python/reactpy/compare/0.33.1...0.33.2
[0.33.1]: https://github.com/reactive-python/reactpy/compare/0.33.0...0.33.1
[0.33.0]: https://github.com/reactive-python/reactpy/compare/0.32.0...0.33.0
[0.32.0]: https://github.com/reactive-python/reactpy/compare/0.31.0...0.32.0
[0.31.0]: https://github.com/reactive-python/reactpy/compare/0.30.1...0.31.0
[0.30.1]: https://github.com/reactive-python/reactpy/compare/0.30.0...0.30.1
[0.30.0]: https://github.com/reactive-python/reactpy/compare/0.29.0...0.30.0
[0.29.0]: https://github.com/reactive-python/reactpy/compare/0.28.0...0.29.0
[0.28.0]: https://github.com/reactive-python/reactpy/compare/0.27.0...0.28.0
[0.27.0]: https://github.com/reactive-python/reactpy/compare/0.26.0...0.27.0
[0.26.0]: https://github.com/reactive-python/reactpy/compare/0.25.0...0.26.0
[0.25.0]: https://github.com/reactive-python/reactpy/compare/0.24.0...0.25.0
[0.24.0]: https://github.com/reactive-python/reactpy/compare/0.23.1...0.24.0
[0.23.1]: https://github.com/reactive-python/reactpy/compare/0.23.0...0.23.1
[0.23.0]: https://github.com/reactive-python/reactpy/releases/tag/0.23.0
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at ryan.morshead@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE
================================================
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.
================================================
FILE: README.md
================================================
# <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy
<p>
<a href="https://github.com/reactive-python/reactpy/actions/workflows/check.yml">
<img src="https://github.com/reactive-python/reactpy/actions/workflows/check.yml/badge.svg">
</a>
<a href="https://pypi.org/project/reactpy/">
<img src="https://img.shields.io/pypi/v/reactpy.svg?label=PyPI">
</a>
<a href="https://github.com/reactive-python/reactpy/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-purple.svg">
</a>
<a href="https://reactpy.dev/">
<img src="https://img.shields.io/website?down_message=offline&label=Docs&logo=read-the-docs&logoColor=white&up_message=online&url=https%3A%2F%2Freactpy.dev%2Fdocs%2Findex.html">
</a>
<a href="https://discord.gg/uNb5P4hA9X">
<img src="https://img.shields.io/discord/1111078259854168116?label=Discord&logo=discord">
</a>
</p>
[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.
<table align="center">
<thead>
<tr>
<th colspan="2" style="text-align: center">Supported Backends</th>
<tr>
<th style="text-align: center">Built-in</th>
<th style="text-align: center">External</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a href="https://reactpy.dev/docs/guides/getting-started/installing-reactpy.html#officially-supported-servers">
Flask, FastAPI, Sanic, Tornado
</a>
</td>
<td>
<a href="https://github.com/reactive-python/reactpy-django">Django</a>,
<a href="https://github.com/reactive-python/reactpy-jupyter">Jupyter</a>,
<a href="https://github.com/idom-team/idom-dash">Plotly-Dash</a>
</td>
</tr>
</tbody>
</table>
# 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)
================================================
FILE: docs/.gitignore
================================================
build
source/_auto
source/_static/custom.js
source/vdom-json-schema.json
================================================
FILE: docs/Dockerfile
================================================
FROM python:3.11
WORKDIR /app/
RUN apt-get update
# Create/Activate Python Venv
# ---------------------------
ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# Install Python Build Dependencies
# ---------------------------------
RUN pip install --upgrade pip poetry hatch uv
RUN curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:$PATH"
# Copy Files
# ----------
COPY LICENSE ./
COPY README.md ./
COPY pyproject.toml ./
COPY src ./src
COPY docs ./docs
COPY branding ./branding
# Install and Build Docs
# ----------------------
WORKDIR /app/docs/
RUN poetry install -v
RUN sphinx-build -v -W -b html source build
# Define Entrypoint
# -----------------
ENV PORT=5000
ENV REACTPY_DEBUG=1
ENV REACTPY_CHECK_VDOM_SPEC=0
CMD ["python", "main.py"]
================================================
FILE: docs/README.md
================================================
# ReactPy's Documentation
...
================================================
FILE: docs/docs_app/__init__.py
================================================
================================================
FILE: docs/docs_app/app.py
================================================
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.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
================================================
FILE: docs/docs_app/dev.py
================================================
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)
================================================
FILE: docs/docs_app/examples.py
================================================
from __future__ import annotations
from collections.abc import Callable, Iterator
from io import StringIO
from pathlib import Path
from traceback import format_exc
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()
================================================
FILE: docs/docs_app/prod.py
================================================
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"))),
)
================================================
FILE: docs/main.py
================================================
import sys
from docs_app import dev, prod
if __name__ == "__main__":
if len(sys.argv) == 1:
prod.main()
else:
dev.main()
================================================
FILE: docs/pyproject.toml
================================================
[tool.poetry]
name = "docs_app"
version = "0.0.0"
description = "docs"
authors = ["rmorshea <ryan.morshead@gmail.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
furo = "2022.04.07"
reactpy = { path = "..", extras = ["all"], develop = false }
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"
================================================
FILE: docs/source/_custom_js/README.md
================================================
# Custom Javascript for ReactPy's Docs
Build the javascript with
```
bun run build
```
This will drop a javascript bundle into `../_static/custom.js`
================================================
FILE: docs/source/_custom_js/package.json
================================================
{
"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"
}
}
================================================
FILE: docs/source/_custom_js/rollup.config.js
================================================
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);
},
};
================================================
FILE: docs/source/_custom_js/src/index.js
================================================
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);
}
================================================
FILE: docs/source/_exts/async_doctest.py
================================================
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)
================================================
FILE: docs/source/_exts/autogen_api_docs.py
================================================
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 / "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))
elif not file.name.startswith("_"):
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
================================================
FILE: docs/source/_exts/build_custom_js.py
================================================
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("bun install", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607
subprocess.run("bun run build", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607
================================================
FILE: docs/source/_exts/copy_vdom_json_schema.py
================================================
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)
================================================
FILE: docs/source/_exts/custom_autosectionlabel.py
================================================
"""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,
}
================================================
FILE: docs/source/_exts/patched_html_translator.py
================================================
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)
================================================
FILE: docs/source/_exts/reactpy_example.py
================================================
from __future__ import annotations
import re
from pathlib import Path
from typing import Any, ClassVar
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: ClassVar[dict[str, Any]] = {
"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)
================================================
FILE: docs/source/_exts/reactpy_view.py
================================================
import os
from typing import Any, ClassVar
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: ClassVar[dict[str, Any]] = {
"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"""
<div>
<div
id="{container_id}"
class="interactive widget-container"
style="margin-bottom: {self.options.get("margin", 0)}px;"
/>
<script type="module">
import {{ mountWidgetExample }} from "{_REACTPY_STATIC_HOST}/_static/custom.js";
mountWidgetExample(
"{container_id}",
"{view_id}",
"{_REACTPY_EXAMPLE_HOST}",
{"true" if "activate-button" in self.options else "false"},
);
</script>
</div>
""",
format="html",
)
]
def setup(app: Sphinx) -> None:
app.add_directive("reactpy-view", IteractiveWidget)
================================================
FILE: docs/source/_static/css/furo-theme-overrides.css
================================================
.sidebar-container {
width: 18em;
}
.sidebar-brand-text {
display: none;
}
================================================
FILE: docs/source/_static/css/larger-api-margins.css
================================================
:is(.data, .function, .class, .exception).py {
margin-top: 3em;
}
:is(.attribute, .method).py {
margin-top: 1.8em;
}
================================================
FILE: docs/source/_static/css/larger-headings.css
================================================
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 1.5em !important;
font-weight: 900 !important;
}
================================================
FILE: docs/source/_static/css/reactpy-view.css
================================================
.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;
}
================================================
FILE: docs/source/_static/css/sphinx-design-overrides.css
================================================
.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;
}
================================================
FILE: docs/source/_static/css/widget-output-css-overrides.css
================================================
.widget-container h1,
.widget-container h2,
.widget-container h3,
.widget-container h4,
.widget-container h5,
.widget-container h6 {
margin: 0 !important;
}
================================================
FILE: docs/source/about/changelog.rst
================================================
.. THIS CHANGELOG HAS BEEN DEPRECATED. SEE TOP LEVEL CHANGELOG.md FILE INSTEAD. ---
Changelog
=========
.. note::
All notable changes to this project will be recorded in this document. The style of
which is based on `Keep a Changelog <https://keepachangelog.com/>`__. The versioning
scheme for the project adheres to `Semantic Versioning <https://semver.org/>`__.
.. Using the following categories, list your changes in this order:
.. [Added, Changed, Deprecated, Removed, Fixed, Security]
.. Don't forget to remove deprecated code on each major release!
Unreleased
----------
**Added**
- :pull:`1113` - Added support for Python 3.12, 3.13, and 3.14.
- :pull:`1281` - Added type hints to ``reactpy.html`` attributes.
- :pull:`1285` - Added support for nested components in web modules
- :pull:`1289` - Added support for inline JavaScript as event handlers or other attributes that expect a callable via ``reactpy.types.InlineJavaScript``
- :pull:`1308` - Event functions can now call ``event.preventDefault()`` and ``event.stopPropagation()`` methods directly on the event data object, rather than using the ``@event`` decorator.
- :pull:`1308` - Event data now supports accessing properties via dot notation (ex. ``event.target.value``).
- :pull:`1308` - Added ``reactpy.types.Event`` to provide type hints for the standard ``data`` function argument (for example ``def on_click(event: Event): ...``).
- :pull:`1113` - Added ``asgi`` and ``jinja`` installation extras (for example ``pip install reactpy[asgi, jinja]``).
- :pull:`1113` - Added ``reactpy.executors.asgi.ReactPy`` that can be used to run ReactPy in standalone mode via ASGI.
- :pull:`1269` - Added ``reactpy.executors.asgi.ReactPyCsr`` that can be used to run ReactPy in standalone mode via ASGI, but rendered entirely client-sided.
- :pull:`1113` - Added ``reactpy.executors.asgi.ReactPyMiddleware`` that can be used to utilize ReactPy within any ASGI compatible framework.
- :pull:`1269` - 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 %}``.
- :pull:`1269` - Added ``reactpy.pyscript_component`` that can be used to embed ReactPy components into your existing application.
- :pull:`1264` - Added ``reactpy.use_async_effect`` hook.
- :pull:`1281` - Added ``reactpy.Vdom`` primitive interface for creating VDOM dictionaries.
- :pull:`1307` - Added ``reactpy.reactjs.component_from_file`` to import ReactJS components from a file.
- :pull:`1307` - Added ``reactpy.reactjs.component_from_url`` to import ReactJS components from a URL.
- :pull:`1307` - Added ``reactpy.reactjs.component_from_string`` to import ReactJS components from a string.
- :pull:`1314` - Added ``reactpy.reactjs.component_from_npm`` to import ReactJS components from NPM.
- :pull:`1314` - Added ``reactpy.h`` as a shorthand alias for ``reactpy.html``.
**Changed**
- :pull:`1314` - The ``key`` attribute is now stored within ``attributes`` in the VDOM spec.
- :pull:`1251` - Substitute client-side usage of ``react`` with ``preact``.
- :pull:`1239` - Script elements no longer support behaving like effects. They now strictly behave like plain HTML scripts.
- :pull:`1255` - 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()``.
- :pull:`1256` - Change ``set_state`` comparison method to check equality with ``==`` more consistently.
- :pull:`1257` - Add support for rendering ``@component`` children within ``vdom_to_html``.
- :pull:`1113` - Renamed the ``use_location`` hook's ``search`` attribute to ``query_string``.
- :pull:`1113` - Renamed the ``use_location`` hook's ``pathname`` attribute to ``path``.
- :pull:`1113` - Renamed ``reactpy.config.REACTPY_DEBUG_MODE`` to ``reactpy.config.REACTPY_DEBUG``.
- :pull:`1263` - 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.
- :pull:`1196` - Rewrite the ``event-to-object`` package to be more robust at handling properties on events.
- :pull:`1312` - Custom JS components will now automatically assume you are using ReactJS in the absence of a ``bind`` function.
- :pull:`1312` - Refactor layout rendering logic to improve readability and maintainability.
- :pull:`1113` - ``@reactpy/client`` now exports ``React`` and ``ReactDOM``.
- :pull:`1281` - ``reactpy.html`` will now automatically flatten lists recursively (ex. ``reactpy.html(["child1", ["child2"]])``)
- :pull:`1278` - ``reactpy.utils.reactpy_to_string`` will now retain the user's original casing for ``data-*`` and ``aria-*`` attributes.
- :pull:`1278` - ``reactpy.utils.string_to_reactpy`` has been upgraded to handle more complex scenarios without causing ReactJS rendering errors.
- :pull:`1281` - ``reactpy.core.vdom._CustomVdomDictConstructor`` has been moved to ``reactpy.types.CustomVdomConstructor``.
- :pull:`1281` - ``reactpy.core.vdom._EllipsisRepr`` has been moved to ``reactpy.types.EllipsisRepr``.
- :pull:`1281` - ``reactpy.types.VdomDictConstructor`` has been renamed to ``reactpy.types.VdomConstructor``.
- :pull:`1312` - ``REACTPY_ASYNC_RENDERING`` can now de-duplicate and cascade renders where necessary.
- :pull:`1312` - ``REACTPY_ASYNC_RENDERING`` is now defaulted to ``True`` for up to 40x performance improvements in environments with high concurrency.
**Deprecated**
-:pull:`1307` - ``reactpy.web.module_from_file`` is deprecated. Use ``reactpy.reactjs.component_from_file`` instead.
-:pull:`1307` - ``reactpy.web.module_from_url`` is deprecated. Use ``reactpy.reactjs.component_from_url`` instead.
-:pull:`1307` - ``reactpy.web.module_from_string`` is deprecated. Use ``reactpy.reactjs.component_from_string`` instead.
-:pull:`1307` - ``reactpy.web.export`` is deprecated. Use ``reactpy.reactjs.component_from_*`` instead.
-:pull:`1314` - ``reactpy.web.*`` is deprecated. Use ``reactpy.reactjs.*`` instead.
**Removed**
- :pull:`1113` - Removed support for Python 3.9 and 3.10.
- :pull:`1255` - Removed the ability to import ``reactpy.html.*`` elements directly. You must now call ``html.*`` to access the elements.
- :pull:`1113` - Removed backend specific installation extras (such as ``pip install reactpy[starlette]``).
- :pull:`1264` - Removed support for async functions within ``reactpy.use_effect`` hook. Use ``reactpy.use_async_effect`` instead.
- :pull:`1113` - Removed deprecated function ``module_from_template``.
- :pull:`1311` - Removed deprecated exception type ``reactpy.core.serve.Stop``.
- :pull:`1311` - Removed deprecated component ``reactpy.widgets.hotswap``.
- :pull:`1255` - Removed ``reactpy.sample`` module.
- :pull:`1255` - Removed ``reactpy.svg`` module. Contents previously within ``reactpy.svg.*`` can now be accessed via ``reactpy.html.svg.*``.
- :pull:`1255` - Removed ``reactpy.html._`` function. Use ``reactpy.html(...)`` or ``reactpy.html.fragment(...)`` instead.
- :pull:`1113` - Removed ``reactpy.run``. See the documentation for the new method to run ReactPy applications.
- :pull:`1113` - Removed ``reactpy.backend.*``. See the documentation for the new method to run ReactPy applications.
- :pull:`1113` - Removed ``reactpy.core.types`` module. Use ``reactpy.types`` instead.
- :pull:`1278` - Removed ``reactpy.utils.html_to_vdom``. Use ``reactpy.utils.string_to_reactpy`` instead.
- :pull:`1278` - Removed ``reactpy.utils.vdom_to_html``. Use ``reactpy.utils.reactpy_to_string`` instead.
- :pull:`1281` - Removed ``reactpy.vdom``. Use ``reactpy.Vdom`` instead.
- :pull:`1281` - Removed ``reactpy.core.make_vdom_constructor``. Use ``reactpy.Vdom`` instead.
- :pull:`1281` - Removed ``reactpy.core.custom_vdom_constructor``. Use ``reactpy.Vdom`` instead.
- :pull:`1311` - Removed ``reactpy.Layout`` top-level re-export. Use ``reactpy.core.layout.Layout`` instead.
- :pull:`1312` - Removed ``reactpy.types.LayoutType``. Use ``reactpy.types.BaseLayout`` instead.
- :pull:`1312` - Removed ``reactpy.types.ContextProviderType``. Use ``reactpy.types.ContextProvider`` instead.
- :pull:`1312` - Removed ``reactpy.core.hooks._ContextProvider``. Use ``reactpy.types.ContextProvider`` instead.
- :pull:`1314` - Removed ``reactpy.web.utils``. Use ``reactpy.reactjs.utils`` instead.
**Fixed**
- :pull:`1239` - Fixed a bug where script elements would not render to the DOM as plain text.
- :pull:`1271` - Fixed a bug where the ``key`` property provided within server-side ReactPy code was failing to propagate to the front-end JavaScript components.
- :pull:`1254` - Fixed a bug where ``RuntimeError("Hook stack is in an invalid state")`` errors could be generated when using a webserver that reuses threads.
- :pull:`1314` - Allow for ReactPy and ReactJS components to be arbitrarily inserted onto the page with any possible hierarchy.
v1.1.0
------
:octicon:`milestone` *released on 2024-11-24*
**Fixed**
- :pull:`1118` - ``module_from_template`` is broken with a recent release of ``requests``
- :pull:`1131` - ``module_from_template`` did not work when using Flask backend
- :pull:`1200` - Fixed ``UnicodeDecodeError`` when using ``reactpy.web.export``
- :pull:`1224` - Fixed needless unmounting of JavaScript components during each ReactPy render.
- :pull:`1126` - Fixed missing ``event["target"]["checked"]`` on checkbox inputs
- :pull:`1191` - Fixed missing static files on `sdist` Python distribution
**Added**
- :pull:`1165` - 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**
- :pull:`1171` - 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.
- :pull:`1210` - Move hooks from ``reactpy.backend.hooks`` into ``reactpy.core.hooks``.
**Deprecated**
- :pull:`1171` - 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.
- :pull:`1210` - Deprecate ``reactpy.backend.hooks`` since the hooks have been moved into
``reactpy.core.hooks``.
v1.0.2
------
:octicon:`milestone` *released on 2023-07-03*
**Fixed**
- :issue:`1086` - fix rendering bug when children change positions (via :pull:`1085`)
v1.0.1
------
:octicon:`milestone` *released on 2023-06-16*
**Changed**
- :pull:`1050` - Warn and attempt to fix missing mime types, which can result in ``reactpy.run`` not working as expected.
- :pull:`1051` - Rename ``reactpy.backend.BackendImplementation`` to ``reactpy.backend.BackendType``
- :pull:`1051` - Allow ``reactpy.run`` to fail in more predictable ways
**Fixed**
- :issue:`930` - better traceback for JSON serialization errors (via :pull:`1008`)
- :issue:`437` - explain that JS component attributes must be JSON (via :pull:`1008`)
- :pull:`1051` - Fix ``reactpy.run`` port assignment sometimes attaching to in-use ports on Windows
- :pull:`1051` - Fix ``reactpy.run`` not recognizing ``fastapi``
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
<https://github.com/psf/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.()``. 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 ``<head>`` 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 ``<html>``, ``<head>``, and ``<body>`` 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 ``<html>``, ``<head>``, and ``<body>`` 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/<file-path>``
- ``/_reactpy/stream/<path>``
- ``/_reactpy/modules/<file-path>``
- ``/<prefix>/<path>``
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 <https://github.com/reactive-python/reactpy-jupyter/issues/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 ``<script>`` or, if no attributes are given, operate similarly to an
effect. If no attributes are given, and when the script evaluates to a function, that
function will be called the first time it is mounted and any time the content of the
script is subsequently changed. If the function then returns another function, that
returned function will be called when the script is removed from the view, or just
before the content of the script changes.
**Closed Issues**
- State mismatch during component update - :issue:`629`
- Implement a script tag - :issue:`544`
**Pull Requests**
- make scripts behave more like normal html script element - :pull:`632`
- Fix state mismatch during component update - :pull:`631`
- implement script element - :pull:`617`
v0.35.4
-------
:octicon:`milestone` *released on 2022-01-27*
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.
**Closed Issues**
- Change Key of Parent Element Does Not Unmount Children - :issue:`622`
**Pull Requests**
- fix issue with key-based identity - :pull:`623`
v0.35.3
-------
:octicon:`milestone` *released on 2022-01-27*
As part of :pull:`614`, elements which changed type were not deeply unmounted. This
behavior is probably undesirable though since the state for children of the element
in question would persist (probably unexpectedly).
**Pull Requests**
- Always deeply unmount - :pull:`620`
v0.35.2
-------
:octicon:`milestone` *released on 2022-01-26*
This release includes several bug fixes. The most significant of which is the ability to
change the type of an element in the try (i.e. to and from being a component) without
getting an error. Originally the errors were introduced because it was though changing
element type would not be desirable. This was not the case though - swapping types
turns out to be quite common and useful.
**Closed Issues**
- Allow Children with the Same Key to Vary in Type - :issue:`613`
- Client Always Looks for Server at "/" - :issue:`611`
- Web modules get double file extensions with v0.35.x - :issue:`605`
**Pull Requests**
- allow elements with the same key to change type - :pull:`614`
- make connection to websocket relative path - :pull:`612`
- fix double file extension - :pull:`606`
v0.35.1
-------
:octicon:`milestone` *released on 2022-01-18*
Re-add accidentally deleted ``py.typed`` file to distribution. See `PEP-561
<https://www.python.org/dev/peps/pep-0561/#packaging-type-information>`__ for info on
this marker file.
v0.35.0
-------
:octicon:`milestone` *released on 2022-01-18*
The highlight of this release is that the default :ref:`"key" <Organizing Items With
Keys>` of all elements will be their index amongst their neighbors. Previously this
behavior could be engaged by setting ``REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY=1`` when
running ReactPy. In this release though, you will need to explicitly turn off this feature
(i.e. ``=0``) to return to the old behavior. With this change, some may notice
additional error logs which warn that:
.. code-block:: text
Key not specified for child in list ...
This is saying is that an element or component which was created in a list does not have
a unique ``key``. For more information on how to mitigate this warning refer to the docs
on :ref:`Organizing Items With Keys`.
**Closed Issues**
- Support Starlette Server - :issue:`588`
- Fix unhandled case in module_from_template - :issue:`584`
- Hide "Children" within REACTPY_DEBUG_MODE key warnings - :issue:`562`
- Bug in Element Key Identity - :issue:`556`
- Add iFrame to reactpy.html - :issue:`542`
- Create a use_linked_inputs widget instead of Input - :issue:`475`
- React warning from module_from_template - :issue:`440`
- Use Index as Default Key - :issue:`351`
**Pull Requests**
- add ``use_linked_inputs`` - :pull:`593`
- add starlette server implementation - :pull:`590`
- Log on web module replacement instead of error - :pull:`586`
- Make Index Default Key - :pull:`579`
- reduce log spam from missing keys in children - :pull:`564`
- fix bug in element key identity - :pull:`563`
- add more standard html elements - :pull:`554`
v0.34.0
-------
:octicon:`milestone` *released on 2021-12-16*
This release contains a variety of minor fixes and improvements which came out of
rewriting the documentation. The most significant of these changes is the remove of
target element attributes from the top-level of event data dictionaries. For example,
instead of being able to find the value of an input at ``event["value"]`` it will
instead be found at ``event["target"]["value"]``. For a short period we will issue a
:class:`DeprecationWarning` when target attributes are requested at the top-level of the
event dictionary. As part of this change we also add ``event["currentTarget"]`` and
``event["relatedTarget"]`` keys to the event dictionary as well as a
``event[some_target]["boundingClientRect"]`` where ``some_target`` may be ``"target"``,
``"currentTarget"`` or ``"relatedTarget"``.
**Closed Issues**
- Move target attributes to ``event['target']`` - :issue:`548`
**Pull Requests**
- Correctly Handle Target Event Data - :pull:`550`
- Clean up WS console logging - :pull:`522`
- automatically infer closure arguments - :pull:`520`
- Documentation Rewrite - :pull:`519`
- add option to replace existing when creating a module - :pull:`516`
v0.33.3
-------
:octicon:`milestone` *released on 2021-10-08*
Contains a small number of bug fixes and improvements. The most significant change is
the addition of a warning stating that `REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY=1` will become
the default in a future release. Beyond that, a lesser improvement makes it possible to
use the default export from a Javascript module when calling `module_from_template` by
specifying `exports_default=True` as a parameter. A
**Closed Issues**
- Memory leak in SharedClientStateServer - :issue:`511`
- Cannot use default export in react template - :issue:`502`
- Add warning that element index will be used as the default key in a future release - :issue:`428`
**Pull Requests**
- warn that REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY=1 will be the default - :pull:`515`
- clean up patch queues after exit - :pull:`514`
- Remove Reconnecting WS alert - :pull:`513`
- Fix 502 - :pull:`503`
v0.33.2
-------
:octicon:`milestone` *released on 2021-09-05*
A release to fix a memory leak caused by event handlers that were not being removed
when components updated.
**Closed Issues**
- Non-root component event handlers cause memory leaks - :issue:`510`
v0.33.1
-------
:octicon:`milestone` *released on 2021-09-02*
A hot fix for a regression introduced in ``0.33.0`` where the root element of the layout
could not be updated. See :issue:`498` for more info. A regression test for this will
be introduced in a future release.
**Pull Requests**
- Fix 498 pt1 - :pull:`501`
v0.33.0
-------
:octicon:`milestone` *released on 2021-09-02*
The most significant fix in this release is for a regression which manifested in
:issue:`480`, :issue:`489`, and :issue:`451` which resulted from an issue in the way
JSON patches were being applied client-side. This was ultimately resolved by
:pull:`490`. While it's difficult to test this without a more thorough Javascript
suite, we added a test that should hopefully catch this in the future by proxy.
The most important breaking change, is yet another which modifies the Custom Javascript
Component interface. We now add a ``create()`` function to the ``bind()`` interface that
allows ReactPy's client to recursively create components from that (and only that) import
source. Prior to this, the interface was given unrendered models for child elements. The
imported module was then responsible for rendering them. This placed a large burden on
the author to understand how to handle these unrendered child models. In addition, in
the React template used by ``module_from_template`` we needed to import a version of
``@reactpy/client`` from the CDN - this had already caused some issues where the
template required a version of ``@reactpy/client`` in the which had not been released
yet.
**Closed Issues**
- Client-side error in mount-01d35dc3.js - :issue:`489`
- Style Cannot Be Updated - :issue:`480`
- Displaying error messages in the client via `__error__` tag can leak secrets - :issue:`454`
- Examples broken in docs - :issue:`451`
- Rework docs landing page - :issue:`446`
- eventHandlers should be a mapping of generic callables - :issue:`423`
- Allow customization of built-in ReactPy client - :issue:`253`
**Pull Requests**
- move VdomDict and VdomJson to proto - :pull:`492`
- only send error info in debug mode - :pull:`491`
- correctly apply client-side JSON patch - :pull:`490`
- add script to set version of all packages in ReactPy - :pull:`483`
- Pass import source to bind - :pull:`482`
- Do not mutate client-side model - :pull:`481`
- assume import source children come from same source - :pull:`479`
- make an EventHandlerType protocol - :pull:`476`
- Update issue form - :pull:`471`
v0.32.0
-------
:octicon:`milestone` *released on 2021-08-20*
In addition to a variety of bug fixes and other minor improvements, there's a breaking
change to the custom component interface - instead of exporting multiple functions that
render custom components, we simply expect a single ``bind()`` function.
binding function then must return an object with a ``render()`` and ``unmount()``
function. This change was made in order to better support the rendering of child models.
See :ref:`Custom JavaScript Components` for details on the new interface.
**Closed Issues**
- Docs broken on Firefox - :issue:`469`
- URL resolution for web modules does not consider urls starting with / - :issue:`460`
- Query params in package name for module_from_template not stripped - :issue:`455`
- Make docs section margins larger - :issue:`450`
- Search broken in docs - :issue:`443`
- Move src/reactpy/client out of Python package - :issue:`429`
- Use composition instead of classes async with Layout and LifeCycleHook - :issue:`412`
- Remove Python language extension - :issue:`282`
- Add keys to models so React doesn't complain of child arrays requiring them -
:issue:`255`
- Fix binder link in docs - :issue:`231`
**Pull Requests**
- Update issue form - :pull:`471`
- improve heading legibility - :pull:`470`
- fix search in docs by upgrading sphinx - :pull:`462`
- rework custom component interface with bind() func - :pull:`458`
- parse package as url path in module_from_template - :pull:`456`
- add file extensions to import - :pull:`439`
- fix key warnings - :pull:`438`
- fix #429 - move client JS to top of src/ dir - :pull:`430`
v0.31.0
-------
:octicon:`milestone` *released on 2021-07-14*
The :class:`~reactpy.core.layout.Layout` is now a prototype, and ``Layout.update`` is no
longer a public API. This is combined with a much more significant refactor of the
underlying rendering logic.
The biggest issue that has been resolved relates to the relationship between
:class:`~reactpy.core.hooks.LifeCycleHook` and ``Layout``. Previously, the
``LifeCycleHook`` accepted a layout instance in its constructor and called
``Layout.update``. Additionally, the ``Layout`` would manipulate the
``LifeCycleHook.component`` attribute whenever the component instance changed after a
render. The former behavior leads to a non-linear code path that's a touch to follow.
The latter behavior is the most egregious design issue since there's absolutely no local
indication that the component instance can be swapped out (not even a comment).
The new refactor no longer binds component or layout instances to a ``LifeCycleHook``.
Instead, the hook simply receives an un-parametrized callback that can be triggered to
schedule a render. While some error logs lose clarity (since we can't say what component
caused them). This change precludes a need for the layout to ever mutate the hook.
To accommodate this change, the internal representation of the layout's state had to
change. Previously, a class-based approach was take, where methods of the state-holding
classes were meant to handle all use cases. Now we rely much more heavily on very simple
(and mostly static) data structures that have purpose built constructor functions that
much more narrowly address each use case.
After these refactors, ``ComponentTypes`` no longer needs a unique ``id`` attribute.
Instead, a unique ID is generated internally which is associated with the
``LifeCycleState``, not component instances since they are inherently transient.
**Pull Requests**
- fix #419 and #412 - :pull:`422`
v0.30.1
-------
:octicon:`milestone` *released on 2021-07-13*
Removes the usage of the :func:`id` function for generating unique ideas because there
were situations where the IDs bound to the lifetime of an object are problematic. Also
adds a warning :class:`Deprecation` warning to render functions that include the
parameter ``key``. It's been decided that allowing ``key`` to be used in this way can
lead to confusing bugs.
**Pull Requests**
- warn if key is param of component render function - :pull:`421`
- fix :issue:`417` and :issue:`413` - :pull:`418`
- add changelog entry for :ref:`v0.30.0` - :pull:`415`
v0.30.0
-------
:octicon:`milestone` *released on 2021-06-28*
With recent changes to the custom component interface, it's now possible to remove all
runtime reliance on NPM. Doing so has many virtuous knock-on effects:
1. Removal of large chunks of code
2. Greatly simplifies how users dynamically experiment with React component libraries,
because their usage no longer requires a build step. Instead they can be loaded in
the browser from a CDN that distributes ESM modules.
3. The built-in client code needs to make fewer assumption about where static resources
are located, and as a result, it's also easier to coordinate the server and client
code.
4. Alternate client implementations benefit from this simplicity. Now, it's possible to
install @reactpy/client normally and write a ``loadImportSource()`` function that
looks for route serving the contents of `REACTPY_WEB_MODULES_DIR.`
This change includes large breaking changes:
- The CLI is being removed as it won't be needed any longer
- The `reactpy.client` is being removed in favor of a stripped down ``reactpy.web`` module
- The `REACTPY_CLIENT_BUILD_DIR` config option will no longer exist and a new
``REACTPY_WEB_MODULES_DIR`` which only contains dynamically linked web modules. While
this new directory's location is configurable, it is meant to be transient and should
not be re-used across sessions.
The new ``reactpy.web`` module takes a simpler approach to constructing import sources and
expands upon the logic for resolving imports by allowing exports from URLs to be
discovered too. Now, that ReactPy isn't using NPM to dynamically install component
libraries ``reactpy.web`` instead creates JS modules from template files and links them
into ``REACTPY_WEB_MODULES_DIR``. These templates ultimately direct the browser to load the
desired library from a CDN.
**Pull Requests**
- Add changelog entry for 0.30.0 - :pull:`415`
- Fix typo in index.rst - :pull:`411`
- Add event handlers docs - :pull:`410`
- Misc doc improvements - :pull:`409`
- Port first ReactPy article to docs - :pull:`408`
- Test build in CI - :pull:`404`
- Remove all runtime reliance on NPM - :pull:`398`
v0.29.0
-------
:octicon:`milestone` *released on 2021-06-20*
Contains breaking changes, the most significant of which are:
- Moves the runtime client build directory to a "user data" directory rather a directory
where ReactPy's code was installed. This has the advantage of not requiring write
permissions to rebuild the client if ReactPy was installed globally rather than in a
virtual environment.
- The custom JS component interface has been reworked to expose an API similar to
the ``createElement``, ``render``, ``unmountComponentAtNode`` functions from React.
**Issues Fixed:**
- :issue:`375`
- :issue:`394`
- :issue:`401`
**Highlighted Commits:**
- add try/except around event handling - :commit:`f2bf589`
- do not call find_builtin_server_type at import time - :commit:`e29745e`
- import default from react/reactDOM/fast-json-patch - :commit:`74c8a34`
- no named exports for react/reactDOM - :commit:`f13bf35`
- debug logs for runtime build dir create/update - :commit:`af94f4e`
- put runtime build in user data dir - :commit:`0af69d2`
- change shared to update_on_change - :commit:`6c09a86`
- rework js module interface + fix docs - :commit:`699cc66`
- correctly serialize File object - :commit:`a2398dc`
v0.28.0
-------
:octicon:`milestone` *released on 2021-06-01*
Includes a wide variety of improvements:
- support ``currentTime`` attr of audio/video elements
- support for the ``files`` attribute from the target of input elements
- model children are passed to the Javascript ``mount()`` function
- began to add tests to client-side javascript
- add a ``mountLayoutWithWebSocket`` function to ``@reactpy/client``
and breaking changes, the most significant of which are:
- Refactor existing server implementations as functions adhering to a protocol. This
greatly simplified much of the code responsible for setting up servers and avoids
the use of inheritance.
- Switch to a monorepo-style structure for Javascript enabling a greater separation of
concerns and common workspace scripts in ``package.json``.
- Use a ``loadImportSource()`` function instead of trying to infer the path to dynamic
modules which was brittle and inflexible. Allowing the specific client implementation
to discover where "import sources" are located means ``@reactpy/client`` doesn't
need to try and devise a solution that will work for all cases. The fallout from this
change is the addition of `importSource.sourceType` which, for the moment can either
be ``"NAME"`` or ``"URL"`` where the former indicates the client is expected to know
where to find a module of that name, and the latter should (usually) be passed on to
``import()``
**Issues Fixed:**
- :issue:`324` (partially resolved)
- :issue:`375`
**Highlighted Commits:**
- xfail due to bug in Python - :commit:`fee49a7`
- add importSource sourceType field - :commit:`795bf94`
- refactor client to use loadImportSource param - :commit:`bb5e3f3`
- turn app into a package - :commit:`b282fc2`
- add debug logs - :commit:`4b4f9b7`
- add basic docs about JS test suite - :commit:`9ecfde5`
- only use nox for python tests - :commit:`5056b7b`
- test event serialization - :commit:`05fd86c`
- serialize files attribute of file input element - :commit:`f0d00b7`
- rename hasMount to exportsMount - :commit:`d55a28f`
- refactor flask - :commit:`94681b6`
- refactor tornado + misc fixes to sanic/fastapi - :commit:`16c9209`
- refactor fastapi using server protocol - :commit:`0cc03ba`
- refactor sanic server - :commit:`43d4b4f`
- use server protocol instead of inheritance - :commit:`abe0fde`
- support currentTime attr of audio/video elements - :commit:`975b54a`
- pass children as props to mount() - :commit:`9494bc0`
v0.27.0
-------
:octicon:`milestone` *released on 2021-05-14*
Introduces changes to the interface for custom Javascript components. This now allows
JS modules to export a ``mount(element, component, props)`` function which can be used
to bind new elements to the DOM instead of using the application's own React instance
and specifying React as a peer dependency. This avoids a wide variety of potential
issues with implementing custom components and opens up the possibility for a wider
variety of component implementations.
**Highlighted Commits:**
- modules with mount func should not have children - :commit:`94d006c`
- limit to flask<2.0 - :commit:`e7c11d0`
- federate modules with mount function - :commit:`bf63a62`
v0.26.0
-------
:octicon:`milestone` *released on 2021-05-07*
A collection of minor fixes and changes that, as a whole, add up to something requiring
a minor release. The most significant addition is a fix for situations where a
``Layout`` can raise an error when a component whose state has been delete is rendered.
This occurs when element has been unmounted, but a latent event tells the layout it
should be updated. For example, when a user clicks a button rapidly, and the resulting
update deletes the original button.
**Highlighted Commits:**
- only one attr dict in vdom constructor - :commit:`555086a`
- remove Option setter/getter with current property - :commit:`2627f79`
- add cli command to show options - :commit:`c9e6869`
- check component has model state before render - :commit:`6a50d56`
- rename daemon to run_in_thread + misc - :commit:`417b687`
v0.25.0
-------
:octicon:`milestone` *released on 2021-04-30*
Completely refactors layout dispatcher by switching from a class-based approach to one
that leverages pure functions. While the logic itself isn't any simpler, it was easier
to implement, and now hopefully understand, correctly. This conversion was motivated by
several bugs that had cropped up related to improper usage of ``anyio``.
**Issues Fixed:**
- :issue:`330`
- :issue:`298`
**Highlighted Commits:**
- improve docs + simplify multi-view - :commit:`4129b60`
- require anyio>=3.0 - :commit:`24aed28`
- refactor dispatchers - :commit:`ce8e060`
v0.24.0
-------
:octicon:`milestone` *released on 2021-04-18*
This release contains an update that allows components and elements to have "identity".
That is, their state can be preserved across updates. Before this point, only the state
for the component at the root of an update was preserved. Now though, the state for any
component and element with a ``key`` that is unique amongst its siblings, will be
preserved so long as this is also true for parent elements/components within the scope
of the current update. Thus, only when the key of the element or component changes will
its state do the same.
In a future update, the default key for all elements and components will be its index
with respect to its siblings in the layout. The
:attr:`~reactpy.config.REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY` feature flag has been introduced
to allow users to enable this behavior early.
**Highlighted Commits:**
- add feature flag for default key behavior - :commit:`42ee01c`
- use unique object instead of index as default key - :commit:`5727ab4`
- make HookCatcher/StaticEventHandlers testing utils - :commit:`1abfd76`
- add element and component identity - :commit:`5548f02`
- minor doc updates - :commit:`e5511d9`
- add tests for callback identity preservation with keys - :commit:`72e03ec`
- add 'key' to VDOM spec - :commit:`c3236fe`
- Rename validate_serialized_vdom to validate_vdom_json - :commit:`d04faf9`
- EventHandler should not serialize itself - :commit:`f7a59f2`
- fix docs typos - :commit:`42b2e20`
- fixes: #331 - add roadmap to docs - :commit:`4226c12`
v0.23.1
-------
:octicon:`milestone` *released on 2021-04-02*
**Highlighted Commits:**
- fix non-deterministic return order in install() - :commit:`494d5c2`
v0.23.0
-------
:octicon:`milestone` *released on 2021-04-01*
**Highlighted Commits:**
- add changelog to docs - :commit:`9cbfe94`
- automatically reconnect to server - :commit:`3477e2b`
- allow no reconnect in client - :commit:`ef263c2`
- cleaner way to specify import sources - :commit:`ea19a07`
- add the reactpy-react-client back into the main repo - :commit:`5dcc3bb`
- implement fastapi render server - :commit:`94e0620`
- improve docstring for REACTPY_CLIENT_BUILD_DIR - :commit:`962d885`
- cli improvements - :commit:`788fd86`
- rename SERIALIZED_VDOM_JSON_SCHEMA to VDOM_JSON_SCHEMA - :commit:`74ad578`
- better logging for modules - :commit:`39565b9`
- move client utils into private module - :commit:`f825e96`
- redirect BUILD_DIR imports to REACTPY_CLIENT_BUILD_DIR option - :commit:`53fb23b`
- upgrade snowpack - :commit:`5697a2d`
- better logs for reactpy.run + flask server - :commit:`2b34e3d`
- move package to src dir - :commit:`066c9c5`
- reactpy restore uses backup - :commit:`773f78e`
================================================
FILE: docs/source/about/contributor-guide.rst
================================================
Contributor Guide
=================
Creating a development environment
----------------------------------
If you plan to make code changes to this repository, you will need to install the following dependencies first:
- `Git <https://www.python.org/downloads/>`__
- `Python 3.9+ <https://www.python.org/downloads/>`__
- `Hatch <https://hatch.pypa.io/latest/>`__
- `Bun <https://bun.sh/>`__
- `Docker <https://docs.docker.com/get-docker/>`__ (optional)
Once you finish installing these dependencies, you can clone this repository:
.. code-block:: bash
git clone https://github.com/reactive-python/reactpy.git
cd reactpy
Executing test environment commands
-----------------------------------
By utilizing ``hatch``, the following commands are available to manage the development environment.
Python Tests
............
.. list-table::
:header-rows: 1
* - Command
- Description
* - ``hatch test``
- Run Python tests using the current environment's Python version
* - ``hatch test --all``
- Run tests using all compatible Python versions
* - ``hatch test --python 3.9``
- Run tests using a specific Python version
* - ``hatch test -k test_use_connection``
- Run only a specific test
Python Package
..............
.. list-table::
:header-rows: 1
* - Command
- Description
* - ``hatch fmt``
- Run all linters and formatters
* - ``hatch fmt --check``
- Run all linters and formatters, but do not save fixes to the disk
* - ``hatch fmt --linter``
- Run only linters
* - ``hatch fmt --formatter``
- Run only formatters
* - ``hatch run python:type_check``
- Run the Python type checker
JavaScript Packages
...................
.. list-table::
:header-rows: 1
* - Command
- Description
* - ``hatch run javascript:check``
- Run the JavaScript linter/formatter
* - ``hatch run javascript:fix``
- Run the JavaScript linter/formatter and write fixes to disk
* - ``hatch run javascript:test``
- Run the JavaScript tests
* - ``hatch run javascript:build``
- Build all JavaScript packages
* - ``hatch run javascript:build_event_to_object``
- Build the ``event-to-object`` package
* - ``hatch run javascript:build_client``
- Build the ``@reactpy/client`` package
* - ``hatch run javascript:build_app``
- Build the ``@reactpy/app`` package
Documentation
.............
.. list-table::
:header-rows: 1
* - Command
- Description
* - ``hatch run docs:serve``
- Start the documentation preview webserver
* - ``hatch run docs:build``
- Build the documentation
* - ``hatch run docs:check``
- Check the documentation for build errors
* - ``hatch run docs:docker_serve``
- Start the documentation preview webserver using Docker
* - ``hatch run docs:docker_build``
- Build the documentation using Docker
Environment Management
......................
.. list-table::
:header-rows: 1
* - Command
- Description
* - ``hatch build --clean``
- Build the package from source
* - ``hatch env prune``
- Delete all virtual environments created by ``hatch``
* - ``hatch python install 3.12``
- Install a specific Python version to your system
Other ReactPy Repositories
--------------------------
ReactPy has several external packages that can be installed to enhance your user experience. For documentation on them
you should refer to their respective documentation in the links below:
- `reactpy-router <https://github.com/reactive-python/reactpy-router>`__ - ReactPy support for URL
routing
- `reactpy-js-component-template
<https://github.com/reactive-python/reactpy-js-component-template>`__ - Template repo
for making :ref:`Custom Javascript Components`.
- `reactpy-django <https://github.com/reactive-python/reactpy-django>`__ - ReactPy integration for
Django
- `reactpy-jupyter <https://github.com/reactive-python/reactpy-jupyter>`__ - ReactPy integration for
Jupyter
.. Links
.. =====
.. _Hatch: https://hatch.pypa.io/
.. _Invoke: https://www.pyinvoke.org/
.. _Google Chrome: https://www.google.com/chrome/
.. _Docker: https://docs.docker.com/get-docker/
.. _Git: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
.. _Git Bash: https://gitforwindows.org/
.. _NPM: https://www.npmjs.com/get-npm
.. _PyPI: https://pypi.org/project/reactpy
.. _pip: https://pypi.org/project/pip/
.. _PyTest: pytest <https://docs.pytest.org
.. _Playwright: https://playwright.dev/python/
.. _React: https://reactjs.org/
.. _Heroku: https://www.heroku.com/what
.. _GitHub Actions: https://github.com/features/actions
.. _pre-commit: https://pre-commit.com/
.. _GitHub Flow: https://guides.github.com/introduction/flow/
.. _MyPy: http://mypy-lang.org/
.. _Black: https://github.com/psf/black
.. _Flake8: https://flake8.pycqa.org/en/latest/
.. _Ruff: https://github.com/charliermarsh/ruff
.. _UVU: https://github.com/lukeed/uvu
.. _Prettier: https://prettier.io/
.. _ESLint: https://eslint.org/
================================================
FILE: docs/source/about/credits-and-licenses.rst
================================================
Credits and Licenses
====================
Much of this documentation, including its layout and content, was created with heavy
influence from https://reactjs.org which uses the `Creative Commons Attribution 4.0
International
<https://raw.githubusercontent.com/reactjs/reactjs.org/b2d5613b6ae20855ced7c83067b604034bebbb44/LICENSE-DOCS.md>`__
license. While many things have been transformed, we paraphrase and, in some places,
copy language or examples where ReactPy's behavior mirrors that of React's.
Source Code License
-------------------
.. literalinclude:: ../../../LICENSE
:language: text
================================================
FILE: docs/source/conf.py
================================================
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
import sys
from doctest import DONT_ACCEPT_TRUE_FOR_1, ELLIPSIS, NORMALIZE_WHITESPACE
from pathlib import Path
# -- Path Setup --------------------------------------------------------------
THIS_DIR = Path(__file__).parent
ROOT_DIR = THIS_DIR.parent.parent
DOCS_DIR = THIS_DIR.parent
# extension path
sys.path.insert(0, str(DOCS_DIR))
sys.path.insert(0, str(THIS_DIR / "_exts"))
# -- Project information -----------------------------------------------------
project = "ReactPy"
title = "ReactPy"
description = (
"ReactPy is a Python web framework for building interactive websites without needing "
"a single line of Javascript. It can be run standalone, in a Jupyter Notebook, or "
"as part of an existing application."
)
copyright = "2023, Ryan Morshead" # noqa: A001
author = "Ryan Morshead"
# -- Common External Links ---------------------------------------------------
extlinks = {
"issue": (
"https://github.com/reactive-python/reactpy/issues/%s",
"#%s",
),
"pull": (
"https://github.com/reactive-python/reactpy/pull/%s",
"#%s",
),
"discussion": (
"https://github.com/reactive-python/reactpy/discussions/%s",
"#%s",
),
"discussion-type": (
"https://github.com/reactive-python/reactpy/discussions/categories/%s",
"%s",
),
"commit": (
"https://github.com/reactive-python/reactpy/commit/%s",
"%s",
),
}
extlinks_detect_hardcoded_links = True
# -- General configuration ---------------------------------------------------
# If your documentatirston needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"sphinx.ext.extlinks",
# third party extensions
"sphinx_copybutton",
"sphinx_reredirects",
"sphinx_design",
"sphinxext.opengraph",
# custom extensions
"async_doctest",
"autogen_api_docs",
"copy_vdom_json_schema",
"reactpy_view",
"patched_html_translator",
"reactpy_example",
"build_custom_js",
"custom_autosectionlabel",
]
# Add any paths that contain templates here, relative to this directory.
# templates_path = ["templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ".rst"
# The master toctree document.
master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [
"_custom_js",
]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
# The default language to highlight source code in.
highlight_language = "python3"
# Controls how sphinx.ext.autodoc represents typehints in the function signature
autodoc_typehints = "description"
# -- Doc Test Configuration -------------------------------------------------------
doctest_default_flags = NORMALIZE_WHITESPACE | ELLIPSIS | DONT_ACCEPT_TRUE_FOR_1
# -- Extension Configuration ------------------------------------------------------
# -- sphinx.ext.autosectionlabel ---
autosectionlabel_skip_docs = ["_auto/apis"]
# -- sphinx.ext.autodoc --
# show base classes for autodoc
autodoc_default_options = {
"show-inheritance": True,
"member-order": "bysource",
}
# order autodoc members by their order in the source
autodoc_member_order = "bysource"
# -- sphinx_reredirects --
redirects = {
"package-api": "_autogen/user-apis.html",
"configuration-options": "_autogen/dev-apis.html#configuration-options",
"examples": "creating-interfaces/index.html",
}
# -- sphinxext.opengraph --
ogp_site_url = "https://reactpy.dev/"
ogp_image = "https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/png/reactpy-logo-landscape-padded.png"
# We manually specify this below
# ogp_description_length = 200
ogp_type = "website"
ogp_custom_meta_tags = [
# Open Graph Meta Tags
f'<meta property="og:title" content="{title}">',
f'<meta property="og:description" content="{description}">',
# Twitter Meta Tags
'<meta name="twitter:card" content="summary_large_image">',
'<meta name="twitter:creator" content="@rmorshea">',
'<meta name="twitter:site" content="@rmorshea">',
]
# -- Options for HTML output -------------------------------------------------
# Set the page title
html_title = title
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "furo"
html_logo = str(ROOT_DIR / "branding" / "svg" / "reactpy-logo-landscape.svg")
html_favicon = str(ROOT_DIR / "branding" / "ico" / "reactpy-logo.ico")
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
"light_css_variables": {
# furo
"admonition-title-font-size": "1rem",
"admonition-font-size": "1rem",
# sphinx-design
"sd-color-info": "var(--color-admonition-title-background--note)",
"sd-color-warning": "var(--color-admonition-title-background--warning)",
"sd-color-danger": "var(--color-admonition-title-background--danger)",
"sd-color-info-text": "var(--color-admonition-title--note)",
"sd-color-warning-text": "var(--color-admonition-title--warning)",
"sd-color-danger-text": "var(--color-admonition-title--danger)",
},
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# These paths are either relative to html_static_path
# or fully qualified paths (eg. https://...)
css_dir = THIS_DIR / "_static" / "css"
html_css_files = [
str(p.relative_to(THIS_DIR / "_static")) for p in css_dir.glob("*.css")
]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for Sphinx Panels -----------------------------------------------
panels_css_variables = {
"tabs-color-label-active": "rgb(106, 176, 221)",
"tabs-color-label-inactive": "rgb(201, 225, 250)",
"tabs-color-overline": "rgb(201, 225, 250)",
"tabs-color-underline": "rgb(201, 225, 250)",
}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "ReactPydoc"
# -- Options for LaTeX output ------------------------------------------------
# latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
# }
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [(master_doc, "ReactPy.tex", html_title, "Ryan Morshead", "manual")]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "reactpy", html_title, [author], 1)]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"ReactPy",
html_title,
author,
"ReactPy",
"One line description of project.",
"Miscellaneous",
)
]
# -- Options for Sphinx-Autodoc-Typehints output -------------------------------------------------
set_type_checking_flag = False
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ["search.html"]
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"pyalect": ("https://pyalect.readthedocs.io/en/latest", None),
"sanic": ("https://sanic.readthedocs.io/en/latest/", None),
"tornado": ("https://www.tornadoweb.org/en/stable/", None),
"flask": ("https://flask.palletsprojects.com/en/1.1.x/", None),
}
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json
================================================
[
{
"name": "Homenaje a la Neurocirugía",
"artist": "Marta Colvin Andrade",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg/1024px-Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg",
"alt": "A bronze statue of two crossed hands delicately holding a human brain in their fingertips."
},
{
"name": "Eternal Presence",
"artist": "John Woodrow Wilson",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/6/6f/Chicago%2C_Illinois_Eternal_Silence1_crop.jpg",
"alt": "The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity."
},
{
"name": "Moai",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/5/50/AhuTongariki.JPG",
"alt": "Three monumental stone busts with the heads that are disproportionately large with somber faces."
},
{
"name": "Blue Nana",
"artist": "Niki de Saint Phalle",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Blue_Nana_-_panoramio.jpg/1024px-Blue_Nana_-_panoramio.jpg",
"alt": "A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy."
},
{
"name": "Cavaliere",
"artist": "Lamidi Olonade Fakeye",
"description": "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.",
"url": "https://upload.wikimedia.org/wikipedia/commons/3/34/Nigeria%2C_lamidi_olonade_fakeye%2C_cavaliere%2C_1992.jpg",
"alt": "An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns."
},
{
"name": "Big Bellies",
"artist": "Alina Szapocznikow",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/KMM_Szapocznikow.JPG/200px-KMM_Szapocznikow.JPG",
"alt": "The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures."
},
{
"name": "Terracotta Army",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg",
"alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor."
},
{
"name": "Lunar Landscape",
"artist": "Louise Nevelson",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/1999-3-A--J_s.jpg/220px-1999-3-A--J_s.jpg",
"alt": "A black matte sculpture where the individual elements are initially indistinguishable."
},
{
"name": "Aureole",
"artist": "Ranjani Shettar",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Shettar-_5854-sm_%285132866765%29.jpg/399px-Shettar-_5854-sm_%285132866765%29.jpg",
"alt": "A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light."
},
{
"name": "Hippos",
"artist": "Taipei Zoo",
"description": "The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Hippo_sculpture_Taipei_Zoo_20543.jpg/250px-Hippo_sculpture_Taipei_Zoo_20543.jpg",
"alt": "A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming."
}
]
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py
================================================
import json
from pathlib import Path
from reactpy import component, hooks, html, run
HERE = Path(__file__)
DATA_PATH = HERE.parent / "data.json"
sculpture_data = json.loads(DATA_PATH.read_text())
@component
def Gallery():
index, set_index = hooks.use_state(0)
def handle_click(event):
set_index(index + 1)
bounded_index = index % len(sculpture_data)
sculpture = sculpture_data[bounded_index]
alt = sculpture["alt"]
artist = sculpture["artist"]
description = sculpture["description"]
name = sculpture["name"]
url = sculpture["url"]
return html.div(
html.button({"on_click": handle_click}, "Next"),
html.h2(name, " by ", artist),
html.p(f"({bounded_index + 1} of {len(sculpture_data)})"),
html.img({"src": url, "alt": alt, "style": {"height": "200px"}}),
html.p(description),
)
run(Gallery)
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/data.json
================================================
[
{
"name": "Homenaje a la Neurocirugía",
"artist": "Marta Colvin Andrade",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg/1024px-Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg",
"alt": "A bronze statue of two crossed hands delicately holding a human brain in their fingertips."
},
{
"name": "Eternal Presence",
"artist": "John Woodrow Wilson",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/6/6f/Chicago%2C_Illinois_Eternal_Silence1_crop.jpg",
"alt": "The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity."
},
{
"name": "Moai",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/5/50/AhuTongariki.JPG",
"alt": "Three monumental stone busts with the heads that are disproportionately large with somber faces."
},
{
"name": "Blue Nana",
"artist": "Niki de Saint Phalle",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Blue_Nana_-_panoramio.jpg/1024px-Blue_Nana_-_panoramio.jpg",
"alt": "A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy."
},
{
"name": "Cavaliere",
"artist": "Lamidi Olonade Fakeye",
"description": "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.",
"url": "https://upload.wikimedia.org/wikipedia/commons/3/34/Nigeria%2C_lamidi_olonade_fakeye%2C_cavaliere%2C_1992.jpg",
"alt": "An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns."
},
{
"name": "Big Bellies",
"artist": "Alina Szapocznikow",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/KMM_Szapocznikow.JPG/200px-KMM_Szapocznikow.JPG",
"alt": "The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures."
},
{
"name": "Terracotta Army",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg",
"alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor."
},
{
"name": "Lunar Landscape",
"artist": "Louise Nevelson",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/1999-3-A--J_s.jpg/220px-1999-3-A--J_s.jpg",
"alt": "A black matte sculpture where the individual elements are initially indistinguishable."
},
{
"name": "Aureole",
"artist": "Ranjani Shettar",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Shettar-_5854-sm_%285132866765%29.jpg/399px-Shettar-_5854-sm_%285132866765%29.jpg",
"alt": "A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light."
},
{
"name": "Hippos",
"artist": "Taipei Zoo",
"description": "The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Hippo_sculpture_Taipei_Zoo_20543.jpg/250px-Hippo_sculpture_Taipei_Zoo_20543.jpg",
"alt": "A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming."
}
]
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py
================================================
import json
from pathlib import Path
from reactpy import component, hooks, html, run
HERE = Path(__file__)
DATA_PATH = HERE.parent / "data.json"
sculpture_data = json.loads(DATA_PATH.read_text())
@component
def Gallery():
index, set_index = hooks.use_state(0)
show_more, set_show_more = hooks.use_state(False)
def handle_next_click(event):
set_index(index + 1)
def handle_more_click(event):
set_show_more(not show_more)
bounded_index = index % len(sculpture_data)
sculpture = sculpture_data[bounded_index]
alt = sculpture["alt"]
artist = sculpture["artist"]
description = sculpture["description"]
name = sculpture["name"]
url = sculpture["url"]
return html.div(
html.button({"on_click": handle_next_click}, "Next"),
html.h2(name, " by ", artist),
html.p(f"({bounded_index + 1} or {len(sculpture_data)})"),
html.img({"src": url, "alt": alt, "style": {"height": "200px"}}),
html.div(
html.button(
{"on_click": handle_more_click},
f"{('Show' if show_more else 'Hide')} details",
),
(html.p(description) if show_more else ""),
),
)
@component
def App():
return html.div(
html.section({"style": {"width": "50%", "float": "left"}}, Gallery()),
html.section({"style": {"width": "50%", "float": "left"}}, Gallery()),
)
run(App)
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json
================================================
[
{
"name": "Homenaje a la Neurocirugía",
"artist": "Marta Colvin Andrade",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg/1024px-Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg",
"alt": "A bronze statue of two crossed hands delicately holding a human brain in their fingertips."
},
{
"name": "Eternal Presence",
"artist": "John Woodrow Wilson",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/6/6f/Chicago%2C_Illinois_Eternal_Silence1_crop.jpg",
"alt": "The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity."
},
{
"name": "Moai",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/5/50/AhuTongariki.JPG",
"alt": "Three monumental stone busts with the heads that are disproportionately large with somber faces."
},
{
"name": "Blue Nana",
"artist": "Niki de Saint Phalle",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Blue_Nana_-_panoramio.jpg/1024px-Blue_Nana_-_panoramio.jpg",
"alt": "A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy."
},
{
"name": "Cavaliere",
"artist": "Lamidi Olonade Fakeye",
"description": "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.",
"url": "https://upload.wikimedia.org/wikipedia/commons/3/34/Nigeria%2C_lamidi_olonade_fakeye%2C_cavaliere%2C_1992.jpg",
"alt": "An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns."
},
{
"name": "Big Bellies",
"artist": "Alina Szapocznikow",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/KMM_Szapocznikow.JPG/200px-KMM_Szapocznikow.JPG",
"alt": "The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures."
},
{
"name": "Terracotta Army",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg",
"alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor."
},
{
"name": "Lunar Landscape",
"artist": "Louise Nevelson",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/1999-3-A--J_s.jpg/220px-1999-3-A--J_s.jpg",
"alt": "A black matte sculpture where the individual elements are initially indistinguishable."
},
{
"name": "Aureole",
"artist": "Ranjani Shettar",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Shettar-_5854-sm_%285132866765%29.jpg/399px-Shettar-_5854-sm_%285132866765%29.jpg",
"alt": "A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light."
},
{
"name": "Hippos",
"artist": "Taipei Zoo",
"description": "The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Hippo_sculpture_Taipei_Zoo_20543.jpg/250px-Hippo_sculpture_Taipei_Zoo_20543.jpg",
"alt": "A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming."
}
]
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py
================================================
import json
from pathlib import Path
from reactpy import component, hooks, html, run
HERE = Path(__file__)
DATA_PATH = HERE.parent / "data.json"
sculpture_data = json.loads(DATA_PATH.read_text())
@component
def Gallery():
index, set_index = hooks.use_state(0)
show_more, set_show_more = hooks.use_state(False)
def handle_next_click(event):
set_index(index + 1)
def handle_more_click(event):
set_show_more(not show_more)
bounded_index = index % len(sculpture_data)
sculpture = sculpture_data[bounded_index]
alt = sculpture["alt"]
artist = sculpture["artist"]
description = sculpture["description"]
name = sculpture["name"]
url = sculpture["url"]
return html.div(
html.button({"on_click": handle_next_click}, "Next"),
html.h2(name, " by ", artist),
html.p(f"({bounded_index + 1} or {len(sculpture_data)})"),
html.img({"src": url, "alt": alt, "style": {"height": "200px"}}),
html.div(
html.button(
{"on_click": handle_more_click},
f"{('Show' if show_more else 'Hide')} details",
),
(html.p(description) if show_more else ""),
),
)
run(Gallery)
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/data.json
================================================
[
{
"name": "Homenaje a la Neurocirugía",
"artist": "Marta Colvin Andrade",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg/1024px-Homenaje_a_la_Neurocirug%C3%ADa%2C_Instituto_de_Neurocirug%C3%ADa%2C_Providencia%2C_Santiago_20200106_02.jpg",
"alt": "A bronze statue of two crossed hands delicately holding a human brain in their fingertips."
},
{
"name": "Eternal Presence",
"artist": "John Woodrow Wilson",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/6/6f/Chicago%2C_Illinois_Eternal_Silence1_crop.jpg",
"alt": "The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity."
},
{
"name": "Moai",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/5/50/AhuTongariki.JPG",
"alt": "Three monumental stone busts with the heads that are disproportionately large with somber faces."
},
{
"name": "Blue Nana",
"artist": "Niki de Saint Phalle",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Blue_Nana_-_panoramio.jpg/1024px-Blue_Nana_-_panoramio.jpg",
"alt": "A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy."
},
{
"name": "Cavaliere",
"artist": "Lamidi Olonade Fakeye",
"description": "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.",
"url": "https://upload.wikimedia.org/wikipedia/commons/3/34/Nigeria%2C_lamidi_olonade_fakeye%2C_cavaliere%2C_1992.jpg",
"alt": "An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns."
},
{
"name": "Big Bellies",
"artist": "Alina Szapocznikow",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/KMM_Szapocznikow.JPG/200px-KMM_Szapocznikow.JPG",
"alt": "The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures."
},
{
"name": "Terracotta Army",
"artist": "Unknown Artist",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg",
"alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor."
},
{
"name": "Lunar Landscape",
"artist": "Louise Nevelson",
"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.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/1999-3-A--J_s.jpg/220px-1999-3-A--J_s.jpg",
"alt": "A black matte sculpture where the individual elements are initially indistinguishable."
},
{
"name": "Aureole",
"artist": "Ranjani Shettar",
"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.\"",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Shettar-_5854-sm_%285132866765%29.jpg/399px-Shettar-_5854-sm_%285132866765%29.jpg",
"alt": "A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light."
},
{
"name": "Hippos",
"artist": "Taipei Zoo",
"description": "The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Hippo_sculpture_Taipei_Zoo_20543.jpg/250px-Hippo_sculpture_Taipei_Zoo_20543.jpg",
"alt": "A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming."
}
]
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py
================================================
# flake8: noqa
# errors F841,F823 for `index = index + 1` inside the closure
# :lines: 7-
# :linenos:
import json
from pathlib import Path
from reactpy import component, html, run
HERE = Path(__file__)
DATA_PATH = HERE.parent / "data.json"
sculpture_data = json.loads(DATA_PATH.read_text())
@component
def Gallery():
index = 0
def handle_click(event):
index = index + 1
bounded_index = index % len(sculpture_data)
sculpture = sculpture_data[bounded_index]
alt = sculpture["alt"]
artist = sculpture["artist"]
description = sculpture["description"]
name = sculpture["name"]
url = sculpture["url"]
return html.div(
html.button({"on_click": handle_click}, "Next"),
html.h2(name, " by ", artist),
html.p(f"({bounded_index + 1} or {len(sculpture_data)})"),
html.img({"src": url, "alt": alt, "style": {"height": "200px"}}),
html.p(description),
)
run(Gallery)
================================================
FILE: docs/source/guides/adding-interactivity/components-with-state/index.rst
================================================
Components With State
=====================
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 “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 like the current input value,
the current image, the shopping cart. In ReactPy, this kind of component-specific memory is
called state.
When Variables Aren't Enough
----------------------------
Below is a gallery of images about sculpture. Clicking the "Next" button should
increment the ``index`` and, as a result, change what image is displayed. However, this
does not work:
.. reactpy:: _examples/when_variables_are_not_enough
.. note::
Try clicking the button to see that it does not cause a change.
After clicking "Next", if you check your web server's logs, you'll discover an
``UnboundLocalError`` error. It turns out that in this case, the ``index = index + 1``
statement is similar to `trying to set global variables
<https://stackoverflow.com/questions/9264763/dont-understand-why-unboundlocalerror-occurs-closure>`__.
Technically there's a way to `fix this error
<https://docs.python.org/3/reference/simple_stmts.html#nonlocal>`__, but even if we did,
that still wouldn't fix the underlying problems:
1. **Local variables do not persist across component renders** - when a component is
updated, its associated function gets called again. That is, it renders. As a result,
all the local state that was created the last time the function was called gets
destroyed when it updates.
2. **Changes to local variables do not cause components to re-render** - there's no way
for ReactPy to observe when these variables change. Thus ReactPy is not aware that
something has changed and that a re-render should take place.
To address these problems, ReactPy provides the :func:`~reactpy.core.hooks.use_state` "hook"
which provides:
1. A **state variable** whose data is retained across renders.
2. A **state setter** function that can be used to update that variable and trigger a
render.
Adding State to Components
--------------------------
To create a state variable and state setter with :func:`~reactpy.core.hooks.use_state` hook
as described above, we'll begin by importing it:
.. testcode::
from reactpy import use_state
Then we'll make the following changes to our code :ref:`from before <When Variables
Aren't Enough>`:
.. code-block:: diff
- index = 0
+ index, set_index = use_state
def handle_click(event):
- index = index + 1
+ set_index(index + 1)
After making those changes we should get:
.. code-block::
:linenos:
:lineno-start: 14
index, set_index = use_state(0)
def handle_click(event):
set_index(index + 1)
We'll talk more about what this is doing :ref:`shortly <your first hook>`, but for
now let's just verify that this does in fact fix the problems from before:
.. reactpy:: _examples/adding_state_variable
Your First Hook
---------------
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 <the rendering process>`. 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 <managing state>`).
While hooks are just normal functions, but it's helpful to think of them as
:ref:`unconditioned <rules of hooks>` declarations about a component's needs. In other
words, you'll "use" hooks at the top of your component in the same way you might
"import" modules at the top of your Python files.
.. _Introduction to use_state:
Introduction to ``use_state``
-----------------------------
When you call :func:`~reactpy.core.hooks.use_state` inside the body of a component's render
function, you're declaring that this component needs to remember something. That
"something" which needs to be remembered, is known as **state**. So when we look at an
assignment expression like the one below
.. code-block::
index, set_index = use_state(0)
we should read it as saying that ``index`` is a piece of state which must be
remembered by the component that declared it. The argument to ``use_state`` (in this
case ``0``) is then conveying what the initial value for ``index`` is.
We should then understand that each time the component which owns this state renders
``use_state`` will return a tuple containing two values - the current value of the state
(``index``) and a function to change that value the next time the component is rendered.
Thus, in this example:
- ``index`` - is a **state variable** containing the currently stored value.
- ``set_index`` - is a **state setter** for changing that value and triggering a re-render
of the component.
The convention is that, if you name your state variable ``thing``, your state setter
should be named ``set_thing``. While you could name them anything you want, adhering to
the convention makes things easier to understand across projects.
----
To understand how this works in context, let's break down our example by examining key
moments in the execution of the ``Gallery`` component. Each numbered tab in the section
below highlights a line of code where something of interest occurs:
.. hint::
Try clicking through the numbered tabs to each highlighted step of execution
.. tab-set::
.. tab-item:: 1
.. raw:: html
<h2>Initial render</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 2
At this point, we've just begun to render the ``Gallery`` component. As yet,
ReactPy is not aware that this component has any state or what view it will
display. This will change in a moment though when we move to the next line...
.. tab-item:: 2
.. raw:: html
<h2>Initial state declaration</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 3
The ``Gallery`` component has just declared some state. ReactPy now knows that it
must remember the ``index`` and trigger an update of this component when
``set_index`` is called. Currently the value of ``index`` is ``0`` as per the
default value given to ``use_state``. Thus, the resulting view will display
information about the first item in our ``sculpture_data`` list.
.. tab-item:: 3
.. raw:: html
<h2>Define event handler</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 5
We've now defined an event handler that we intend to assign to a button in the
view. This will respond once the user clicks that button. The action this
handler performs is to update the value of ``index`` and schedule our ``Gallery``
component to update.
.. tab-item:: 4
.. raw:: html
<h2>Return the view</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 16
The ``handle_click`` function we defined above has now been assigned to a button
in the view and we are about to display information about the first item in out
``sculpture_data`` list. When the view is ultimately displayed, if a user clicks
the "Next" button, the handler we just assigned will be triggered. Until that
point though, the application will remain static.
.. tab-item:: 5
.. raw:: html
<h2>User interaction</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 5
A user has just clicked the button 🖱️! ReactPy has sent information about the event
to the ``handle_click`` function and it is about to execute. In a moment we will
update the state of this component and schedule a re-render.
.. tab-item:: 6
.. raw:: html
<h2>New state is set</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 6
We've just now told ReactPy that we want to update the state of our ``Gallery`` and
that it needs to be re-rendered. More specifically, we are incrementing its
``index``, and once ``Gallery`` re-renders the index *will* be ``1``.
Importantly, at this point, the value of ``index`` is still ``0``! This will
only change once the component begins to re-render.
.. tab-item:: 7
.. raw:: html
<h2>Next render begins</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 2
The scheduled re-render of ``Gallery`` has just begun. ReactPy has now updated its
internal state store such that, the next time we call ``use_state`` we will get
back the updated value of ``index``.
.. tab-item:: 8
.. raw:: html
<h2>Next state is acquired</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
:emphasize-lines: 3
With ReactPy's state store updated, as we call ``use_state``, instead of returning
``0`` for the value of ``index`` as it did before, ReactPy now returns the value
``1``. With this change the view we display will be altered - instead of
displaying data for the first item in our ``sculpture_data`` list we will now
display information about the second.
.. tab-item:: 9
.. raw:: html
<h2>Repeat...</h2>
.. literalinclude:: _examples/adding_state_variable/main.py
:lines: 12-33
From this point on, the steps remain the same. The only difference being the
progressively incrementing ``index`` each time the user clicks the "Next" button
and the view which is altered to to reflect the currently indexed item in the
``sculpture_data`` list.
.. note::
Once we reach the end of the ``sculpture_data`` list the view will return
back to the first item since we create a ``bounded_index`` by doing a modulo
of the index with the length of the list (``index % len(sculpture_data)``).
Ideally we would do this bounding at the time we call ``set_index`` to
prevent ``index`` from incrementing to infinity, but to keep things simple
in this examples, we've kept this logic separate.
Multiple State Declarations
---------------------------
The powerful thing about hooks like :func:`~reactpy.core.hooks.use_state` is that you're
not limited to just one state declaration. You can call ``use_state()`` as many times as
you need to in one component. For example, in the example below we've added a
``show_more`` state variable along with a few other modifications (e.g. renaming
``handle_click``) to make the description for each sculpture optionally displayed. Only
when the user clicks the "Show details" button is this description shown:
.. reactpy:: _examples/multiple_state_variables
It's generally a good idea to define separate state variables if the data they represent
is unrelated. In this case, ``index`` corresponds to what sculpture information is being
displayed and ``show_more`` is solely concerned with whether the description for a given
sculpture is shown. Put other way ``index`` is concerned with *what* information is
displayed while ``show_more`` is concerned with *how* it is displayed. Conversely
though, if you have a form with many fields, it probably makes sense to have a single
object that holds the data for all the fields rather than an object per-field.
.. note::
This topic is discussed more in the :ref:`structuring your state` section.
State is Isolated and Private
-----------------------------
State is local to a component instance on the screen. In other words, if you render the
same component twice, each copy will have completely isolated state! Changing one of
them will not affect the other.
In this example, the ``Gallery`` component from earlier is rendered twice with no
changes to its logic. Try clicking the buttons inside each of the galleries. Notice that
their state is independent:
.. reactpy:: _examples/isolated_state
:result-is-default-tab:
This is what makes state different from regular variables that you might declare at the
top of your module. State is not tied to a particular function call or a place in the
code, but it’s “local” to the specific place on the screen. You rendered two ``Gallery``
components, so their state is stored separately.
Also notice how the Page component doesn’t “know” anything about the Gallery state or
even whether it has any. Unlike props, state is fully private to the component declaring
it. The parent component can’t change it. This lets you add state to any component or
remove it without impacting the rest of the components.
.. card::
:link: /guides/managing-state/sharing-component-state/index
:link-type: doc
:octicon:`book` Read More
^^^^^^^^^^^^^^^^^^^^^^^^^
What if you wanted both galleries to keep their states in sync? The right way to do
it in ReactPy is to remove state from child components and add it to their closest
shared parent.
================================================
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py
================================================
from reactpy import component, html, run, use_state
@component
def Definitions():
term_to_add, set_term_to_add = use_state(None)
definition_to_add, set_definition_to_add = use_state(None)
all_terms, set_all_terms = use_state({})
def handle_term_to_add_change(event):
set_term_to_add(event["target"]["value"])
def handle_definition_to_add_change(event):
set_definition_to_add(event["target"]["value"])
def handle_add_click(event):
if term_to_add and definition_to_add:
set_all_terms({**all_terms, term_to_add: definition_to_add})
set_term_to_add(None)
set_definition_to_add(None)
def make_delete_click_handler(term_to_delete):
def handle_click(event):
set_all_terms({t: d for t, d in all_terms.items() if t != term_to_delete})
return handle_click
return html.div(
html.button({"on_click": handle_add_click}, "add term"),
html.label(
"Term: ",
html.input({"value": term_to_add, "on_change": handle_term_to_add_change}),
),
html.label(
"Definition: ",
html.input(
{
"value": definition_to_add,
"on_change": handle_definition_to_add_change,
}
),
),
html.hr(),
[
html.div(
{"key": term},
html.button(
{"on_click": make_delete_click_handler(term)}, "delete term"
),
html.dt(term),
html.dd(definition),
)
for term, definition in all_terms.items()
],
)
run(Definitions)
================================================
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py
================================================
from reactpy import component, html, run, use_state
@component
def Form():
person, set_person = use_state(
{
"first_name": "Barbara",
"last_name": "Hepworth",
"email": "bhepworth@sculpture.com",
}
)
def handle_first_name_change(event):
set_person({**person, "first_name": event["target"]["value"]})
def handle_last_name_change(event):
set_person({**person, "last_name": event["target"]["value"]})
def handle_email_change(event):
set_person({**person, "email": event["target"]["value"]})
return html.div(
html.label(
"First name: ",
html.input(
{"value": person["first_name"], "on_change": handle_first_name_change}
),
),
html.label(
"Last name: ",
html.input(
{"value": person["last_name"], "on_change": handle_last_name_change}
),
),
html.label(
"Email: ",
html.input({"value": person["email"], "on_change": handle_email_change}),
),
html.p(f"{person['first_name']} {person['last_name']} {person['email']}"),
)
run(Form)
================================================
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py
================================================
from reactpy import component, html, run, use_state
@component
def ArtistList():
artist_to_add, set_artist_to_add = use_state("")
artists, set_artists = use_state([])
def handle_change(event):
set_artist_to_add(event["target"]["value"])
def handle_click(event):
if artist_to_add and artist_to_add not in artists:
set_artists([*artists, artist_to_add])
set_artist_to_add("")
return html.div(
html.h1("Inspiring sculptors:"),
html.input({"value": artist_to_add, "on_change": handle_change}),
html.button({"on_click":
gitextract_qt3yaq80/
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── config.yml
│ │ └── issue-form.yml
│ ├── copilot-instructions.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── .hatch-run.yml
│ ├── check.yml
│ ├── codeql-analysis.yml
│ └── publish.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .prettierrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs/
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── docs_app/
│ │ ├── __init__.py
│ │ ├── app.py
│ │ ├── dev.py
│ │ ├── examples.py
│ │ └── prod.py
│ ├── main.py
│ ├── pyproject.toml
│ └── source/
│ ├── _custom_js/
│ │ ├── README.md
│ │ ├── bun.lockb
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ └── src/
│ │ └── index.js
│ ├── _exts/
│ │ ├── async_doctest.py
│ │ ├── autogen_api_docs.py
│ │ ├── build_custom_js.py
│ │ ├── copy_vdom_json_schema.py
│ │ ├── custom_autosectionlabel.py
│ │ ├── patched_html_translator.py
│ │ ├── reactpy_example.py
│ │ └── reactpy_view.py
│ ├── _static/
│ │ └── css/
│ │ ├── furo-theme-overrides.css
│ │ ├── larger-api-margins.css
│ │ ├── larger-headings.css
│ │ ├── reactpy-view.css
│ │ ├── sphinx-design-overrides.css
│ │ └── widget-output-css-overrides.css
│ ├── about/
│ │ ├── changelog.rst
│ │ ├── contributor-guide.rst
│ │ └── credits-and-licenses.rst
│ ├── conf.py
│ ├── guides/
│ │ ├── adding-interactivity/
│ │ │ ├── components-with-state/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── adding_state_variable/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ ├── isolated_state/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ ├── multiple_state_variables/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ └── when_variables_are_not_enough/
│ │ │ │ │ ├── data.json
│ │ │ │ │ └── main.py
│ │ │ │ └── index.rst
│ │ │ ├── dangers-of-mutability/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── dict_remove.py
│ │ │ │ │ ├── dict_update.py
│ │ │ │ │ ├── list_insert.py
│ │ │ │ │ ├── list_re_order.py
│ │ │ │ │ ├── list_remove.py
│ │ │ │ │ ├── list_replace.py
│ │ │ │ │ ├── moving_dot.py
│ │ │ │ │ ├── moving_dot_broken.py
│ │ │ │ │ ├── set_remove.py
│ │ │ │ │ └── set_update.py
│ │ │ │ └── index.rst
│ │ │ ├── index.rst
│ │ │ ├── multiple-state-updates/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── delay_before_count_updater.py
│ │ │ │ │ ├── delay_before_set_count.py
│ │ │ │ │ ├── set_color_3_times.py
│ │ │ │ │ └── set_state_function.py
│ │ │ │ └── index.rst
│ │ │ ├── responding-to-events/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── audio_player.py
│ │ │ │ │ ├── button_async_handlers.py
│ │ │ │ │ ├── button_does_nothing.py
│ │ │ │ │ ├── button_handler_as_arg.py
│ │ │ │ │ ├── button_prints_event.py
│ │ │ │ │ ├── button_prints_message.py
│ │ │ │ │ ├── prevent_default_event_actions.py
│ │ │ │ │ └── stop_event_propagation.py
│ │ │ │ └── index.rst
│ │ │ └── state-as-a-snapshot/
│ │ │ ├── _examples/
│ │ │ │ ├── delayed_print_after_set.py
│ │ │ │ ├── print_chat_message.py
│ │ │ │ ├── print_count_after_set.py
│ │ │ │ ├── send_message.py
│ │ │ │ └── set_counter_3_times.py
│ │ │ └── index.rst
│ │ ├── creating-interfaces/
│ │ │ ├── html-with-reactpy/
│ │ │ │ └── index.rst
│ │ │ ├── index.rst
│ │ │ ├── rendering-data/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── sorted_and_filtered_todo_list.py
│ │ │ │ │ ├── todo_from_list.py
│ │ │ │ │ └── todo_list_with_keys.py
│ │ │ │ └── index.rst
│ │ │ └── your-first-components/
│ │ │ ├── _examples/
│ │ │ │ ├── bad_conditional_todo_list.py
│ │ │ │ ├── good_conditional_todo_list.py
│ │ │ │ ├── nested_photos.py
│ │ │ │ ├── parametrized_photos.py
│ │ │ │ ├── simple_photo.py
│ │ │ │ ├── todo_list.py
│ │ │ │ ├── wrap_in_div.py
│ │ │ │ └── wrap_in_fragment.py
│ │ │ └── index.rst
│ │ ├── escape-hatches/
│ │ │ ├── _examples/
│ │ │ │ ├── material_ui_button_no_action.py
│ │ │ │ ├── material_ui_button_on_click.py
│ │ │ │ └── super_simple_chart/
│ │ │ │ ├── main.py
│ │ │ │ └── super-simple-chart.js
│ │ │ ├── distributing-javascript.rst
│ │ │ ├── index.rst
│ │ │ ├── javascript-components.rst
│ │ │ ├── using-a-custom-backend.rst
│ │ │ └── using-a-custom-client.rst
│ │ ├── getting-started/
│ │ │ ├── _examples/
│ │ │ │ ├── debug_error_example.py
│ │ │ │ ├── hello_world.py
│ │ │ │ ├── run_fastapi.py
│ │ │ │ ├── run_flask.py
│ │ │ │ ├── run_sanic.py
│ │ │ │ ├── run_starlette.py
│ │ │ │ ├── run_tornado.py
│ │ │ │ └── sample_app.py
│ │ │ ├── _static/
│ │ │ │ ├── embed-doc-ex.html
│ │ │ │ └── embed-reactpy-view/
│ │ │ │ ├── index.html
│ │ │ │ └── main.py
│ │ │ ├── index.rst
│ │ │ ├── installing-reactpy.rst
│ │ │ └── running-reactpy.rst
│ │ ├── managing-state/
│ │ │ ├── combining-contexts-and-reducers/
│ │ │ │ └── index.rst
│ │ │ ├── deeply-sharing-state-with-contexts/
│ │ │ │ └── index.rst
│ │ │ ├── how-to-structure-state/
│ │ │ │ └── index.rst
│ │ │ ├── index.rst
│ │ │ ├── sharing-component-state/
│ │ │ │ ├── _examples/
│ │ │ │ │ ├── filterable_list/
│ │ │ │ │ │ ├── data.json
│ │ │ │ │ │ └── main.py
│ │ │ │ │ └── synced_inputs/
│ │ │ │ │ └── main.py
│ │ │ │ └── index.rst
│ │ │ ├── simplifying-updates-with-reducers/
│ │ │ │ └── index.rst
│ │ │ └── when-and-how-to-reset-state/
│ │ │ └── index.rst
│ │ └── understanding-reactpy/
│ │ ├── index.rst
│ │ ├── layout-render-servers.rst
│ │ ├── representing-html.rst
│ │ ├── the-rendering-pipeline.rst
│ │ ├── the-rendering-process.rst
│ │ ├── what-are-components.rst
│ │ ├── why-reactpy-needs-keys.rst
│ │ └── writing-tests.rst
│ ├── index.rst
│ └── reference/
│ ├── _examples/
│ │ ├── character_movement/
│ │ │ └── main.py
│ │ ├── click_count.py
│ │ ├── material_ui_switch.py
│ │ ├── matplotlib_plot.py
│ │ ├── network_graph.py
│ │ ├── pigeon_maps.py
│ │ ├── simple_dashboard.py
│ │ ├── slideshow.py
│ │ ├── snake_game.py
│ │ ├── todo.py
│ │ ├── use_reducer_counter.py
│ │ ├── use_state_counter.py
│ │ └── victory_chart.py
│ ├── _static/
│ │ └── vdom-json-schema.json
│ ├── browser-events.rst
│ ├── hooks-api.rst
│ ├── html-attributes.rst
│ ├── javascript-api.rst
│ └── specifications.rst
├── pyproject.toml
├── src/
│ ├── build_scripts/
│ │ ├── build_js_app.py
│ │ ├── build_js_client.py
│ │ ├── build_js_event_to_object.py
│ │ ├── clean_js_dir.py
│ │ ├── copy_dir.py
│ │ ├── delete_old_coverage.py
│ │ └── install_playwright.py
│ ├── js/
│ │ ├── .gitignore
│ │ ├── bun.lockb
│ │ ├── eslint.config.mjs
│ │ ├── package.json
│ │ ├── packages/
│ │ │ ├── @reactpy/
│ │ │ │ ├── app/
│ │ │ │ │ ├── bun.lockb
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── preact-dom.ts
│ │ │ │ │ │ ├── preact-jsx-runtime.ts
│ │ │ │ │ │ └── preact.ts
│ │ │ │ │ └── tsconfig.json
│ │ │ │ └── client/
│ │ │ │ ├── README.md
│ │ │ │ ├── bun.lockb
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ ├── bind.tsx
│ │ │ │ │ ├── client.ts
│ │ │ │ │ ├── components.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── logger.ts
│ │ │ │ │ ├── mount.tsx
│ │ │ │ │ ├── types.ts
│ │ │ │ │ ├── vdom.tsx
│ │ │ │ │ └── websocket.ts
│ │ │ │ └── tsconfig.json
│ │ │ └── event-to-object/
│ │ │ ├── README.md
│ │ │ ├── bun.lockb
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ └── index.ts
│ │ │ ├── tests/
│ │ │ │ ├── event-to-object.test.ts
│ │ │ │ └── tooling/
│ │ │ │ ├── check.ts
│ │ │ │ ├── mock.ts
│ │ │ │ └── setup.js
│ │ │ ├── tsconfig.json
│ │ │ └── vitest.config.ts
│ │ └── tsconfig.json
│ └── reactpy/
│ ├── __init__.py
│ ├── _console/
│ │ ├── __init__.py
│ │ ├── ast_utils.py
│ │ ├── cli.py
│ │ ├── rewrite_keys.py
│ │ └── rewrite_props.py
│ ├── _html.py
│ ├── _option.py
│ ├── _warnings.py
│ ├── config.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── _f_back.py
│ │ ├── _life_cycle_hook.py
│ │ ├── _thread_local.py
│ │ ├── component.py
│ │ ├── events.py
│ │ ├── hooks.py
│ │ ├── layout.py
│ │ ├── serve.py
│ │ └── vdom.py
│ ├── executors/
│ │ ├── __init__.py
│ │ ├── asgi/
│ │ │ ├── __init__.py
│ │ │ ├── middleware.py
│ │ │ ├── pyscript.py
│ │ │ ├── standalone.py
│ │ │ └── types.py
│ │ ├── pyscript/
│ │ │ ├── __init__.py
│ │ │ ├── component_template.py
│ │ │ ├── components.py
│ │ │ ├── layout_handler.py
│ │ │ └── utils.py
│ │ └── utils.py
│ ├── logging.py
│ ├── py.typed
│ ├── reactjs/
│ │ ├── __init__.py
│ │ ├── module.py
│ │ ├── types.py
│ │ └── utils.py
│ ├── static/
│ │ └── pyscript-hide-debug.css
│ ├── templatetags/
│ │ ├── __init__.py
│ │ └── jinja.py
│ ├── testing/
│ │ ├── __init__.py
│ │ ├── backend.py
│ │ ├── common.py
│ │ ├── display.py
│ │ └── logs.py
│ ├── transforms.py
│ ├── types.py
│ ├── utils.py
│ ├── web/
│ │ ├── __init__.py
│ │ ├── module.py
│ │ └── utils.py
│ └── widgets.py
└── tests/
├── __init__.py
├── conftest.py
├── sample.py
├── templates/
│ ├── index.html
│ ├── jinja_bad_kwargs.html
│ └── pyscript.html
├── test_asgi/
│ ├── __init__.py
│ ├── pyscript_components/
│ │ ├── load_first.py
│ │ ├── load_second.py
│ │ └── root.py
│ ├── test_init.py
│ ├── test_middleware.py
│ ├── test_pyscript.py
│ ├── test_standalone.py
│ └── test_utils.py
├── test_client.py
├── test_config.py
├── test_console/
│ ├── __init__.py
│ ├── test_rewrite_keys.py
│ └── test_rewrite_props.py
├── test_core/
│ ├── __init__.py
│ ├── test_component.py
│ ├── test_events.py
│ ├── test_hooks.py
│ ├── test_layout.py
│ ├── test_serve.py
│ └── test_vdom.py
├── test_html.py
├── test_option.py
├── test_pyscript/
│ ├── __init__.py
│ ├── pyscript_components/
│ │ ├── custom_root_name.py
│ │ └── root.py
│ ├── test_components.py
│ └── test_utils.py
├── test_reactjs/
│ ├── __init__.py
│ ├── js_fixtures/
│ │ ├── callable-prop.js
│ │ ├── component-can-have-child.js
│ │ ├── export-resolution/
│ │ │ ├── index.js
│ │ │ ├── one.js
│ │ │ └── two.js
│ │ ├── exports-syntax.js
│ │ ├── exports-two-components.js
│ │ ├── generic-module.js
│ │ ├── keys-properly-propagated.js
│ │ ├── nest-custom-under-web.js
│ │ ├── set-flag-when-unmount-is-called.js
│ │ ├── simple-button.js
│ │ └── subcomponent-notation.js
│ ├── test_modules.py
│ ├── test_modules_from_npm.py
│ └── test_utils.py
├── test_sample.py
├── test_testing.py
├── test_utils.py
├── test_web/
│ ├── __init__.py
│ ├── js_fixtures/
│ │ ├── callable-prop.js
│ │ ├── component-can-have-child.js
│ │ ├── export-resolution/
│ │ │ ├── index.js
│ │ │ ├── one.js
│ │ │ └── two.js
│ │ ├── exports-syntax.js
│ │ ├── exports-two-components.js
│ │ ├── generic-module.js
│ │ ├── keys-properly-propagated.js
│ │ ├── set-flag-when-unmount-is-called.js
│ │ ├── simple-button.js
│ │ └── subcomponent-notation.js
│ └── test_module.py
├── test_widgets.py
└── tooling/
├── __init__.py
├── aio.py
├── common.py
├── hooks.py
├── layout.py
└── select.py
SYMBOL INDEX (1044 symbols across 189 files)
FILE: docs/docs_app/app.py
function Example (line 24) | def Example():
function _get_examples (line 30) | def _get_examples():
function reload_examples (line 36) | def reload_examples():
function make_app (line 44) | def make_app(name: str):
FILE: docs/docs_app/dev.py
function wrap_builder (line 25) | def wrap_builder(old_builder):
function main (line 61) | def main():
FILE: docs/docs_app/examples.py
function load_examples (line 17) | def load_examples() -> Iterator[tuple[str, Callable[[], ComponentType]]]:
function all_example_names (line 22) | def all_example_names() -> set[str]:
function load_one_example (line 30) | def load_one_example(file_or_name: Path | str) -> Callable[[], Component...
function get_normalized_example_name (line 37) | def get_normalized_example_name(
function get_main_example_file_by_name (line 45) | def get_main_example_file_by_name(
function get_example_files_by_name (line 55) | def get_example_files_by_name(
function _iter_example_files (line 66) | def _iter_example_files(root: Path) -> Iterator[Path]:
function _load_one_example (line 75) | def _load_one_example(file_or_name: Path | str) -> ComponentType:
function _get_root_example_path_by_name (line 133) | def _get_root_example_path_by_name(name: str, relative_to: str | Path | ...
class _PrintBuffer (line 142) | class _PrintBuffer:
method __init__ (line 143) | def __init__(self, max_lines: int = 10):
method set_callback (line 148) | def set_callback(self, function: Callable[[str], None]) -> None:
method getvalue (line 151) | def getvalue(self) -> str:
method write (line 154) | def write(self, text: str) -> None:
function _make_example_did_not_run (line 163) | def _make_example_did_not_run(example_name):
function _make_error_display (line 171) | def _make_error_display(message):
FILE: docs/docs_app/prod.py
function main (line 8) | def main() -> None:
FILE: docs/source/_custom_js/src/index.js
function mountWidgetExample (line 5) | function mountWidgetExample(
function activateView (line 37) | function activateView(mountEl, client, useActivateButton) {
function triggerIfInViewport (line 80) | function triggerIfInViewport(element, callback) {
FILE: docs/source/_exts/async_doctest.py
class TestRunnerWrapper (line 22) | class TestRunnerWrapper:
method __init__ (line 23) | def __init__(self, runner: DocTestRunner):
method __getattr__ (line 26) | def __getattr__(self, name: str) -> Any:
method run (line 29) | def run(self, test: DocTest, *args: Any, **kwargs: Any) -> Any:
class AsyncDoctestBuilder (line 35) | class AsyncDoctestBuilder(DocTestBuilder):
method test_runner (line 37) | def test_runner(self) -> DocTestRunner:
method test_runner (line 41) | def test_runner(self, value: DocTestRunner) -> None:
function setup (line 45) | def setup(app: Sphinx) -> None:
FILE: docs/source/_exts/autogen_api_docs.py
function generate_api_docs (line 39) | def generate_api_docs():
function make_package_section (line 52) | def make_package_section(file: Path) -> str:
function make_module_section (line 66) | def make_module_section(file: Path) -> str:
function get_module_name (line 79) | def get_module_name(path: Path) -> str:
function get_section_symbol (line 83) | def get_section_symbol(path: Path) -> str:
function walk_python_files (line 92) | def walk_python_files(root: Path, ignore_dirs: Collection[str]) -> Itera...
function setup (line 142) | def setup(app: Sphinx) -> None:
FILE: docs/source/_exts/build_custom_js.py
function setup (line 10) | def setup(app: Sphinx) -> None:
FILE: docs/source/_exts/copy_vdom_json_schema.py
function setup (line 9) | def setup(app: Sphinx) -> None:
FILE: docs/source/_exts/custom_autosectionlabel.py
function get_node_depth (line 23) | def get_node_depth(node: Node) -> int:
function register_sections_as_label (line 32) | def register_sections_as_label(app: Sphinx, document: Node) -> None:
function setup (line 70) | def setup(app: Sphinx) -> dict[str, Any]:
FILE: docs/source/_exts/patched_html_translator.py
class PatchedHTMLTranslator (line 6) | class PatchedHTMLTranslator(
method starttag (line 9) | def starttag(self, node, tagname, *args, **attrs):
function setup (line 23) | def setup(app):
FILE: docs/source/_exts/reactpy_example.py
class WidgetExample (line 19) | class WidgetExample(SphinxDirective):
method run (line 29) | def run(self):
function _make_tab_items (line 96) | def _make_tab_items(labeled_content_tuples):
function _literal_include (line 106) | def _literal_include(path: Path, linenos: bool):
function _join_options (line 124) | def _join_options(option_strings: list[str]) -> str:
function _get_file_options (line 131) | def _get_file_options(file: Path) -> list[str]:
function _interactive_widget (line 148) | def _interactive_widget(name, with_activate_button):
function _string_to_nested_lines (line 175) | def _string_to_nested_lines(content):
function setup (line 179) | def setup(app: Sphinx) -> None:
FILE: docs/source/_exts/reactpy_view.py
class IteractiveWidget (line 16) | class IteractiveWidget(SphinxDirective):
method run (line 26) | def run(self):
function setup (line 60) | def setup(app: Sphinx) -> None:
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py
function Gallery (line 12) | def Gallery():
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py
function Gallery (line 12) | def Gallery():
function App (line 46) | def App():
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py
function Gallery (line 12) | def Gallery():
FILE: docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py
function Gallery (line 19) | def Gallery():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py
function Definitions (line 5) | def Definitions():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py
function Form (line 5) | def Form():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py
function ArtistList (line 5) | def ArtistList():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py
function ArtistList (line 5) | def ArtistList():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_remove.py
function ArtistList (line 5) | def ArtistList():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py
function CounterList (line 5) | def CounterList():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py
function MovingDot (line 5) | def MovingDot():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py
function MovingDot (line 7) | def MovingDot():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_remove.py
function Grid (line 5) | def Grid():
FILE: docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_update.py
function Grid (line 5) | def Grid():
FILE: docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py
function Counter (line 7) | def Counter():
FILE: docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py
function Counter (line 7) | def Counter():
FILE: docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py
function ColorButton (line 5) | def ColorButton():
FILE: docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_state_function.py
function increment (line 4) | def increment(old_number):
function Counter (line 10) | def Counter():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/audio_player.py
function PlayDinosaurSound (line 7) | def PlayDinosaurSound():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/button_async_handlers.py
function ButtonWithDelay (line 7) | def ButtonWithDelay(message, delay):
function App (line 16) | def App():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/button_does_nothing.py
function Button (line 5) | def Button():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py
function Button (line 5) | def Button(display_text, on_click):
function PlayButton (line 10) | def PlayButton(movie_name):
function FastForwardButton (line 18) | def FastForwardButton():
function App (line 26) | def App():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_event.py
function Button (line 5) | def Button():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_message.py
function PrintButton (line 5) | def PrintButton(display_text, message_text):
function App (line 13) | def App():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py
function DoNotChangePages (line 5) | def DoNotChangePages():
FILE: docs/source/guides/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py
function DivInDiv (line 5) | def DivInDiv():
FILE: docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/delayed_print_after_set.py
function Counter (line 7) | def Counter():
FILE: docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/print_chat_message.py
function App (line 7) | def App():
FILE: docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/print_count_after_set.py
function Counter (line 5) | def Counter():
FILE: docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/send_message.py
function App (line 5) | def App():
FILE: docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/set_counter_3_times.py
function Counter (line 5) | def Counter():
FILE: docs/source/guides/creating-interfaces/rendering-data/_examples/sorted_and_filtered_todo_list.py
function DataList (line 5) | def DataList(items, filter_by_priority=None, sort_by_priority=False):
function TodoList (line 15) | def TodoList():
FILE: docs/source/guides/creating-interfaces/rendering-data/_examples/todo_from_list.py
function DataList (line 5) | def DataList(items):
function TodoList (line 11) | def TodoList():
FILE: docs/source/guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys.py
function DataList (line 5) | def DataList(items, filter_by_priority=None, sort_by_priority=False):
function TodoList (line 15) | def TodoList():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/bad_conditional_todo_list.py
function Item (line 5) | def Item(name, done):
function TodoList (line 13) | def TodoList():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/good_conditional_todo_list.py
function Item (line 5) | def Item(name, done):
function TodoList (line 10) | def TodoList():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/nested_photos.py
function Photo (line 5) | def Photo():
function Gallery (line 16) | def Gallery():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/parametrized_photos.py
function Photo (line 5) | def Photo(alt_text, image_id):
function Gallery (line 16) | def Gallery():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/simple_photo.py
function Photo (line 5) | def Photo():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/todo_list.py
function Item (line 5) | def Item(name, done):
function TodoList (line 10) | def TodoList():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_div.py
function MyTodoList (line 5) | def MyTodoList():
FILE: docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_fragment.py
function MyTodoList (line 5) | def MyTodoList():
FILE: docs/source/guides/escape-hatches/_examples/material_ui_button_no_action.py
function HelloWorld (line 12) | def HelloWorld():
FILE: docs/source/guides/escape-hatches/_examples/material_ui_button_on_click.py
function ViewButtonEvents (line 14) | def ViewButtonEvents():
FILE: docs/source/guides/escape-hatches/_examples/super_simple_chart/main.py
function App (line 11) | def App():
FILE: docs/source/guides/escape-hatches/_examples/super_simple_chart/super-simple-chart.js
function bind (line 6) | function bind(node, config) {
function SuperSimpleChart (line 14) | function SuperSimpleChart(props) {
function makePath (line 45) | function makePath(props, domain, data, options) {
function makeAxis (line 65) | function makeAxis(props, options) {
FILE: docs/source/guides/getting-started/_examples/debug_error_example.py
function App (line 5) | def App():
function GoodComponent (line 10) | def GoodComponent():
function BadComponent (line 15) | def BadComponent():
FILE: docs/source/guides/getting-started/_examples/hello_world.py
function App (line 5) | def App():
FILE: docs/source/guides/getting-started/_examples/run_fastapi.py
function HelloWorld (line 17) | def HelloWorld():
FILE: docs/source/guides/getting-started/_examples/run_flask.py
function HelloWorld (line 17) | def HelloWorld():
FILE: docs/source/guides/getting-started/_examples/run_sanic.py
function HelloWorld (line 17) | def HelloWorld():
FILE: docs/source/guides/getting-started/_examples/run_starlette.py
function HelloWorld (line 17) | def HelloWorld():
FILE: docs/source/guides/getting-started/_examples/run_tornado.py
function HelloWorld (line 18) | def HelloWorld():
function make_app (line 22) | def make_app():
FILE: docs/source/guides/getting-started/_static/embed-reactpy-view/main.py
function index (line 11) | async def index(request):
function ReactPyView (line 16) | def ReactPyView():
FILE: docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py
function FilterableList (line 12) | def FilterableList():
function Search (line 18) | def Search(value, set_value):
function Table (line 29) | def Table(value, set_value):
FILE: docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py
function SyncedInputs (line 5) | def SyncedInputs():
function Input (line 14) | def Input(label, value, set_value):
FILE: docs/source/reference/_examples/character_movement/main.py
class Position (line 11) | class Position(NamedTuple):
function rotate (line 17) | def rotate(degrees):
function translate (line 25) | def translate(x=0, y=0):
function Scene (line 34) | def Scene():
FILE: docs/source/reference/_examples/click_count.py
function ClickCount (line 5) | def ClickCount():
FILE: docs/source/reference/_examples/material_ui_switch.py
function DayNightSwitch (line 8) | def DayNightSwitch():
FILE: docs/source/reference/_examples/matplotlib_plot.py
function PolynomialPlot (line 10) | def PolynomialPlot():
function ExpandableNumberInputs (line 23) | def ExpandableNumberInputs(values, set_values):
function plot (line 49) | def plot(title, x, y):
function poly_coef_input (line 59) | def poly_coef_input(index, callback):
function polynomial (line 72) | def polynomial(x, coefficients):
function linspace (line 76) | def linspace(start, stop, n):
FILE: docs/source/reference/_examples/network_graph.py
function RandomNetworkGraph (line 14) | def RandomNetworkGraph():
function random_network (line 24) | def random_network(number_of_nodes):
FILE: docs/source/reference/_examples/pigeon_maps.py
function MapWithMarkers (line 8) | def MapWithMarkers():
function use_set (line 34) | def use_set(initial_value=None):
FILE: docs/source/reference/_examples/simple_dashboard.py
function RandomWalk (line 19) | def RandomWalk():
function RandomWalkGraph (line 48) | def RandomWalkGraph(mu, sigma):
function NumberInput (line 74) | def NumberInput(label, value, set_value_callback, domain):
function use_interval (line 92) | def use_interval(rate):
FILE: docs/source/reference/_examples/slideshow.py
function Slideshow (line 5) | def Slideshow():
FILE: docs/source/reference/_examples/snake_game.py
class GameState (line 9) | class GameState(enum.Enum):
function GameView (line 17) | def GameView():
class Direction (line 45) | class Direction(enum.Enum):
function GameLoop (line 53) | def GameLoop(grid_size, block_scale, set_game_state):
function use_snake_food (line 119) | def use_snake_food(grid_size, current_snake):
function use_interval (line 131) | def use_interval(rate):
function create_grid (line 141) | def create_grid(grid_size, block_scale):
function create_grid_block (line 168) | def create_grid_block(color, block_scale, key):
function assign_grid_block_color (line 182) | def assign_grid_block_color(grid, point, color):
FILE: docs/source/reference/_examples/todo.py
function Todo (line 5) | def Todo():
FILE: docs/source/reference/_examples/use_reducer_counter.py
function reducer (line 4) | def reducer(count, action):
function Counter (line 17) | def Counter():
FILE: docs/source/reference/_examples/use_state_counter.py
function increment (line 4) | def increment(last_count):
function decrement (line 8) | def decrement(last_count):
function Counter (line 13) | def Counter():
FILE: src/build_scripts/copy_dir.py
function copy_files (line 12) | def copy_files(source: Path, destination: Path) -> None:
FILE: src/js/packages/@reactpy/client/src/bind.tsx
function infer_bind_from_environment (line 3) | async function infer_bind_from_environment() {
function local_preact_bind (line 18) | function local_preact_bind(node: HTMLElement) {
function reactjs_bind (line 31) | function reactjs_bind(node: HTMLElement, React: any, ReactDOM: any) {
FILE: src/js/packages/@reactpy/client/src/client.ts
method constructor (line 15) | constructor() {
method onMessage (line 20) | onMessage(type: string, handler: (message: any) => void): () => void {
method handleIncoming (line 38) | protected handleIncoming(message: any): void {
class ReactPyClient (line 55) | class ReactPyClient
method constructor (line 64) | constructor(props: GenericReactPyClientProps) {
method sendMessage (line 82) | sendMessage(message: any): void {
method loadModule (line 93) | loadModule(moduleName: string): Promise<ReactPyModule> {
FILE: src/js/packages/@reactpy/client/src/components.tsx
function Layout (line 21) | function Layout(props: { client: ReactPyClient }): JSX.Element {
function Element (line 45) | function Element({ model }: { model: ReactPyVdom }): JSX.Element | null {
function StandardElement (line 67) | function StandardElement({ model }: { model: ReactPyVdom }) {
function UserInputElement (line 81) | function UserInputElement({ model }: { model: ReactPyVdom }): JSX.Element {
function ScriptElement (line 114) | function ScriptElement({ model }: { model: ReactPyVdom }) {
function ImportedElement (line 149) | function ImportedElement({ model }: { model: ReactPyVdom }) {
function useForceUpdate (line 173) | function useForceUpdate() {
function useImportSource (line 178) | function useImportSource(model: ReactPyVdom): MutableRefObject<any> {
constant SPECIAL_ELEMENTS (line 229) | const SPECIAL_ELEMENTS = {
FILE: src/js/packages/@reactpy/client/src/mount.tsx
function mountReactPy (line 6) | function mountReactPy(props: MountProps) {
FILE: src/js/packages/@reactpy/client/src/types.ts
type ReconnectOptions (line 5) | type ReconnectOptions = {
type CreateReconnectingWebSocketProps (line 12) | type CreateReconnectingWebSocketProps = {
type ReactPyUrls (line 24) | type ReactPyUrls = {
type GenericReactPyClientProps (line 29) | type GenericReactPyClientProps = {
type MountProps (line 35) | type MountProps = {
type ReactPyComponent (line 47) | type ReactPyComponent = ComponentType<{ model: ReactPyVdom }>;
type ReactPyVdom (line 49) | type ReactPyVdom = {
type ReactPyVdomEventHandler (line 59) | type ReactPyVdomEventHandler = {
type ReactPyVdomImportSource (line 65) | type ReactPyVdomImportSource = {
type ReactPyModule (line 72) | type ReactPyModule = {
type ReactPyModuleBindingContext (line 79) | type ReactPyModuleBindingContext = {
type ReactPyModuleBinding (line 84) | type ReactPyModuleBinding = {
type BindImportSource (line 94) | type BindImportSource = (
type ImportSourceBinding (line 98) | type ImportSourceBinding = {
type LayoutUpdateMessage (line 105) | type LayoutUpdateMessage = {
type LayoutEventMessage (line 111) | type LayoutEventMessage = {
type IncomingMessage (line 117) | type IncomingMessage = LayoutUpdateMessage;
type OutgoingMessage (line 118) | type OutgoingMessage = LayoutEventMessage;
type Message (line 119) | type Message = IncomingMessage | OutgoingMessage;
type ReactPyClientInterface (line 126) | interface ReactPyClientInterface {
FILE: src/js/packages/@reactpy/client/src/vdom.tsx
function loadImportSource (line 16) | async function loadImportSource(
function createImportSourceElement (line 64) | function createImportSourceElement(props: {
function getComponentFromModule (line 111) | function getComponentFromModule(
function isImportSourceEqual (line 147) | function isImportSourceEqual(
function stringifyImportSource (line 157) | function stringifyImportSource(importSource: ReactPyVdomImportSource) {
function createChildren (line 164) | function createChildren<Child>(
function createAttributes (line 182) | function createAttributes(
function createEventHandler (line 206) | function createEventHandler(
function createInlineJavaScript (line 234) | function createInlineJavaScript(
class ReactPyChild (line 263) | class ReactPyChild extends HTMLElement {
method constructor (line 270) | constructor() {
method connectedCallback (line 276) | connectedCallback() {
method client (line 280) | set client(value: ReactPyClient) {
method model (line 284) | set model(value: ReactPyVdom) {
method requestUpdate (line 288) | requestUpdate() {
method update (line 292) | async update() {
method disconnectedCallback (line 337) | disconnectedCallback() {
FILE: src/js/packages/@reactpy/client/src/websocket.ts
function createReconnectingWebSocket (line 4) | function createReconnectingWebSocket(
function nextInterval (line 64) | function nextInterval(
FILE: src/js/packages/event-to-object/src/index.ts
function convert (line 6) | function convert(
function serializeSelection (line 60) | function serializeSelection(
function deepCloneClass (line 90) | function deepCloneClass(
function classToArray (line 134) | function classToArray(
function classToObject (line 165) | function classToObject(
function shouldIgnoreValue (line 269) | function shouldIgnoreValue(
FILE: src/js/packages/event-to-object/tests/event-to-object.test.ts
type SimpleTestCase (line 9) | type SimpleTestCase<E extends Event> = {
FILE: src/js/packages/event-to-object/tests/tooling/check.ts
function checkEventConversion (line 7) | function checkEventConversion(
FILE: src/js/packages/event-to-object/tests/tooling/setup.js
function setup (line 6) | function setup() {
function reset (line 21) | function reset() {
FILE: src/reactpy/_console/ast_utils.py
function rewrite_changed_nodes (line 18) | def rewrite_changed_nodes(
class ChangedNode (line 92) | class ChangedNode:
function find_element_constructor_usages (line 97) | def find_element_constructor_usages(
class ElementConstructorInfo (line 166) | class ElementConstructorInfo:
function _find_comments (line 172) | def _find_comments(lines: list[str]) -> list[str]:
function _walk_with_parent (line 181) | def _walk_with_parent(
FILE: src/reactpy/_console/cli.py
function entry_point (line 11) | def entry_point() -> None:
FILE: src/reactpy/_console/rewrite_keys.py
function rewrite_keys (line 18) | def rewrite_keys(paths: list[str]) -> None:
function generate_rewrite (line 55) | def generate_rewrite(file: Path, source: str) -> str | None:
function find_nodes_to_change (line 69) | def find_nodes_to_change(tree: ast.AST) -> list[ChangedNode]:
function log_could_not_rewrite (line 90) | def log_could_not_rewrite(file: Path, tree: ast.AST) -> None:
FILE: src/reactpy/_console/rewrite_props.py
function rewrite_props (line 20) | def rewrite_props(paths: list[str]) -> None:
function generate_rewrite (line 30) | def generate_rewrite(file: Path, source: str) -> str | None:
function find_nodes_to_change (line 44) | def find_nodes_to_change(tree: ast.AST) -> list[ChangedNode]:
function conv_attr_name (line 55) | def conv_attr_name(name: str) -> str:
function _construct_prop_item (line 73) | def _construct_prop_item(key: str, value: ast.expr) -> tuple[str, ast.ex...
function _rewrite_props (line 95) | def _rewrite_props(
FILE: src/reactpy/_html.py
function _fragment (line 99) | def _fragment(
function _script (line 119) | def _script(
class SvgConstructor (line 173) | class SvgConstructor:
method __call__ (line 179) | def __call__(
method __call__ (line 184) | def __call__(self, *children: VdomChildren) -> VdomDict: ...
method __call__ (line 186) | def __call__(
method __getattr__ (line 191) | def __getattr__(self, value: str) -> VdomConstructor:
class HtmlConstructor (line 274) | class HtmlConstructor:
method __getattr__ (line 292) | def __getattr__(self, value: str) -> VdomConstructor:
FILE: src/reactpy/_option.py
class Option (line 15) | class Option(Generic[_O]):
method __init__ (line 18) | def __init__(
method name (line 47) | def name(self) -> str:
method mutable (line 52) | def mutable(self) -> bool:
method default (line 57) | def default(self) -> _O:
method current (line 62) | def current(self) -> _O:
method current (line 69) | def current(self, new: _O) -> None:
method current (line 73) | def current(self) -> None:
method subscribe (line 76) | def subscribe(self, handler: Callable[[_O], None]) -> Callable[[_O], N...
method is_set (line 85) | def is_set(self) -> bool:
method set_current (line 89) | def set_current(self, new: Any) -> None:
method set_default (line 112) | def set_default(self, new: _O) -> _O:
method reload (line 121) | def reload(self) -> None:
method unset (line 125) | def unset(self) -> None:
method __repr__ (line 137) | def __repr__(self) -> str:
class DeprecatedOption (line 141) | class DeprecatedOption(Option[_O]):
method __init__ (line 144) | def __init__(self, *args: Any, message: str, **kwargs: Any) -> None:
method current (line 149) | def current(self) -> _O:
FILE: src/reactpy/_warnings.py
function warn (line 10) | def warn(*args: Any, **kwargs: Any) -> Any:
function _frame_depth_in_module (line 19) | def _frame_depth_in_module() -> int:
function _iter_frames (line 29) | def _iter_frames(index: int = 1) -> Iterator[FrameType]:
FILE: src/reactpy/config.py
function boolean (line 17) | def boolean(value: str | bool | int) -> bool:
FILE: src/reactpy/core/_f_back.py
function f_module_name (line 7) | def f_module_name(index: int = 0) -> str:
function f_back (line 17) | def f_back(index: int = 0) -> FrameType | None:
FILE: src/reactpy/core/_life_cycle_hook.py
class EffectFunc (line 19) | class EffectFunc(Protocol):
method __call__ (line 20) | async def __call__(self, stop: Event) -> None: ...
class _HookStack (line 26) | class _HookStack(Singleton): # nocov
method get (line 35) | def get(self) -> list[LifeCycleHook]:
method initialize (line 41) | def initialize(self) -> Token[list[LifeCycleHook]] | None:
method reset (line 44) | def reset(self, token: Token[list[LifeCycleHook]] | None) -> None:
method current_hook (line 52) | def current_hook(self) -> LifeCycleHook:
class LifeCycleHook (line 63) | class LifeCycleHook:
method __init__ (line 154) | def __init__(
method schedule_render (line 169) | def schedule_render(self) -> None:
method use_state (line 180) | def use_state(self, function: Callable[[], T]) -> T:
method add_effect (line 197) | def add_effect(self, effect_func: EffectFunc) -> None:
method set_context_provider (line 207) | def set_context_provider(self, provider: ContextProvider[Any]) -> None:
method get_context_provider (line 215) | def get_context_provider(self, context: Context[T]) -> ContextProvider...
method affect_component_will_render (line 223) | async def affect_component_will_render(self, component: Component) -> ...
method affect_component_did_render (line 230) | async def affect_component_did_render(self) -> None:
method affect_layout_did_render (line 238) | async def affect_layout_did_render(self) -> None:
method affect_component_will_unmount (line 245) | async def affect_component_will_unmount(self) -> None:
method set_current (line 257) | def set_current(self) -> None:
method unset_current (line 269) | def unset_current(self) -> None:
FILE: src/reactpy/core/_thread_local.py
class ThreadLocal (line 9) | class ThreadLocal(Generic[_StateType]): # nocov
method __init__ (line 14) | def __init__(self, default: Callable[[], _StateType]):
method get (line 18) | def get(self) -> _StateType:
FILE: src/reactpy/core/component.py
function component (line 11) | def component(
FILE: src/reactpy/core/events.py
function event (line 16) | def event(
function event (line 25) | def event(
function event (line 33) | def event(
class EventHandler (line 78) | class EventHandler(BaseEventHandler):
method __init__ (line 92) | def __init__(
method __eq__ (line 124) | def __eq__(self, other: object) -> bool:
method __repr__ (line 137) | def __repr__(self) -> str:
function to_event_handler_function (line 143) | def to_event_handler_function(
function merge_event_handlers (line 181) | def merge_event_handlers(
function merge_event_handler_funcs (line 219) | def merge_event_handler_funcs(
function _inspect_event_handler_code (line 238) | def _inspect_event_handler_code(code: CodeType) -> tuple[bool, bool]:
FILE: src/reactpy/core/hooks.py
function use_state (line 55) | def use_state(initial_value: Callable[[], _Type]) -> State[_Type]: ...
function use_state (line 59) | def use_state(initial_value: _Type) -> State[_Type]: ...
function use_state (line 62) | def use_state(initial_value: _Type | Callable[[], _Type]) -> State[_Type]:
class _CurrentState (line 80) | class _CurrentState(Generic[_Type]):
method __init__ (line 83) | def __init__(
function use_effect (line 108) | def use_effect(
function use_effect (line 115) | def use_effect(
function use_effect (line 121) | def use_effect(
function use_async_effect (line 180) | def use_async_effect(
function use_async_effect (line 187) | def use_async_effect(
function use_async_effect (line 193) | def use_async_effect(
function use_debug_value (line 275) | def use_debug_value(
function create_context (line 306) | def create_context(default_value: _Type) -> Context[_Type]:
function use_context (line 326) | def use_context(context: Context[_Type]) -> _Type:
function use_connection (line 351) | def use_connection() -> Connection[Any]:
function use_scope (line 360) | def use_scope() -> dict[str, Any] | asgi_types.HTTPScope | asgi_types.We...
function use_location (line 365) | def use_location() -> Location:
function use_reducer (line 373) | def use_reducer(
function _create_dispatcher (line 393) | def _create_dispatcher(
function use_callback (line 407) | def use_callback(
function use_callback (line 414) | def use_callback(
function use_callback (line 420) | def use_callback(
class _LambdaCaller (line 447) | class _LambdaCaller(Protocol):
method __call__ (line 450) | def __call__(self, func: Callable[[], _Type]) -> _Type: ...
function use_memo (line 454) | def use_memo(
function use_memo (line 461) | def use_memo(
function use_memo (line 467) | def use_memo(
class _Memo (line 523) | class _Memo(Generic[_Type]):
method empty (line 531) | def empty(self) -> bool:
function use_ref (line 540) | def use_ref(initial_value: _Type) -> Ref[_Type]:
function _use_const (line 552) | def _use_const(function: Callable[[], _Type]) -> _Type:
function _try_to_infer_closure_values (line 556) | def _try_to_infer_closure_values(
function strictly_equal (line 572) | def strictly_equal(x: Any, y: Any) -> bool:
function run_effect_cleanup (line 625) | def run_effect_cleanup(cleanup_func: Ref[_EffectCleanFunc | None]) -> None:
FILE: src/reactpy/core/layout.py
class Layout (line 58) | class Layout(BaseLayout):
method __init__ (line 59) | def __init__(self, root: Component | Context[Any] | ContextProvider[An...
method __aenter__ (line 66) | async def __aenter__(self) -> Layout:
method __aexit__ (line 84) | async def __aexit__(
method deliver (line 111) | async def deliver(self, event: LayoutEventMessage | dict[str, Any]) ->...
method _process_event_queue (line 132) | async def _process_event_queue(
method render (line 162) | async def render(self) -> LayoutUpdateMessage:
method _serial_render (line 168) | async def _serial_render(self) -> LayoutUpdateMessage: # nocov
method _parallel_render (line 182) | async def _parallel_render(self) -> LayoutUpdateMessage:
method _create_layout_update (line 204) | async def _create_layout_update(
method _render_component (line 249) | async def _render_component(
method _render_model (line 325) | async def _render_model(
method _render_model_attributes (line 359) | def _render_model_attributes(
method _render_model_event_handlers_without_old_state (line 406) | def _render_model_event_handlers_without_old_state(
method _render_model_children (line 431) | async def _render_model_children(
method _unmount_model_states (line 487) | async def _unmount_model_states(self, old_states: list[_ModelState]) -...
method _schedule_render_task (line 502) | def _schedule_render_task(self, lcs_id: _LifeCycleStateId) -> None:
method __repr__ (line 519) | def __repr__(self) -> str:
function _new_root_model_state (line 523) | def _new_root_model_state(
function _make_component_model_state (line 539) | def _make_component_model_state(
function _copy_component_model_state (line 559) | def _copy_component_model_state(old_model_state: _ModelState) -> _ModelS...
function _update_component_model_state (line 579) | def _update_component_model_state(
function _make_element_model_state (line 603) | def _make_element_model_state(
function _update_element_model_state (line 620) | def _update_element_model_state(
class _ModelState (line 637) | class _ModelState:
method __init__ (line 654) | def __init__(
method is_component_state (line 699) | def is_component_state(self) -> bool:
method parent (line 703) | def parent(self) -> _ModelState:
method append_child (line 709) | def append_child(self, child: Any) -> None:
method __repr__ (line 712) | def __repr__(self) -> str: # nocov
function _make_life_cycle_state (line 716) | def _make_life_cycle_state(
function _update_life_cycle_state (line 728) | def _update_life_cycle_state(
class _LifeCycleState (line 743) | class _LifeCycleState(NamedTuple):
class _ThreadSafeQueue (line 759) | class _ThreadSafeQueue(Generic[_Type]):
method __init__ (line 760) | def __init__(self) -> None:
method put (line 765) | def put(self, value: _Type) -> None:
method get (line 770) | async def get(self) -> _Type:
function _get_children_info (line 776) | def _get_children_info(
FILE: src/reactpy/core/serve.py
function serve_layout (line 26) | async def serve_layout(
function _single_outgoing_loop (line 40) | async def _single_outgoing_loop(
function _single_incoming_loop (line 61) | async def _single_incoming_loop(
FILE: src/reactpy/core/vdom.py
function validate_vdom_json (line 112) | def validate_vdom_json(value: Any) -> VdomJson:
function is_vdom (line 118) | def is_vdom(value: Any) -> bool:
class Vdom (line 123) | class Vdom:
method __init__ (line 128) | def __init__(
method __getattr__ (line 148) | def __getattr__(self, attr: str) -> Vdom:
method __call__ (line 160) | def __call__(
method __call__ (line 165) | def __call__(self, *children: VdomChildren) -> VdomDict: ...
method __call__ (line 167) | def __call__(
function separate_attributes_and_children (line 207) | def separate_attributes_and_children(
function separate_attributes_handlers_and_inline_javascript (line 226) | def separate_attributes_handlers_and_inline_javascript(
function _flatten_children (line 248) | def _flatten_children(children: Sequence[Any]) -> list[Any]:
function _is_single_child (line 258) | def _is_single_child(value: Any) -> bool:
function _validate_child_key_integrity (line 266) | def _validate_child_key_integrity(value: Any) -> None:
FILE: src/reactpy/executors/asgi/middleware.py
class ReactPyMiddleware (line 44) | class ReactPyMiddleware:
method __init__ (line 49) | def __init__(
method __call__ (line 106) | async def __call__(
method match_dispatch_path (line 131) | def match_dispatch_path(self, scope: AsgiWebsocketScope) -> bool:
method match_static_path (line 134) | def match_static_path(self, scope: AsgiHttpScope) -> bool:
method match_web_modules_path (line 137) | def match_web_modules_path(self, scope: AsgiHttpScope) -> bool:
method match_extra_paths (line 140) | def match_extra_paths(self, scope: AsgiScope) -> AsgiApp | None:
class ComponentDispatchApp (line 147) | class ComponentDispatchApp:
method __call__ (line 150) | async def __call__(
class ReactPyWebsocket (line 178) | class ReactPyWebsocket(ResponseWebSocket):
method __init__ (line 179) | def __init__(
method __aenter__ (line 192) | async def __aenter__(self) -> ReactPyWebsocket:
method __aexit__ (line 196) | async def __aexit__(self, *_: Any) -> None:
method run_dispatcher (line 201) | async def run_dispatcher(self) -> None:
method send_json (line 244) | async def send_json(self, data: Any) -> None:
class StaticFileApp (line 251) | class StaticFileApp:
method __call__ (line 255) | async def __call__(
class WebModuleApp (line 270) | class WebModuleApp:
method __call__ (line 274) | async def __call__(
class Error404App (line 289) | class Error404App:
method __call__ (line 290) | async def __call__(
FILE: src/reactpy/executors/asgi/pyscript.py
class ReactPyCsr (line 24) | class ReactPyCsr(ReactPy):
method __init__ (line 25) | def __init__(
method match_dispatch_path (line 83) | def match_dispatch_path(self, scope: AsgiWebsocketScope) -> bool: # n...
class ReactPyPyscriptApp (line 89) | class ReactPyPyscriptApp(ReactPyApp):
method render_index_html (line 97) | def render_index_html(self) -> None:
FILE: src/reactpy/executors/asgi/standalone.py
class ReactPy (line 39) | class ReactPy(ReactPyMiddleware):
method __init__ (line 42) | def __init__(
method match_dispatch_path (line 83) | def match_dispatch_path(self, scope: AsgiWebsocketScope) -> bool:
method match_extra_paths (line 87) | def match_extra_paths(self, scope: AsgiScope) -> AsgiApp | None:
method route (line 109) | def route(
method route (line 116) | def route(
method route (line 122) | def route(
method lifespan (line 157) | def lifespan(self, app: AsgiV3LifespanApp | str) -> None:
class ReactPyApp (line 173) | class ReactPyApp:
method __call__ (line 182) | async def __call__(
method render_index_html (line 230) | def render_index_html(self) -> None:
FILE: src/reactpy/executors/asgi/types.py
class AsgiV2Protocol (line 98) | class AsgiV2Protocol(Protocol):
method __init__ (line 102) | def __init__(self, scope: Any) -> None: ...
method __call__ (line 104) | async def __call__(self, receive: Any, send: Any) -> None: ...
FILE: src/reactpy/executors/pyscript/component_template.py
function user_workspace_UUID (line 9) | def user_workspace_UUID():
FILE: src/reactpy/executors/pyscript/components.py
function _pyscript_component (line 16) | def _pyscript_component(
function pyscript_component (line 40) | def pyscript_component(
FILE: src/reactpy/executors/pyscript/layout_handler.py
class ReactPyLayoutHandler (line 13) | class ReactPyLayoutHandler:
method __init__ (line 21) | def __init__(self, uuid):
method update_model (line 26) | def update_model(update, root_model):
method render_html (line 33) | def render_html(self, layout, model):
method build_element_tree (line 46) | def build_element_tree(self, layout, parent, model):
method create_event_handler (line 96) | def create_event_handler(self, layout, element, event_name, event_hand...
method delete_old_workspaces (line 118) | def delete_old_workspaces():
method run (line 150) | async def run(self, workspace_function):
FILE: src/reactpy/executors/pyscript/utils.py
function minify_python (line 28) | def minify_python(source: str) -> str:
function pyscript_executor_html (line 50) | def pyscript_executor_html(file_paths: Sequence[str], uuid: str, root: s...
function pyscript_component_html (line 75) | def pyscript_component_html(
function pyscript_setup_html (line 91) | def pyscript_setup_html(
function extend_pyscript_config (line 109) | def extend_pyscript_config(
function reactpy_version_string (line 143) | def reactpy_version_string() -> str: # nocov
function get_reactpy_versions (line 215) | def get_reactpy_versions() -> dict[Any, Any]:
function cached_file_read (line 234) | def cached_file_read(file_path: str, minifiy: bool = True) -> str:
FILE: src/reactpy/executors/utils.py
function import_components (line 21) | def import_components(dotted_paths: Iterable[str]) -> dict[str, Any]:
function check_path (line 28) | def check_path(url_path: str) -> str: # nocov
function vdom_head_to_html (line 42) | def vdom_head_to_html(head: VdomDict) -> str:
function process_settings (line 49) | def process_settings(settings: ReactPyConfig) -> None:
function server_side_component_html (line 62) | def server_side_component_html(
function default_import_map (line 95) | def default_import_map() -> str:
FILE: src/reactpy/logging.py
function _set_debug_level (line 31) | def _set_debug_level(debug: bool) -> None:
FILE: src/reactpy/reactjs/__init__.py
function component_from_url (line 36) | def component_from_url(
function component_from_url (line 48) | def component_from_url(
function component_from_url (line 59) | def component_from_url(
function component_from_npm (line 107) | def component_from_npm(
function component_from_npm (line 121) | def component_from_npm(
function component_from_npm (line 134) | def component_from_npm(
function component_from_file (line 193) | def component_from_file(
function component_from_file (line 207) | def component_from_file(
function component_from_file (line 220) | def component_from_file(
function component_from_string (line 277) | def component_from_string(
function component_from_string (line 290) | def component_from_string(
function component_from_string (line 302) | def component_from_string(
FILE: src/reactpy/reactjs/module.py
function url_to_module (line 22) | def url_to_module(
function file_to_module (line 43) | def file_to_module(
function string_to_module (line 85) | def string_to_module(
function module_to_vdom (line 121) | def module_to_vdom(
function make_module (line 163) | def make_module(
function import_reactjs (line 181) | def import_reactjs(
function module_name_suffix (line 256) | def module_name_suffix(name: str) -> str:
function get_module_path (line 264) | def get_module_path(name: str) -> Path:
FILE: src/reactpy/reactjs/utils.py
function resolve_names_from_file (line 16) | def resolve_names_from_file(
function resolve_names_from_url (line 46) | def resolve_names_from_url(
function resolve_names_from_source (line 71) | def resolve_names_from_source(
function normalize_url_path (line 132) | def normalize_url_path(base_url: str, rel_url: str) -> str:
function are_files_identical (line 152) | def are_files_identical(f1: Path, f2: Path) -> bool:
function copy_file (line 160) | def copy_file(target: Path, source: Path, symlink: bool) -> None:
function file_lock (line 197) | def file_lock(lock_file: Path, timeout: float = 10.0):
FILE: src/reactpy/templatetags/jinja.py
class ReactPyJinja (line 13) | class ReactPyJinja(StandaloneTag): # type: ignore
method render (line 17) | def render(self, *args: str, **kwargs: str) -> str:
function component (line 31) | def component(dotted_path: str, **kwargs: str) -> str:
function pyscript_component (line 40) | def pyscript_component(*file_paths: str, initial: str = "", root: str = ...
function pyscript_setup (line 44) | def pyscript_setup(*extra_py: str, extra_js: str = "", config: str = "")...
FILE: src/reactpy/testing/backend.py
class BackendFixture (line 28) | class BackendFixture:
method __init__ (line 44) | def __init__(
method url (line 73) | def url(self, path: str = "", query: Any | None = None) -> str:
method list_logged_exceptions (line 91) | def list_logged_exceptions(
method __aenter__ (line 113) | async def __aenter__(self) -> BackendFixture:
method __aexit__ (line 141) | async def __aexit__(
method restart (line 157) | async def restart(self) -> None:
function _hotswap (line 166) | def _hotswap(update_on_change: bool = False) -> tuple[_MountFunc, Compon...
FILE: src/reactpy/testing/common.py
class poll (line 26) | class poll(Generic[_R]): # noqa: N801
method __init__ (line 29) | def __init__(
method until (line 48) | async def until(
method until_is (line 66) | async def until_is(
method until_equals (line 80) | async def until_equals(
class HookCatcher (line 95) | class HookCatcher:
method __init__ (line 121) | def __init__(self, index_by_kwarg: str | None = None):
method capture (line 125) | def capture(self, render_function: Callable[..., Any]) -> Callable[......
class StaticEventHandler (line 149) | class StaticEventHandler:
method __init__ (line 191) | def __init__(self) -> None:
method use (line 194) | def use(
FILE: src/reactpy/testing/display.py
class DisplayFixture (line 21) | class DisplayFixture:
method __init__ (line 28) | def __init__(
method show (line 46) | async def show(
method goto (line 53) | async def goto(self, path: str, query: Any | None = None) -> None:
method __aenter__ (line 57) | async def __aenter__(self) -> DisplayFixture:
method configure_page (line 75) | async def configure_page(self) -> None:
method __aexit__ (line 95) | async def __aexit__(
function _playwright_visible (line 105) | def _playwright_visible(pytestconfig: pytest.Config | None = None) -> bool:
FILE: src/reactpy/testing/logs.py
class LogAssertionError (line 13) | class LogAssertionError(AssertionError):
function assert_reactpy_did_log (line 18) | def assert_reactpy_did_log(
function assert_reactpy_did_not_log (line 74) | def assert_reactpy_did_not_log(
function list_logged_exceptions (line 94) | def list_logged_exceptions(
function capture_reactpy_logs (line 125) | def capture_reactpy_logs() -> Iterator[list[logging.LogRecord]]:
class _LogRecordCaptor (line 152) | class _LogRecordCaptor(logging.NullHandler):
method __init__ (line 153) | def __init__(self) -> None:
method handle (line 157) | def handle(self, record: logging.LogRecord) -> bool:
function _raise_log_message_error (line 165) | def _raise_log_message_error(
FILE: src/reactpy/transforms.py
function attributes_to_reactjs (line 9) | def attributes_to_reactjs(attributes: VdomAttributes):
class RequiredTransforms (line 19) | class RequiredTransforms:
method __init__ (line 24) | def __init__(self, vdom: VdomDict, intercept_links: bool = True) -> None:
method normalize_style_attributes (line 33) | def normalize_style_attributes(self, vdom: dict[str, Any]) -> None:
method textarea_children_to_prop (line 50) | def textarea_children_to_prop(vdom: VdomDict) -> None:
method select_element_to_reactjs (line 63) | def select_element_to_reactjs(self, vdom: VdomDict) -> None:
method input_element_value_prop_to_defaultValue (line 84) | def input_element_value_prop_to_defaultValue(vdom: VdomDict) -> None:
method infer_key_from_attributes (line 99) | def infer_key_from_attributes(vdom: VdomDict) -> None:
method intercept_link_clicks (line 119) | def intercept_link_clicks(self, vdom: VdomDict) -> None:
method _parse_options (line 132) | def _parse_options(self, vdom_or_any: Any) -> list[str]:
method _kebab_to_camel_case (line 156) | def _kebab_to_camel_case(kebab_case: str) -> str:
FILE: src/reactpy/types.py
class State (line 27) | class State(NamedTuple, Generic[_Type]):
class Component (line 42) | class Component:
method __init__ (line 47) | def __init__(
method render (line 61) | def render(self) -> Component | VdomDict | str | None:
method __repr__ (line 64) | def __repr__(self) -> str:
class BaseLayout (line 81) | class BaseLayout(Protocol[_Render_co, _Event_contra]):
method render (line 95) | async def render(
method deliver (line 101) | async def deliver(self, event: _Event_contra) -> None:
method __aenter__ (line 105) | async def __aenter__(
method __aexit__ (line 111) | async def __aexit__(
class CssStyleTypeDict (line 121) | class CssStyleTypeDict(TypedDict, total=False):
class DangerouslySetInnerHTML (line 543) | class DangerouslySetInnerHTML(TypedDict):
class VdomTypeDict (line 817) | class VdomTypeDict(TypedDict):
class VdomDict (line 828) | class VdomDict(dict):
method __init__ (line 831) | def __init__(self, **kwargs: Unpack[VdomTypeDict]) -> None:
method __getitem__ (line 843) | def __getitem__(self, key: Literal["tagName"]) -> str: ...
method __getitem__ (line 845) | def __getitem__(
method __getitem__ (line 849) | def __getitem__(self, key: Literal["attributes"]) -> VdomAttributes: ...
method __getitem__ (line 851) | def __getitem__(self, key: Literal["eventHandlers"]) -> EventHandlerDi...
method __getitem__ (line 853) | def __getitem__(self, key: Literal["inlineJavaScript"]) -> InlineJavaS...
method __getitem__ (line 855) | def __getitem__(self, key: Literal["importSource"]) -> ImportSourceDic...
method __getitem__ (line 856) | def __getitem__(self, key: VdomDictKeys) -> Any:
method __setitem__ (line 860) | def __setitem__(self, key: Literal["tagName"], value: str) -> None: ...
method __setitem__ (line 862) | def __setitem__(
method __setitem__ (line 866) | def __setitem__(
method __setitem__ (line 870) | def __setitem__(
method __setitem__ (line 874) | def __setitem__(
method __setitem__ (line 878) | def __setitem__(
method __setitem__ (line 881) | def __setitem__(self, key: VdomDictKeys, value: Any) -> None:
class ImportSourceDict (line 894) | class ImportSourceDict(TypedDict):
class VdomJson (line 901) | class VdomJson(TypedDict):
class JsonEventTarget (line 914) | class JsonEventTarget(TypedDict):
class JsonImportSource (line 920) | class JsonImportSource(TypedDict):
class InlineJavaScript (line 925) | class InlineJavaScript(str):
class EventHandlerFunc (line 931) | class EventHandlerFunc(Protocol):
method __call__ (line 934) | async def __call__(self, data: Sequence[Any]) -> None: ...
class BaseEventHandler (line 937) | class BaseEventHandler:
class VdomConstructor (line 982) | class VdomConstructor(Protocol):
method __call__ (line 986) | def __call__(
method __call__ (line 991) | def __call__(self, *children: VdomChildren) -> VdomDict: ...
method __call__ (line 993) | def __call__(
class LayoutUpdateMessage (line 998) | class LayoutUpdateMessage(TypedDict):
class LayoutEventMessage (line 1009) | class LayoutEventMessage(TypedDict):
class Context (line 1020) | class Context(Protocol[_Type]):
method __call__ (line 1023) | def __call__(
class ContextProvider (line 1031) | class ContextProvider(Component, Generic[_Type]):
method __init__ (line 1032) | def __init__(
method render (line 1044) | def render(self) -> VdomDict:
method __repr__ (line 1050) | def __repr__(self) -> str:
class Connection (line 1055) | class Connection(Generic[CarrierType]):
class Location (line 1072) | class Location:
class ReactPyConfig (line 1090) | class ReactPyConfig(TypedDict, total=False):
class PyScriptOptions (line 1102) | class PyScriptOptions(TypedDict, total=False):
class CustomVdomConstructor (line 1108) | class CustomVdomConstructor(Protocol):
method __call__ (line 1109) | def __call__(
class EllipsisRepr (line 1117) | class EllipsisRepr:
method __repr__ (line 1118) | def __repr__(self) -> str:
class Event (line 1122) | class Event(dict):
method __getattr__ (line 1127) | def __getattr__(self, name: str) -> Any:
method preventDefault (line 1131) | def preventDefault(self) -> None:
method stopPropagation (line 1134) | def stopPropagation(self) -> None:
class JavaScriptModule (line 1142) | class JavaScriptModule:
FILE: src/reactpy/utils.py
class Ref (line 21) | class Ref(Generic[_RefValue]):
method __init__ (line 34) | def __init__(self, initial_value: _RefValue = _UNDEFINED) -> None:
method set_current (line 39) | def set_current(self, new: _RefValue) -> _RefValue:
method __eq__ (line 50) | def __eq__(self, other: object) -> bool:
method __repr__ (line 57) | def __repr__(self) -> str:
function reactpy_to_string (line 66) | def reactpy_to_string(root: VdomDict | Component) -> str:
function string_to_reactpy (line 84) | def string_to_reactpy(
class HTMLParseError (line 141) | class HTMLParseError(etree.LxmlSyntaxError): # type: ignore[misc]
function _etree_to_vdom (line 145) | def _etree_to_vdom(
function _add_vdom_to_etree (line 174) | def _add_vdom_to_etree(parent: etree._Element, vdom: VdomDict | dict[str...
function _generate_vdom_children (line 218) | def _generate_vdom_children(
function component_to_vdom (line 240) | def component_to_vdom(component: Component) -> VdomDict:
function _react_attribute_to_html (line 253) | def _react_attribute_to_html(key: str, value: Any) -> tuple[str, str]:
function import_dotted_path (line 284) | def import_dotted_path(dotted_path: str) -> Any:
class Singleton (line 304) | class Singleton:
method __new__ (line 307) | def __new__(cls, *args, **kw):
function str_to_bool (line 314) | def str_to_bool(s: str) -> bool:
FILE: src/reactpy/web/module.py
function module_from_url (line 28) | def module_from_url(
function module_from_file (line 50) | def module_from_file(
function module_from_string (line 76) | def module_from_string(
function export (line 101) | def export(
function export (line 110) | def export(
function export (line 118) | def export(
FILE: src/reactpy/widgets.py
function image (line 12) | def image(
function use_linked_inputs (line 34) | def use_linked_inputs(
class _CastFunc (line 79) | class _CastFunc(Protocol[_CastTo_co]):
method __call__ (line 80) | def __call__(self, value: str) -> _CastTo_co: ...
FILE: tests/conftest.py
function pytest_addoption (line 22) | def pytest_addoption(parser: Parser) -> None:
function display (line 32) | async def display(server, browser):
function server (line 38) | async def server():
function browser (line 44) | async def browser(pytestconfig: pytest.Config):
function assert_no_logged_exceptions (line 56) | def assert_no_logged_exceptions():
FILE: tests/sample.py
function SampleApp (line 8) | def SampleApp():
FILE: tests/test_asgi/pyscript_components/load_first.py
function root (line 10) | def root():
FILE: tests/test_asgi/pyscript_components/load_second.py
function child (line 5) | def child():
FILE: tests/test_asgi/pyscript_components/root.py
function root (line 5) | def root():
FILE: tests/test_asgi/test_init.py
function test_asgi_import_error (line 7) | def test_asgi_import_error():
FILE: tests/test_asgi/test_middleware.py
function display (line 20) | async def display(browser):
function test_invalid_path_prefix (line 39) | def test_invalid_path_prefix():
function test_invalid_web_modules_dir (line 48) | def test_invalid_web_modules_dir():
function test_unregistered_root_component (line 59) | async def test_unregistered_root_component(browser):
function test_display_simple_hello_world (line 95) | async def test_display_simple_hello_world(display: DisplayFixture):
function test_static_file_not_found (line 110) | async def test_static_file_not_found():
function test_templatetag_bad_kwargs (line 123) | async def test_templatetag_bad_kwargs(browser):
FILE: tests/test_asgi/test_pyscript.py
function display (line 17) | async def display(browser):
function multi_file_display (line 32) | async def multi_file_display(browser):
function jinja_display (line 46) | async def jinja_display(browser):
function test_root_component (line 65) | async def test_root_component(display: DisplayFixture):
function test_multi_file_components (line 81) | async def test_multi_file_components(multi_file_display: DisplayFixture):
function test_bad_file_path (line 96) | def test_bad_file_path():
function test_jinja_template_tag (line 101) | async def test_jinja_template_tag(jinja_display: DisplayFixture):
FILE: tests/test_asgi/test_standalone.py
function test_display_simple_hello_world (line 17) | async def test_display_simple_hello_world(display: DisplayFixture):
function test_display_simple_click_counter (line 32) | async def test_display_simple_click_counter(display: DisplayFixture):
function test_use_connection (line 53) | async def test_use_connection(display: DisplayFixture):
function test_use_scope (line 67) | async def test_use_scope(display: DisplayFixture):
function test_use_location (line 81) | async def test_use_location(display: DisplayFixture):
function test_carrier (line 110) | async def test_carrier(display: DisplayFixture):
function test_customized_head (line 126) | async def test_customized_head(browser):
function test_head_request (line 141) | async def test_head_request():
function test_custom_http_app (line 160) | async def test_custom_http_app():
function test_custom_websocket_app (line 197) | async def test_custom_websocket_app():
function test_custom_lifespan_app (line 233) | async def test_custom_lifespan_app():
FILE: tests/test_asgi/test_utils.py
function test_invalid_vdom_head (line 7) | def test_invalid_vdom_head():
function test_process_settings (line 12) | def test_process_settings():
function test_invalid_setting (line 19) | def test_invalid_setting():
FILE: tests/test_client.py
function test_automatic_reconnect (line 12) | async def test_automatic_reconnect(display: DisplayFixture, server: Back...
function test_style_can_be_changed (line 57) | async def test_style_can_be_changed(display: DisplayFixture):
function _get_style (line 94) | async def _get_style(element):
function test_slow_server_response_on_input_change (line 100) | async def test_slow_server_response_on_input_change(display: DisplayFixt...
FILE: tests/test_config.py
function reset_options (line 8) | def reset_options():
function test_reactpy_debug_toggle (line 26) | def test_reactpy_debug_toggle():
function test_boolean (line 32) | def test_boolean():
FILE: tests/test_console/test_rewrite_keys.py
function test_rewrite_key_declarations (line 10) | def test_rewrite_key_declarations(tmp_path):
function test_rewrite_key_declarations_no_files (line 25) | def test_rewrite_key_declarations_no_files():
function test_generate_rewrite (line 216) | def test_generate_rewrite(source, expected):
FILE: tests/test_console/test_rewrite_props.py
function test_rewrite_camel_case_props_declarations (line 13) | def test_rewrite_camel_case_props_declarations(tmp_path):
function test_rewrite_camel_case_props_declarations_no_files (line 28) | def test_rewrite_camel_case_props_declarations_no_files():
function test_generate_rewrite (line 109) | def test_generate_rewrite(source, expected):
FILE: tests/test_core/test_component.py
function test_component_repr (line 5) | def test_component_repr():
function test_simple_component (line 19) | async def test_simple_component():
function test_simple_parameterized_component (line 27) | async def test_simple_parameterized_component():
function test_component_with_var_args (line 35) | async def test_component_with_var_args():
function test_display_simple_hello_world (line 47) | async def test_display_simple_hello_world(display: DisplayFixture):
function test_pre_tags_are_rendered_correctly (line 57) | async def test_pre_tags_are_rendered_correctly(display: DisplayFixture):
FILE: tests/test_core/test_events.py
function test_event_handler_repr (line 20) | def test_event_handler_repr():
function test_event_handler_props (line 28) | def test_event_handler_props():
function test_event_handler_equivalence (line 50) | def test_event_handler_equivalence():
function test_to_event_handler_function (line 69) | async def test_to_event_handler_function():
function test_merge_event_handler_empty_list (line 91) | async def test_merge_event_handler_empty_list():
function test_merge_event_handlers_raises_on_mismatch (line 104) | async def test_merge_event_handlers_raises_on_mismatch(kwargs_1, kwargs_2):
function test_merge_event_handlers (line 117) | async def test_merge_event_handlers():
function test_merge_event_handler_funcs_empty_list (line 132) | def test_merge_event_handler_funcs_empty_list():
function test_merge_event_handler_funcs (line 137) | async def test_merge_event_handler_funcs():
function test_can_prevent_event_default_operation (line 153) | async def test_can_prevent_event_default_operation(display: DisplayFixtu...
function test_simple_click_event (line 170) | async def test_simple_click_event(display: DisplayFixture):
function test_can_stop_event_propagation (line 192) | async def test_can_stop_event_propagation(display: DisplayFixture):
function test_javascript_event_as_arrow_function (line 232) | async def test_javascript_event_as_arrow_function(display: DisplayFixture):
function test_javascript_event_as_this_statement (line 256) | async def test_javascript_event_as_this_statement(display: DisplayFixture):
function test_javascript_event_after_state_update (line 280) | async def test_javascript_event_after_state_update(display: DisplayFixtu...
function test_detect_prevent_default (line 326) | def test_detect_prevent_default():
function test_detect_stop_propagation (line 334) | def test_detect_stop_propagation():
function test_detect_both (line 342) | def test_detect_both():
function test_detect_both_when_handler_is_partial (line 352) | def test_detect_both_when_handler_is_partial():
function test_no_detect (line 362) | def test_no_detect():
function test_event_wrapper (line 371) | def test_event_wrapper():
function test_vdom_has_prevent_default (line 380) | async def test_vdom_has_prevent_default():
function test_event_export (line 396) | def test_event_export():
function test_detect_false_positive (line 402) | def test_detect_false_positive():
function test_detect_renamed_argument (line 414) | def test_detect_renamed_argument():
function test_event_queue_sequential_processing (line 424) | async def test_event_queue_sequential_processing(display: DisplayFixture):
function test_event_targeting_with_shifting_elements (line 460) | async def test_event_targeting_with_shifting_elements(display: DisplayFi...
function test_event_targeting_with_index_shifting (line 525) | async def test_event_targeting_with_index_shifting(display: DisplayFixtu...
FILE: tests/test_core/test_hooks.py
function test_must_be_rendering_in_layout_to_use_hooks (line 17) | async def test_must_be_rendering_in_layout_to_use_hooks():
function test_simple_stateful_component (line 30) | async def test_simple_stateful_component():
function test_set_state_callback_identity_is_preserved (line 75) | async def test_set_state_callback_identity_is_preserved():
function test_use_state_with_constructor (line 98) | async def test_use_state_with_constructor():
function test_set_state_with_reducer_instead_of_value (line 141) | async def test_set_state_with_reducer_instead_of_value():
function test_set_state_checks_equality_not_identity (line 162) | async def test_set_state_checks_equality_not_identity(display: DisplayFi...
function test_simple_input_with_use_state (line 230) | async def test_simple_input_with_use_state(display: DisplayFixture):
function test_double_set_state (line 256) | async def test_double_set_state(display: DisplayFixture):
function test_use_effect_callback_occurs_after_full_render_is_complete (line 298) | async def test_use_effect_callback_occurs_after_full_render_is_complete():
function test_use_effect_cleanup_occurs_before_next_effect (line 330) | async def test_use_effect_cleanup_occurs_before_next_effect():
function test_use_effect_cleanup_occurs_on_will_unmount (line 362) | async def test_use_effect_cleanup_occurs_on_will_unmount():
function test_memoized_effect_on_recreated_if_dependencies_change (line 401) | async def test_memoized_effect_on_recreated_if_dependencies_change():
function test_memoized_effect_cleanup_only_triggered_before_new_effect (line 441) | async def test_memoized_effect_cleanup_only_triggered_before_new_effect():
function test_memoized_async_effect_cleanup_only_triggered_before_new_effect (line 479) | async def test_memoized_async_effect_cleanup_only_triggered_before_new_e...
function test_use_async_effect (line 522) | async def test_use_async_effect():
function test_use_async_effect_cleanup (line 538) | async def test_use_async_effect_cleanup():
function test_use_async_effect_cancel (line 564) | async def test_use_async_effect_cancel():
function test_async_effect_sleep_is_cancelled_on_re_render (line 603) | async def test_async_effect_sleep_is_cancelled_on_re_render():
function test_error_in_effect_is_gracefully_handled (line 638) | async def test_error_in_effect_is_gracefully_handled():
function test_error_in_effect_pre_unmount_cleanup_is_gracefully_handled (line 653) | async def test_error_in_effect_pre_unmount_cleanup_is_gracefully_handled():
function test_use_reducer (line 683) | async def test_use_reducer():
function test_use_reducer_dispatch_callback_identity_is_preserved (line 719) | async def test_use_reducer_dispatch_callback_identity_is_preserved():
function test_use_callback_identity (line 744) | async def test_use_callback_identity():
function test_use_callback_memoization (line 763) | async def test_use_callback_memoization():
function test_use_memo (line 794) | async def test_use_memo():
function test_use_memo_always_runs_if_dependencies_are_none (line 824) | async def test_use_memo_always_runs_if_dependencies_are_none():
function test_use_memo_with_stored_deps_is_empty_tuple_after_deps_are_none (line 847) | async def test_use_memo_with_stored_deps_is_empty_tuple_after_deps_are_n...
function test_use_memo_never_runs_if_deps_is_empty_list (line 876) | async def test_use_memo_never_runs_if_deps_is_empty_list():
function test_use_ref (line 899) | async def test_use_ref():
function test_bad_schedule_render_callback (line 918) | def test_bad_schedule_render_callback():
function test_use_effect_automatically_infers_closure_values (line 929) | async def test_use_effect_automatically_infers_closure_values():
function test_use_memo_automatically_infers_closure_values (line 957) | async def test_use_memo_automatically_infers_closure_values():
function test_use_context_default_value (line 985) | async def test_use_context_default_value():
function test_context_repr (line 1012) | def test_context_repr():
function test_use_context_updates_components_even_if_memoized (line 1017) | async def test_use_context_updates_components_even_if_memoized():
function test_context_values_are_scoped (line 1057) | async def test_context_values_are_scoped():
function test_error_in_layout_effect_cleanup_is_gracefully_handled (line 1079) | async def test_error_in_layout_effect_cleanup_is_gracefully_handled():
function test_set_state_during_render (line 1103) | async def test_set_state_during_render():
function test_use_debug_mode (line 1128) | async def test_use_debug_mode():
function test_use_debug_mode_with_factory (line 1155) | async def test_use_debug_mode_with_factory():
function test_use_debug_mode_does_not_log_if_not_in_debug_mode (line 1182) | async def test_use_debug_mode_does_not_log_if_not_in_debug_mode():
function test_conditionally_rendered_components_can_use_context (line 1201) | async def test_conditionally_rendered_components_can_use_context():
function test_strictly_equal (line 1249) | def test_strictly_equal(x, y, result):
function test_strictly_equal_named_closures (line 1253) | def test_strictly_equal_named_closures():
function test_use_state_compares_with_strict_equality (line 1287) | async def test_use_state_compares_with_strict_equality(get_value):
function test_use_effect_compares_with_strict_equality (line 1305) | async def test_use_effect_compares_with_strict_equality(get_value):
function test_use_state_named_tuple (line 1327) | async def test_use_state_named_tuple():
function test_error_in_component_effect_cleanup_is_gracefully_handled (line 1342) | async def test_error_in_component_effect_cleanup_is_gracefully_handled():
function test_use_effect_exception_on_async_function (line 1368) | def test_use_effect_exception_on_async_function():
function test_async_effect_cancelled_on_dependency_change (line 1389) | async def test_async_effect_cancelled_on_dependency_change():
FILE: tests/test_core/test_layout.py
function async_rendering (line 37) | def async_rendering(request):
function no_logged_errors (line 43) | def no_logged_errors():
function test_layout_repr (line 51) | def test_layout_repr():
function test_layout_expects_abstract_component (line 60) | def test_layout_expects_abstract_component():
function test_layout_cannot_be_used_outside_context_manager (line 67) | async def test_layout_cannot_be_used_outside_context_manager():
function test_simple_layout (line 81) | async def test_simple_layout():
function test_nested_component_layout (line 105) | async def test_nested_component_layout():
function test_layout_render_error_has_partial_update_with_error_message (line 164) | async def test_layout_render_error_has_partial_update_with_error_message():
function test_layout_render_error_has_partial_update_without_error_message (line 215) | async def test_layout_render_error_has_partial_update_without_error_mess...
function test_render_raw_vdom_dict_with_single_component_object_as_children (line 259) | async def test_render_raw_vdom_dict_with_single_component_object_as_chil...
function test_components_are_garbage_collected (line 293) | async def test_components_are_garbage_collected():
function test_root_component_life_cycle_hook_is_garbage_collected (line 342) | async def test_root_component_life_cycle_hook_is_garbage_collected():
function test_life_cycle_hooks_are_garbage_collected (line 373) | async def test_life_cycle_hooks_are_garbage_collected():
function test_double_updated_component_is_not_double_rendered (line 429) | async def test_double_updated_component_is_not_double_rendered():
function test_update_path_to_component_that_is_not_direct_child_is_correct (line 457) | async def test_update_path_to_component_that_is_not_direct_child_is_corr...
function test_log_on_dispatch_to_missing_event_handler (line 478) | async def test_log_on_dispatch_to_missing_event_handler(caplog):
function test_model_key_preserves_callback_identity_for_common_elements (line 494) | async def test_model_key_preserves_callback_identity_for_common_elements...
function test_model_key_preserves_callback_identity_for_components (line 542) | async def test_model_key_preserves_callback_identity_for_components():
function test_component_can_return_another_component_directly (line 592) | async def test_component_can_return_another_component_directly():
function test_hooks_for_keyed_components_get_garbage_collected (line 616) | async def test_hooks_for_keyed_components_get_garbage_collected():
function test_event_handler_at_component_root_is_garbage_collected (line 651) | async def test_event_handler_at_component_root_is_garbage_collected():
function test_event_handler_deep_in_component_layout_is_garbage_collected (line 673) | async def test_event_handler_deep_in_component_layout_is_garbage_collect...
function test_duplicate_sibling_keys_causes_error (line 695) | async def test_duplicate_sibling_keys_causes_error():
function test_keyed_components_preserve_hook_on_parent_update (line 731) | async def test_keyed_components_preserve_hook_on_parent_update():
function test_log_error_on_bad_event_handler (line 754) | async def test_log_error_on_bad_event_handler():
function test_schedule_render_from_unmounted_hook (line 773) | async def test_schedule_render_from_unmounted_hook():
function test_elements_and_components_with_the_same_key_can_be_interchanged (line 810) | async def test_elements_and_components_with_the_same_key_can_be_intercha...
function test_layout_does_not_copy_element_children_by_key (line 849) | async def test_layout_does_not_copy_element_children_by_key():
function test_changing_key_of_parent_element_unmounts_children (line 884) | async def test_changing_key_of_parent_element_unmounts_children():
function test_switching_node_type_with_event_handlers (line 910) | async def test_switching_node_type_with_event_handlers():
function test_switching_component_definition (line 948) | async def test_switching_component_definition():
function test_element_keys_inside_components_do_not_reset_state_of_component (line 994) | async def test_element_keys_inside_components_do_not_reset_state_of_comp...
function test_changing_key_of_component_resets_state (line 1044) | async def test_changing_key_of_component_resets_state():
function test_changing_event_handlers_in_the_next_render (line 1072) | async def test_changing_event_handlers_in_the_next_render():
function test_change_element_to_string_causes_unmount (line 1097) | async def test_change_element_to_string_causes_unmount():
function test_does_render_children_after_component (line 1123) | async def test_does_render_children_after_component():
function test_render_removed_context_consumer (line 1162) | async def test_render_removed_context_consumer():
function test_ensure_model_path_udpates (line 1201) | async def test_ensure_model_path_udpates():
function test_async_renders (line 1257) | async def test_async_renders(async_rendering):
function test_none_does_not_render (line 1306) | async def test_none_does_not_render():
function test_conditionally_render_none_does_not_trigger_state_change_in_siblings (line 1325) | async def test_conditionally_render_none_does_not_trigger_state_change_i...
function test_deduplicate_async_renders (line 1348) | async def test_deduplicate_async_renders():
function test_deduplicate_async_renders_nested (line 1400) | async def test_deduplicate_async_renders_nested():
function test_deduplicate_async_renders_rapid (line 1495) | async def test_deduplicate_async_renders_rapid():
function test_event_handler_retry_logic (line 1523) | async def test_event_handler_retry_logic():
FILE: tests/test_core/test_serve.py
function make_send_recv_callbacks (line 22) | def make_send_recv_callbacks(events_to_inject):
function make_events_and_expected_model (line 49) | def make_events_and_expected_model():
function assert_changes_produce_expected_model (line 70) | def assert_changes_produce_expected_model(
function Counter (line 86) | def Counter():
function test_dispatch (line 96) | async def test_dispatch():
function test_dispatcher_handles_more_than_one_event_at_a_time (line 104) | async def test_dispatcher_handles_more_than_one_event_at_a_time():
FILE: tests/test_core/test_vdom.py
function test_is_vdom (line 27) | def test_is_vdom(result, value):
function test_simple_node_construction (line 87) | def test_simple_node_construction(actual, expected):
function test_callable_attributes_are_cast_to_event_handlers (line 91) | async def test_callable_attributes_are_cast_to_event_handlers():
function test_make_vdom_constructor (line 109) | def test_make_vdom_constructor():
function test_nested_html_access_raises_error (line 126) | def test_nested_html_access_raises_error():
function test_valid_vdom (line 196) | def test_valid_vdom(value):
function test_invalid_vdom (line 297) | def test_invalid_vdom(value, error_message_pattern):
function test_warn_cannot_verify_keypath_for_genereators (line 303) | def test_warn_cannot_verify_keypath_for_genereators():
function test_warn_dynamic_children_must_have_keys (line 315) | def test_warn_dynamic_children_must_have_keys():
function test_raise_for_non_json_attrs (line 332) | def test_raise_for_non_json_attrs():
function test_invalid_vdom_keys (line 337) | def test_invalid_vdom_keys():
FILE: tests/test_html.py
function test_script_re_run_on_content_change (line 11) | async def test_script_re_run_on_content_change(display: DisplayFixture):
function test_script_from_src (line 48) | async def test_script_from_src(display: DisplayFixture):
function test_script_may_only_have_one_child (line 89) | def test_script_may_only_have_one_child():
function test_child_of_script_must_be_string (line 96) | def test_child_of_script_must_be_string():
function test_script_has_no_event_handlers (line 101) | def test_script_has_no_event_handlers():
function test_simple_fragment (line 106) | def test_simple_fragment():
function test_fragment_can_have_no_attributes (line 120) | def test_fragment_can_have_no_attributes():
function test_svg (line 125) | async def test_svg(display: DisplayFixture):
FILE: tests/test_option.py
function test_option_repr (line 9) | def test_option_repr():
function test_option_from_os_environ (line 16) | def test_option_from_os_environ():
function test_option_from_default (line 21) | def test_option_from_default():
function test_option_validator (line 28) | def test_option_validator():
function test_immutable_option (line 40) | def test_immutable_option():
function test_option_reset (line 49) | def test_option_reset():
function test_option_reload (line 58) | def test_option_reload():
function test_option_set (line 65) | def test_option_set():
function test_option_set_default (line 72) | def test_option_set_default():
function test_cannot_subscribe_immutable_option (line 79) | def test_cannot_subscribe_immutable_option():
function test_option_subscribe (line 85) | def test_option_subscribe():
function test_deprecated_option (line 104) | def test_deprecated_option():
function test_option_parent (line 114) | def test_option_parent():
function test_option_parent_child_must_be_mutable (line 124) | def test_option_parent_child_must_be_mutable():
function test_no_default_or_parent (line 133) | def test_no_default_or_parent():
FILE: tests/test_pyscript/pyscript_components/custom_root_name.py
function custom (line 5) | def custom():
FILE: tests/test_pyscript/pyscript_components/root.py
function root (line 5) | def root():
FILE: tests/test_pyscript/test_components.py
function display (line 13) | async def display(browser):
function test_pyscript_component (line 22) | async def test_pyscript_component(display: DisplayFixture):
function test_custom_root_name (line 45) | async def test_custom_root_name(display: DisplayFixture):
function test_bad_file_path (line 69) | def test_bad_file_path():
FILE: tests/test_pyscript/test_utils.py
function test_bad_root_name (line 12) | def test_bad_root_name():
function test_extend_pyscript_config (line 21) | def test_extend_pyscript_config():
function test_extend_pyscript_config_string_values (line 42) | def test_extend_pyscript_config_string_values():
function test_get_reactpy_versions_https_fail_http_success (line 64) | def test_get_reactpy_versions_https_fail_http_success():
function test_get_reactpy_versions_all_fail (line 96) | def test_get_reactpy_versions_all_fail():
FILE: tests/test_reactjs/js_fixtures/callable-prop.js
function bind (line 6) | function bind(node, config) {
function Component (line 14) | function Component(props) {
FILE: tests/test_reactjs/js_fixtures/component-can-have-child.js
function bind (line 6) | function bind(node, config) {
function Parent (line 16) | function Parent(props) {
function Child (line 25) | function Child({ index }) {
FILE: tests/test_reactjs/js_fixtures/exports-syntax.js
function functionName (line 6) | function functionName(){...}
class ClassName (line 7) | class ClassName {...}
FILE: tests/test_reactjs/js_fixtures/exports-two-components.js
function bind (line 6) | function bind(node, config) {
function Header1 (line 14) | function Header1(props) {
function Header2 (line 18) | function Header2(props) {
FILE: tests/test_reactjs/js_fixtures/generic-module.js
function GenericComponent (line 3) | function GenericComponent(props) {
FILE: tests/test_reactjs/js_fixtures/keys-properly-propagated.js
function bind (line 6) | function bind(node, config) {
FILE: tests/test_reactjs/js_fixtures/nest-custom-under-web.js
function bind (line 6) | function bind(node, config) {
FILE: tests/test_reactjs/js_fixtures/set-flag-when-unmount-is-called.js
function bind (line 1) | function bind(node, config) {
function renderElement (line 9) | function renderElement(element, container) {
function unmountElement (line 16) | function unmountElement(container) {
function SomeComponent (line 26) | function SomeComponent(props) {
FILE: tests/test_reactjs/js_fixtures/simple-button.js
function bind (line 6) | function bind(node, config) {
function SimpleButton (line 14) | function SimpleButton(props) {
FILE: tests/test_reactjs/test_modules.py
function display (line 14) | async def display(browser):
function test_nested_client_side_components (line 21) | async def test_nested_client_side_components(display: DisplayFixture):
function test_interleaved_client_server_components (line 60) | async def test_interleaved_client_server_components(display: DisplayFixt...
function test_nest_custom_component_under_web_component (line 98) | async def test_nest_custom_component_under_web_component(display: Displa...
FILE: tests/test_reactjs/test_modules_from_npm.py
function display (line 13) | async def display(browser):
function test_component_from_npm_react_bootstrap (line 20) | async def test_component_from_npm_react_bootstrap(display: DisplayFixtur...
function test_component_from_npm_react_bootstrap_with_local_framework (line 41) | async def test_component_from_npm_react_bootstrap_with_local_framework(b...
function test_component_from_npm_without_explicit_reactjs_import (line 66) | async def test_component_from_npm_without_explicit_reactjs_import(browse...
function test_component_from_npm_material_ui (line 89) | async def test_component_from_npm_material_ui(display: DisplayFixture):
function test_component_from_npm_antd (line 104) | async def test_component_from_npm_antd(display: DisplayFixture):
function test_component_from_npm_chakra_ui (line 117) | async def test_component_from_npm_chakra_ui(display: DisplayFixture):
function test_component_from_npm_semantic_ui_react (line 132) | async def test_component_from_npm_semantic_ui_react(browser):
function test_component_from_npm_mantine (line 152) | async def test_component_from_npm_mantine(display: DisplayFixture):
function test_component_from_npm_fluent_ui (line 167) | async def test_component_from_npm_fluent_ui(display: DisplayFixture):
function test_component_from_npm_blueprint (line 180) | async def test_component_from_npm_blueprint(display: DisplayFixture):
function test_component_from_npm_grommet (line 193) | async def test_component_from_npm_grommet(display: DisplayFixture):
function test_component_from_npm_evergreen (line 207) | async def test_component_from_npm_evergreen(display: DisplayFixture):
function test_component_from_npm_react_spinners (line 219) | async def test_component_from_npm_react_spinners(display: DisplayFixture):
function test_nested_npm_components (line 241) | async def test_nested_npm_components(display: DisplayFixture):
function test_interleaved_npm_and_server_components (line 272) | async def test_interleaved_npm_and_server_components(display: DisplayFix...
function test_complex_nested_material_ui (line 304) | async def test_complex_nested_material_ui(display: DisplayFixture):
function _get_chakra_components (line 386) | def _get_chakra_components():
FILE: tests/test_reactjs/test_utils.py
function test_module_name_suffix (line 34) | def test_module_name_suffix(name, suffix):
function test_resolve_module_exports_from_file (line 39) | def test_resolve_module_exports_from_file():
function test_resolve_module_exports_from_file_log_on_max_depth (line 54) | def test_resolve_module_exports_from_file_log_on_max_depth(caplog):
function test_resolve_module_exports_from_file_log_on_unknown_file_location (line 67) | def test_resolve_module_exports_from_file_log_on_unknown_file_location(
function test_resolve_module_exports_from_url (line 80) | def test_resolve_module_exports_from_url():
function test_resolve_module_exports_from_url_log_on_max_depth (line 110) | def test_resolve_module_exports_from_url_log_on_max_depth(caplog):
function test_resolve_module_exports_from_url_log_on_bad_response (line 116) | def test_resolve_module_exports_from_url_log_on_bad_response(caplog):
function test_resolve_module_default_exports_from_source (line 133) | def test_resolve_module_default_exports_from_source(text):
function test_resolve_module_exports_from_source (line 138) | def test_resolve_module_exports_from_source():
function test_log_on_unknown_export_type (line 152) | def test_log_on_unknown_export_type():
function test_resolve_relative_url (line 159) | def test_resolve_relative_url():
function test_copy_file_fallback (line 171) | def test_copy_file_fallback(tmp_path):
function test_simple_file_lock_timeout (line 193) | def test_simple_file_lock_timeout(tmp_path):
FILE: tests/test_sample.py
function test_sample_app (line 5) | async def test_sample_app(display: DisplayFixture):
FILE: tests/test_testing.py
function test_assert_reactpy_logged_does_not_suppress_errors (line 14) | def test_assert_reactpy_logged_does_not_suppress_errors():
function test_assert_reactpy_logged_message (line 21) | def test_assert_reactpy_logged_message():
function test_assert_reactpy_logged_error (line 29) | def test_assert_reactpy_logged_error():
function test_assert_reactpy_logged_assertion_error_message (line 90) | def test_assert_reactpy_logged_assertion_error_message():
function test_assert_reactpy_logged_ignores_level (line 104) | def test_assert_reactpy_logged_ignores_level():
function test_assert_reactpy_did_not_log (line 115) | def test_assert_reactpy_did_not_log():
function test_simple_display_fixture (line 139) | async def test_simple_display_fixture(browser):
function test_list_logged_exceptions (line 145) | def test_list_logged_exceptions():
function test_hotswap_update_on_change (line 161) | async def test_hotswap_update_on_change(display: DisplayFixture):
function test_backend_server_failure (line 212) | async def test_backend_server_failure():
function test_display_fixture_headless_logic (line 232) | async def test_display_fixture_headless_logic():
function test_display_fixture_internal_backend (line 266) | async def test_display_fixture_internal_backend():
FILE: tests/test_utils.py
function test_basic_ref_behavior (line 9) | def test_basic_ref_behavior():
function test_ref_equivalence (line 27) | def test_ref_equivalence():
function test_ref_repr (line 35) | def test_ref_repr():
function test_string_to_reactpy (line 91) | def test_string_to_reactpy(case):
function test_string_to_reactpy_empty_source (line 96) | def test_string_to_reactpy_empty_source(source):
function test_string_to_reactpy_non_string_source (line 101) | def test_string_to_reactpy_non_string_source(source):
function test_string_to_reactpy_missing_tags (line 107) | def test_string_to_reactpy_missing_tags(source):
function test_string_to_reactpy_default_transforms (line 222) | def test_string_to_reactpy_default_transforms(case):
function test_string_to_reactpy_intercept_links (line 226) | def test_string_to_reactpy_intercept_links():
function test_string_to_reactpy_custom_transform (line 243) | def test_string_to_reactpy_custom_transform():
function test_non_html_tag_behavior (line 276) | def test_non_html_tag_behavior():
class StableReprObject (line 295) | class StableReprObject:
method __repr__ (line 296) | def __repr__(self):
function example_parent (line 304) | def example_parent():
function example_middle (line 309) | def example_middle():
function example_child (line 314) | def example_child():
function example_str_return (line 319) | def example_str_return():
function example_none_return (line 324) | def example_none_return():
function test_reactpy_to_string (line 408) | def test_reactpy_to_string(vdom_in, html_out):
function test_reactpy_to_string_error (line 412) | def test_reactpy_to_string_error():
function test_invalid_dotted_path (line 417) | def test_invalid_dotted_path():
function test_invalid_component (line 422) | def test_invalid_component():
function test_invalid_module (line 429) | def test_invalid_module():
FILE: tests/test_web/js_fixtures/callable-prop.js
function bind (line 6) | function bind(node, config) {
function Component (line 14) | function Component(props) {
FILE: tests/test_web/js_fixtures/component-can-have-child.js
function bind (line 6) | function bind(node, config) {
function Parent (line 16) | function Parent(props) {
function Child (line 25) | function Child({ index }) {
FILE: tests/test_web/js_fixtures/exports-syntax.js
function functionName (line 6) | function functionName(){...}
class ClassName (line 7) | class ClassName {...}
FILE: tests/test_web/js_fixtures/exports-two-components.js
function bind (line 6) | function bind(node, config) {
function Header1 (line 14) | function Header1(props) {
function Header2 (line 18) | function Header2(props) {
FILE: tests/test_web/js_fixtures/generic-module.js
function GenericComponent (line 3) | function GenericComponent(props) {
FILE: tests/test_web/js_fixtures/keys-properly-propagated.js
function bind (line 6) | function bind(node, config) {
FILE: tests/test_web/js_fixtures/set-flag-when-unmount-is-called.js
function bind (line 1) | function bind(node, config) {
function renderElement (line 9) | function renderElement(element, container) {
function unmountElement (line 16) | function unmountElement(container) {
function SomeComponent (line 26) | function SomeComponent(props) {
FILE: tests/test_web/js_fixtures/simple-button.js
function bind (line 6) | function bind(node, config) {
function SimpleButton (line 14) | function SimpleButton(props) {
FILE: tests/test_web/test_module.py
function display (line 28) | async def display(browser):
function test_that_js_module_unmount_is_called (line 35) | async def test_that_js_module_unmount_is_called(display: DisplayFixture):
function test_module_from_url (line 68) | async def test_module_from_url(browser):
function test_module_from_file (line 90) | async def test_module_from_file(display: DisplayFixture):
function test_module_from_file_source_conflict (line 113) | def test_module_from_file_source_conflict(tmp_path):
function test_web_module_from_file_symlink (line 140) | def test_web_module_from_file_symlink(tmp_path):
function test_web_module_from_file_symlink_twice (line 155) | def test_web_module_from_file_symlink_twice(tmp_path):
function test_web_module_from_file_replace_existing (line 178) | def test_web_module_from_file_replace_existing(tmp_path):
function test_module_missing_exports (line 193) | def test_module_missing_exports():
function test_module_exports_multiple_components (line 203) | async def test_module_exports_multiple_components(display: DisplayFixture):
function test_imported_components_can_render_children (line 220) | async def test_imported_components_can_render_children(display: DisplayF...
function test_keys_properly_propagated (line 243) | async def test_keys_properly_propagated(display: DisplayFixture):
function test_subcomponent_notation_as_str_attrs (line 306) | async def test_subcomponent_notation_as_str_attrs(display: DisplayFixture):
function test_subcomponent_notation_as_obj_attrs (line 365) | async def test_subcomponent_notation_as_obj_attrs(display: DisplayFixture):
function test_callable_prop_with_javacript (line 423) | async def test_callable_prop_with_javacript(display: DisplayFixture):
function test_component_from_string (line 444) | def test_component_from_string():
function test_component_from_url (line 455) | async def test_component_from_url(browser):
function test_component_from_file (line 474) | async def test_component_from_file(display: DisplayFixture):
function test_component_from_url_caching (line 494) | def test_component_from_url_caching():
function test_component_from_file_caching (line 511) | def test_component_from_file_caching(tmp_path):
function test_component_from_string_caching (line 527) | def test_component_from_string_caching():
function test_component_from_string_with_no_name (line 542) | def test_component_from_string_with_no_name():
function test_module_without_bind (line 553) | async def test_module_without_bind(display: DisplayFixture):
FILE: tests/test_widgets.py
function test_image_from_string (line 19) | async def test_image_from_string(display: DisplayFixture):
function test_image_from_bytes (line 26) | async def test_image_from_bytes(display: DisplayFixture):
function test_use_linked_inputs (line 33) | async def test_use_linked_inputs(display: DisplayFixture):
function test_use_linked_inputs_on_change (line 56) | async def test_use_linked_inputs_on_change(display: DisplayFixture):
function test_use_linked_inputs_on_change_with_cast (line 84) | async def test_use_linked_inputs_on_change_with_cast(display: DisplayFix...
function test_use_linked_inputs_ignore_empty (line 111) | async def test_use_linked_inputs_ignore_empty(display: DisplayFixture):
FILE: tests/tooling/aio.py
class Event (line 9) | class Event(_Event):
method wait (line 12) | async def wait(self, timeout: float | None = None):
FILE: tests/tooling/common.py
function event_message (line 9) | def event_message(target: str, *data: Any) -> LayoutEventMessage:
function update_message (line 13) | def update_message(path: str, model: Any) -> LayoutUpdateMessage:
FILE: tests/tooling/hooks.py
function use_force_render (line 4) | def use_force_render():
function use_toggle (line 8) | def use_toggle(init=False):
function use_counter (line 13) | def use_counter(initial_value):
FILE: tests/tooling/layout.py
function layout_runner (line 18) | async def layout_runner(layout: Layout) -> AsyncIterator[LayoutRunner]:
class LayoutRunner (line 23) | class LayoutRunner:
method __init__ (line 24) | def __init__(self, layout: Layout) -> None:
method render (line 28) | async def render(self) -> VdomJson:
method trigger (line 39) | async def trigger(self, element: VdomJson, event_name: str, *data: Any...
FILE: tests/tooling/select.py
function id_equals (line 11) | def id_equals(id: str) -> Selector:
function class_equals (line 15) | def class_equals(class_name: str) -> Selector:
function text_equals (line 22) | def text_equals(text: str) -> Selector:
function _element_text (line 26) | def _element_text(element: VdomJson) -> str:
function element_exists (line 32) | def element_exists(element: VdomJson, selector: Selector) -> bool:
function find_element (line 36) | def find_element(
function find_elements (line 69) | def find_elements(
function _find_elements (line 86) | def _find_elements(
class ElementInfo (line 104) | class ElementInfo:
Condensed preview — 339 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,086K chars).
[
{
"path": ".editorconfig",
"chars": 488,
"preview": "# http://editorconfig.org\n\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_traili"
},
{
"path": ".github/CODEOWNERS",
"chars": 13,
"preview": "@maintainers\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 656,
"preview": "# These are supported funding model platforms\n\ngithub: [archmonger, rmorshea]\npatreon: # Replace with a single Patreon u"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 211,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Start a Discussion\n url: https://github.com/reactive-python/reac"
},
{
"path": ".github/ISSUE_TEMPLATE/issue-form.yml",
"chars": 557,
"preview": "name: Plan a Task\ndescription: Create a detailed plan of action (ONLY START AFTER DISCUSSION PLEASE 🙏).\nlabels: [\"flag-t"
},
{
"path": ".github/copilot-instructions.md",
"chars": 15143,
"preview": "# ReactPy Development Instructions\n\nReactPy is a Python library for building user interfaces without JavaScript. It crea"
},
{
"path": ".github/pull_request_template.md",
"chars": 485,
"preview": "## Description\n\n<!-- A summary of the changes. -->\n\n## Checklist\n\nPlease update this checklist as you complete each item"
},
{
"path": ".github/workflows/.hatch-run.yml",
"chars": 2300,
"preview": "name: hatch-run\n\non:\n workflow_call:\n inputs:\n job-name:\n required: true\n "
},
{
"path": ".github/workflows/check.yml",
"chars": 1648,
"preview": "name: check\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - \"*\"\n "
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 2488,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/publish.yml",
"chars": 1333,
"preview": "name: publish\n\non:\n release:\n types: [published]\n\npermissions:\n contents: read # Required to checkout th"
},
{
"path": ".gitignore",
"chars": 585,
"preview": "# --- Build Artifacts ---\nsrc/reactpy/static/*.js*\nsrc/reactpy/static/morphdom/\nsrc/reactpy/static/pyscript/\nsrc/js/**/*"
},
{
"path": ".pre-commit-config.yaml",
"chars": 821,
"preview": "repos:\n - repo: local\n hooks:\n - id: lint-py-fix\n name: Fix Python Lint\n entry: hatch run lint-py"
},
{
"path": ".prettierrc",
"chars": 76,
"preview": "{\n \"proseWrap\": \"never\",\n \"trailingComma\": \"all\",\n \"endOfLine\": \"auto\"\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 14027,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3355,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "LICENSE",
"chars": 1093,
"preview": "The MIT License (MIT)\n\nCopyright (c) Reactive Python and affiliates.\n\nPermission is hereby granted, free of charge, to a"
},
{
"path": "README.md",
"chars": 3073,
"preview": "# <img src=\"https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg\" align=\""
},
{
"path": "docs/.gitignore",
"chars": 73,
"preview": "build\nsource/_auto\nsource/_static/custom.js\nsource/vdom-json-schema.json\n"
},
{
"path": "docs/Dockerfile",
"chars": 809,
"preview": "FROM python:3.11\nWORKDIR /app/\n\nRUN apt-get update\n\n# Create/Activate Python Venv\n# ---------------------------\nENV VIRT"
},
{
"path": "docs/README.md",
"chars": 31,
"preview": "# ReactPy's Documentation\n\n...\n"
},
{
"path": "docs/docs_app/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "docs/docs_app/app.py",
"chars": 1284,
"preview": "from logging import getLogger\nfrom pathlib import Path\n\nfrom sanic import Sanic, response\n\nfrom docs_app.examples import"
},
{
"path": "docs/docs_app/dev.py",
"chars": 2884,
"preview": "import asyncio\nimport os\nimport threading\nimport time\nimport webbrowser\n\nfrom sphinx_autobuild.cli import (\n Server,\n"
},
{
"path": "docs/docs_app/examples.py",
"chars": 5079,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Iterator\nfrom io import StringIO\nfrom pathlib "
},
{
"path": "docs/docs_app/prod.py",
"chars": 323,
"preview": "import os\n\nfrom docs_app.app import make_app\n\napp = make_app(\"docs_prod_app\")\n\n\ndef main() -> None:\n app.run(\n "
},
{
"path": "docs/main.py",
"chars": 147,
"preview": "import sys\n\nfrom docs_app import dev, prod\n\nif __name__ == \"__main__\":\n if len(sys.argv) == 1:\n prod.main()\n "
},
{
"path": "docs/pyproject.toml",
"chars": 548,
"preview": "[tool.poetry]\nname = \"docs_app\"\nversion = \"0.0.0\"\ndescription = \"docs\"\nauthors = [\"rmorshea <ryan.morshead@gmail.com>\"]\n"
},
{
"path": "docs/source/_custom_js/README.md",
"chars": 153,
"preview": "# Custom Javascript for ReactPy's Docs\n\nBuild the javascript with\n\n```\nbun run build\n```\n\nThis will drop a javascript bu"
},
{
"path": "docs/source/_custom_js/package.json",
"chars": 575,
"preview": "{\n \"name\": \"reactpy-docs-example-loader\",\n \"version\": \"1.0.0\",\n \"description\": \"simple javascript client for ReactPy'"
},
{
"path": "docs/source/_custom_js/rollup.config.js",
"chars": 639,
"preview": "import resolve from \"@rollup/plugin-node-resolve\";\nimport commonjs from \"@rollup/plugin-commonjs\";\nimport replace from \""
},
{
"path": "docs/source/_custom_js/src/index.js",
"chars": 2403,
"preview": "import { SimpleReactPyClient, mount } from \"@reactpy/client\";\n\nlet didMountDebug = false;\n\nexport function mountWidgetEx"
},
{
"path": "docs/source/_exts/async_doctest.py",
"chars": 1253,
"preview": "from doctest import DocTest, DocTestRunner\nfrom textwrap import indent\nfrom typing import Any\n\nfrom sphinx.application i"
},
{
"path": "docs/source/_exts/autogen_api_docs.py",
"chars": 3881,
"preview": "from __future__ import annotations\n\nimport sys\nfrom collections.abc import Collection, Iterator\nfrom pathlib import Path"
},
{
"path": "docs/source/_exts/build_custom_js.py",
"chars": 359,
"preview": "import subprocess\nfrom pathlib import Path\n\nfrom sphinx.application import Sphinx\n\nSOURCE_DIR = Path(__file__).parent.pa"
},
{
"path": "docs/source/_exts/copy_vdom_json_schema.py",
"chars": 666,
"preview": "import json\nfrom pathlib import Path\n\nfrom sphinx.application import Sphinx\n\nfrom reactpy.core.vdom import VDOM_JSON_SCH"
},
{
"path": "docs/source/_exts/custom_autosectionlabel.py",
"chars": 2480,
"preview": "\"\"\"Mostly copied from sphinx.ext.autosectionlabel\n\nSee Sphinx BSD license:\nhttps://github.com/sphinx-doc/sphinx/blob/f99"
},
{
"path": "docs/source/_exts/patched_html_translator.py",
"chars": 770,
"preview": "from sphinx.util.docutils import is_html5_writer_available\nfrom sphinx.writers.html import HTMLTranslator\nfrom sphinx.wr"
},
{
"path": "docs/source/_exts/reactpy_example.py",
"chars": 4817,
"preview": "from __future__ import annotations\n\nimport re\nfrom pathlib import Path\nfrom typing import Any, ClassVar\n\nfrom docs_app.e"
},
{
"path": "docs/source/_exts/reactpy_view.py",
"chars": 1986,
"preview": "import os\nfrom typing import Any, ClassVar\n\nfrom docs_app.examples import get_normalized_example_name\nfrom docutils.node"
},
{
"path": "docs/source/_static/css/furo-theme-overrides.css",
"chars": 79,
"preview": ".sidebar-container {\n width: 18em;\n}\n.sidebar-brand-text {\n display: none;\n}\n"
},
{
"path": "docs/source/_static/css/larger-api-margins.css",
"chars": 122,
"preview": ":is(.data, .function, .class, .exception).py {\n margin-top: 3em;\n}\n\n:is(.attribute, .method).py {\n margin-top: 1.8em;\n"
},
{
"path": "docs/source/_static/css/larger-headings.css",
"chars": 90,
"preview": "h1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n margin-top: 1.5em !important;\n font-weight: 900 !important;\n}\n"
},
{
"path": "docs/source/_static/css/reactpy-view.css",
"chars": 960,
"preview": ".interactive {\n -webkit-transition: 0.1s ease-out;\n -moz-transition: 0.1s ease-out;\n -o-transition: 0.1s ease-out;\n "
},
{
"path": "docs/source/_static/css/sphinx-design-overrides.css",
"chars": 208,
"preview": ".sd-card-body {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n}\n\n.sd-tab-content .highlight pre {\n "
},
{
"path": "docs/source/_static/css/widget-output-css-overrides.css",
"chars": 159,
"preview": ".widget-container h1,\n.widget-container h2,\n.widget-container h3,\n.widget-container h4,\n.widget-container h5,\n.widget-co"
},
{
"path": "docs/source/about/changelog.rst",
"chars": 49256,
"preview": ".. THIS CHANGELOG HAS BEEN DEPRECATED. SEE TOP LEVEL CHANGELOG.md FILE INSTEAD. ---\nChangelog\n=========\n\n.. note::\n\n "
},
{
"path": "docs/source/about/contributor-guide.rst",
"chars": 5222,
"preview": "Contributor Guide\n=================\n\nCreating a development environment\n----------------------------------\n\nIf you plan "
},
{
"path": "docs/source/about/credits-and-licenses.rst",
"chars": 602,
"preview": "Credits and Licenses\n====================\n\nMuch of this documentation, including its layout and content, was created wit"
},
{
"path": "docs/source/conf.py",
"chars": 10201,
"preview": "#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a selection of the most com"
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json",
"chars": 5636,
"preview": "[\n {\n \"name\": \"Homenaje a la Neurocirugía\",\n \"artist\": \"Marta Colvin Andrade\",\n \"description\": \"Although Colvi"
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py",
"chars": 890,
"preview": "import json\nfrom pathlib import Path\n\nfrom reactpy import component, hooks, html, run\n\nHERE = Path(__file__)\nDATA_PATH ="
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/data.json",
"chars": 5636,
"preview": "[\n {\n \"name\": \"Homenaje a la Neurocirugía\",\n \"artist\": \"Marta Colvin Andrade\",\n \"description\": \"Although Colvi"
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py",
"chars": 1440,
"preview": "import json\nfrom pathlib import Path\n\nfrom reactpy import component, hooks, html, run\n\nHERE = Path(__file__)\nDATA_PATH ="
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json",
"chars": 5636,
"preview": "[\n {\n \"name\": \"Homenaje a la Neurocirugía\",\n \"artist\": \"Marta Colvin Andrade\",\n \"description\": \"Although Colvi"
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py",
"chars": 1235,
"preview": "import json\nfrom pathlib import Path\n\nfrom reactpy import component, hooks, html, run\n\nHERE = Path(__file__)\nDATA_PATH ="
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/data.json",
"chars": 5636,
"preview": "[\n {\n \"name\": \"Homenaje a la Neurocirugía\",\n \"artist\": \"Marta Colvin Andrade\",\n \"description\": \"Although Colvi"
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py",
"chars": 957,
"preview": "# flake8: noqa\n# errors F841,F823 for `index = index + 1` inside the closure\n\n# :lines: 7-\n# :linenos:\n\nimport json\nfrom"
},
{
"path": "docs/source/guides/adding-interactivity/components-with-state/index.rst",
"chars": 13744,
"preview": "Components With State\n=====================\n\nComponents often need to change what’s on the screen as a result of an inte"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py",
"chars": 1728,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef Definitions():\n term_to_add, set_term_to_add = u"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py",
"chars": 1218,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef Form():\n person, set_person = use_state(\n "
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py",
"chars": 720,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef ArtistList():\n artist_to_add, set_artist_to_add "
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py",
"chars": 645,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef ArtistList():\n artists, set_artists = use_state("
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_remove.py",
"chars": 1239,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef ArtistList():\n artist_to_add, set_artist_to_add "
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py",
"chars": 661,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef CounterList():\n counters, set_counters = use_sta"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py",
"chars": 1259,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef MovingDot():\n position, set_position = use_state"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py",
"chars": 1199,
"preview": "# :linenos:\n\nfrom reactpy import component, html, run, use_state\n\n\n@component\ndef MovingDot():\n position, _ = use_sta"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_remove.py",
"chars": 1166,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef Grid():\n line_size = 5\n selected_indices, set"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_update.py",
"chars": 1033,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef Grid():\n line_size = 5\n selected_indices, set"
},
{
"path": "docs/source/guides/adding-interactivity/dangers-of-mutability/index.rst",
"chars": 14353,
"preview": "Dangers of Mutability\n=====================\n\nWhile state can hold any type of value, you should be careful to avoid dire"
},
{
"path": "docs/source/guides/adding-interactivity/index.rst",
"chars": 8194,
"preview": "Adding Interactivity\n====================\n\n.. toctree::\n :hidden:\n\n responding-to-events/index\n components-with"
},
{
"path": "docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py",
"chars": 385,
"preview": "import asyncio\n\nfrom reactpy import component, html, run, use_state\n\n\n@component\ndef Counter():\n number, set_number ="
},
{
"path": "docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py",
"chars": 362,
"preview": "import asyncio\n\nfrom reactpy import component, html, run, use_state\n\n\n@component\ndef Counter():\n number, set_number ="
},
{
"path": "docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py",
"chars": 591,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef ColorButton():\n color, set_color = use_state(\"gr"
},
{
"path": "docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_state_function.py",
"chars": 451,
"preview": "from reactpy import component, html, run, use_state\n\n\ndef increment(old_number):\n new_number = old_number + 1\n ret"
},
{
"path": "docs/source/guides/adding-interactivity/multiple-state-updates/index.rst",
"chars": 5144,
"preview": "Multiple State Updates\n======================\n\nSetting a state variable will queue another render. But sometimes you mig"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/audio_player.py",
"chars": 511,
"preview": "import json\n\nimport reactpy\n\n\n@reactpy.component\ndef PlayDinosaurSound():\n event, set_event = reactpy.hooks.use_state"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/button_async_handlers.py",
"chars": 437,
"preview": "import asyncio\n\nfrom reactpy import component, html, run\n\n\n@component\ndef ButtonWithDelay(message, delay):\n async def"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/button_does_nothing.py",
"chars": 132,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Button():\n return html.button(\"I don't do anything yet\")\n\n\n"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py",
"chars": 619,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Button(display_text, on_click):\n return html.button({\"on_cl"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_event.py",
"chars": 197,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Button():\n def handle_event(event):\n print(event)\n\n "
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_message.py",
"chars": 364,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef PrintButton(display_text, message_text):\n def handle_event("
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py",
"chars": 425,
"preview": "from reactpy import component, event, html, run\n\n\n@component\ndef DoNotChangePages():\n return html.div(\n html.p"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py",
"chars": 1273,
"preview": "from reactpy import component, event, hooks, html, run\n\n\n@component\ndef DivInDiv():\n stop_propagatation, set_stop_pro"
},
{
"path": "docs/source/guides/adding-interactivity/responding-to-events/index.rst",
"chars": 5544,
"preview": "Responding to Events\n====================\n\nReactPy lets you add event handlers to your parts of the interface. These eve"
},
{
"path": "docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/delayed_print_after_set.py",
"chars": 419,
"preview": "import asyncio\n\nfrom reactpy import component, html, run, use_state\n\n\n@component\ndef Counter():\n number, set_number ="
},
{
"path": "docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/print_chat_message.py",
"chars": 1212,
"preview": "import asyncio\n\nfrom reactpy import component, event, html, run, use_state\n\n\n@component\ndef App():\n recipient, set_re"
},
{
"path": "docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/print_count_after_set.py",
"chars": 331,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef Counter():\n number, set_number = use_state(0)\n\n "
},
{
"path": "docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/send_message.py",
"chars": 893,
"preview": "from reactpy import component, event, html, run, use_state\n\n\n@component\ndef App():\n is_sent, set_is_sent = use_state("
},
{
"path": "docs/source/guides/adding-interactivity/state-as-a-snapshot/_examples/set_counter_3_times.py",
"chars": 371,
"preview": "from reactpy import component, html, run, use_state\n\n\n@component\ndef Counter():\n number, set_number = use_state(0)\n\n "
},
{
"path": "docs/source/guides/adding-interactivity/state-as-a-snapshot/index.rst",
"chars": 6777,
"preview": "State as a Snapshot\n===================\n\nWhen you watch the user interfaces you build change as you interact with them, "
},
{
"path": "docs/source/guides/creating-interfaces/html-with-reactpy/index.rst",
"chars": 4238,
"preview": "HTML With ReactPy\n=================\n\nIn a typical Python-based web application the responsibility of defining the view a"
},
{
"path": "docs/source/guides/creating-interfaces/index.rst",
"chars": 4136,
"preview": "Creating Interfaces\n===================\n\n.. toctree::\n :hidden:\n\n html-with-reactpy/index\n your-first-component"
},
{
"path": "docs/source/guides/creating-interfaces/rendering-data/_examples/sorted_and_filtered_todo_list.py",
"chars": 1026,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef DataList(items, filter_by_priority=None, sort_by_priority=Fals"
},
{
"path": "docs/source/guides/creating-interfaces/rendering-data/_examples/todo_from_list.py",
"chars": 581,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef DataList(items):\n list_item_elements = [html.li(text) for t"
},
{
"path": "docs/source/guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys.py",
"chars": 1116,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef DataList(items, filter_by_priority=None, sort_by_priority=Fals"
},
{
"path": "docs/source/guides/creating-interfaces/rendering-data/index.rst",
"chars": 9543,
"preview": "Rendering Data\n==============\n\nFrequently you need to construct a number of similar components from a collection of\ndata"
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/bad_conditional_todo_list.py",
"chars": 482,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Item(name, done):\n if done:\n return html.li(name, \" "
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/good_conditional_todo_list.py",
"chars": 442,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Item(name, done):\n return html.li(name, \" ✔\" if done else \""
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/nested_photos.py",
"chars": 408,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Photo():\n return html.img(\n {\n \"src\": \"ht"
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/parametrized_photos.py",
"chars": 492,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Photo(alt_text, image_id):\n return html.img(\n {\n "
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/simple_photo.py",
"chars": 253,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Photo():\n return html.img(\n {\n \"src\": \"ht"
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/todo_list.py",
"chars": 420,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef Item(name, done):\n return html.li(name)\n\n\n@component\ndef To"
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_div.py",
"chars": 279,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef MyTodoList():\n return html.div(\n html.h1(\"My Todo Li"
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_fragment.py",
"chars": 277,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef MyTodoList():\n return html._(\n html.h1(\"My Todo List"
},
{
"path": "docs/source/guides/creating-interfaces/your-first-components/index.rst",
"chars": 4762,
"preview": "Your First Components\n=====================\n\nAs we learned :ref:`earlier <HTML with ReactPy>` we can use ReactPy to make"
},
{
"path": "docs/source/guides/escape-hatches/_examples/material_ui_button_no_action.py",
"chars": 310,
"preview": "from reactpy import component, run, web\n\nmui = web.module_from_template(\n \"react@^17.0.0\",\n \"@material-ui/core@4.1"
},
{
"path": "docs/source/guides/escape-hatches/_examples/material_ui_button_on_click.py",
"chars": 622,
"preview": "import json\n\nimport reactpy\n\nmui = reactpy.web.module_from_template(\n \"react@^17.0.0\",\n \"@material-ui/core@4.12.4\""
},
{
"path": "docs/source/guides/escape-hatches/_examples/super_simple_chart/main.py",
"chars": 761,
"preview": "from pathlib import Path\n\nfrom reactpy import component, run, web\n\nfile = Path(__file__).parent / \"super-simple-chart.js"
},
{
"path": "docs/source/guides/escape-hatches/_examples/super_simple_chart/super-simple-chart.js",
"chars": 2127,
"preview": "import { h, render } from \"https://unpkg.com/preact?module\";\nimport htm from \"https://unpkg.com/htm?module\";\n\nconst html"
},
{
"path": "docs/source/guides/escape-hatches/distributing-javascript.rst",
"chars": 10518,
"preview": "Distributing Javascript\n=======================\n\nThere are two ways that you can distribute your :ref:`Custom Javascript"
},
{
"path": "docs/source/guides/escape-hatches/index.rst",
"chars": 202,
"preview": "Escape Hatches\n==============\n\n.. toctree::\n :hidden:\n\n javascript-components\n distributing-javascript\n usin"
},
{
"path": "docs/source/guides/escape-hatches/javascript-components.rst",
"chars": 5253,
"preview": ".. _Javascript Component:\n\nJavascript Components\n=====================\n\nWhile ReactPy is a great tool for displaying HTM"
},
{
"path": "docs/source/guides/escape-hatches/using-a-custom-backend.rst",
"chars": 147,
"preview": ".. _Writing Your Own Backend:\n.. _Using a Custom Backend:\n\nUsing a Custom Backend 🚧\n=========================\n\n.. note::"
},
{
"path": "docs/source/guides/escape-hatches/using-a-custom-client.rst",
"chars": 143,
"preview": ".. _Writing Your Own Client:\n.. _Using a Custom Client:\n\nUsing a Custom Client 🚧\n========================\n\n.. note::\n\n "
},
{
"path": "docs/source/guides/getting-started/_examples/debug_error_example.py",
"chars": 325,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef App():\n return html.div(GoodComponent(), BadComponent())\n\n\n"
},
{
"path": "docs/source/guides/getting-started/_examples/hello_world.py",
"chars": 112,
"preview": "from reactpy import component, html, run\n\n\n@component\ndef App():\n return html.h1(\"Hello, world!\")\n\n\nrun(App)\n"
},
{
"path": "docs/source/guides/getting-started/_examples/run_fastapi.py",
"chars": 425,
"preview": "# :lines: 11-\n\nfrom reactpy import run\nfrom reactpy.backend import fastapi as fastapi_server\n\n# the run() function is th"
},
{
"path": "docs/source/guides/getting-started/_examples/run_flask.py",
"chars": 419,
"preview": "# :lines: 11-\n\nfrom reactpy import run\nfrom reactpy.backend import flask as flask_server\n\n# the run() function is the en"
},
{
"path": "docs/source/guides/getting-started/_examples/run_sanic.py",
"chars": 470,
"preview": "# :lines: 11-\n\nfrom reactpy import run\nfrom reactpy.backend import sanic as sanic_server\n\n# the run() function is the en"
},
{
"path": "docs/source/guides/getting-started/_examples/run_starlette.py",
"chars": 452,
"preview": "# :lines: 10-\n\nfrom reactpy import run\nfrom reactpy.backend import starlette as starlette_server\n\n# the run() function i"
},
{
"path": "docs/source/guides/getting-started/_examples/run_tornado.py",
"chars": 608,
"preview": "# :lines: 11-\n\nfrom reactpy import run\nfrom reactpy.backend import tornado as tornado_server\n\n# the run() function is th"
},
{
"path": "docs/source/guides/getting-started/_examples/sample_app.py",
"chars": 54,
"preview": "import reactpy\n\nreactpy.run(reactpy.sample.SampleApp)\n"
},
{
"path": "docs/source/guides/getting-started/_static/embed-doc-ex.html",
"chars": 265,
"preview": "<div id=\"reactpy-app\" />\n<script type=\"module\">\n import { mountLayoutWithWebSocket } from \"https://esm.sh/@reactpy/clie"
},
{
"path": "docs/source/guides/getting-started/_static/embed-reactpy-view/index.html",
"chars": 788,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"UTF-8\" />\n <title>Example App</title>\n </head>\n <body>\n <h1>T"
},
{
"path": "docs/source/guides/getting-started/_static/embed-reactpy-view/main.py",
"chars": 440,
"preview": "from sanic import Sanic\nfrom sanic.response import file\n\nfrom reactpy import component, html\nfrom reactpy.backend.sanic "
},
{
"path": "docs/source/guides/getting-started/index.rst",
"chars": 4027,
"preview": "Getting Started\n===============\n\n.. toctree::\n :hidden:\n\n installing-reactpy\n running-reactpy\n\n.. dropdown:: :o"
},
{
"path": "docs/source/guides/getting-started/installing-reactpy.rst",
"chars": 4010,
"preview": "Installing ReactPy\n==================\n\nYou will typically ``pip`` install ReactPy to alongside one of it's natively supp"
},
{
"path": "docs/source/guides/getting-started/running-reactpy.rst",
"chars": 6503,
"preview": "Running ReactPy\n===============\n\nThe simplest way to run ReactPy is with the :func:`~reactpy.backend.utils.run` function"
},
{
"path": "docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst",
"chars": 106,
"preview": "Combining Contexts and Reducers 🚧\n==================================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst",
"chars": 112,
"preview": "Deeply Sharing State with Contexts 🚧\n=====================================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/managing-state/how-to-structure-state/index.rst",
"chars": 117,
"preview": ".. _Structuring Your State:\n\nHow to Structure State 🚧\n=========================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/managing-state/index.rst",
"chars": 3652,
"preview": "Managing State\n==============\n\n.. toctree::\n :hidden:\n\n how-to-structure-state/index\n sharing-component-state/i"
},
{
"path": "docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json",
"chars": 766,
"preview": "[\n {\n \"name\": \"Sushi\",\n \"description\": \"Sushi is a traditional Japanese dish of prepared vinegared rice\"\n },\n {"
},
{
"path": "docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py",
"chars": 1113,
"preview": "import json\nfrom pathlib import Path\n\nfrom reactpy import component, hooks, html, run\n\nHERE = Path(__file__)\nDATA_PATH ="
},
{
"path": "docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py",
"chars": 497,
"preview": "from reactpy import component, hooks, html, run\n\n\n@component\ndef SyncedInputs():\n value, set_value = hooks.use_state("
},
{
"path": "docs/source/guides/managing-state/sharing-component-state/index.rst",
"chars": 1155,
"preview": "Sharing Component State\n=======================\n\n.. note::\n\n Parts of this document are still under construction 🚧\n\nS"
},
{
"path": "docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst",
"chars": 110,
"preview": "Simplifying Updates with Reducers 🚧\n====================================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst",
"chars": 124,
"preview": ".. _When to Reset State:\n\nWhen and How to Reset State 🚧\n==============================\n\n.. note::\n\n Under constructio"
},
{
"path": "docs/source/guides/understanding-reactpy/index.rst",
"chars": 279,
"preview": "Understanding ReactPy\n=====================\n\n.. toctree::\n :hidden:\n\n representing-html\n what-are-components\n "
},
{
"path": "docs/source/guides/understanding-reactpy/layout-render-servers.rst",
"chars": 114,
"preview": ".. _Layout Render Servers:\n\nLayout Render Servers 🚧\n========================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/understanding-reactpy/representing-html.rst",
"chars": 2524,
"preview": ".. _Representing HTML:\n\nRepresenting HTML 🚧\n====================\n\n.. note::\n\n Under construction 🚧\n\nWe've already dis"
},
{
"path": "docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst",
"chars": 156,
"preview": ".. _The Rendering Pipeline:\n\nThe Rendering Pipeline 🚧\n=========================\n\n.. talk about layouts and dispatchers\n\n"
},
{
"path": "docs/source/guides/understanding-reactpy/the-rendering-process.rst",
"chars": 176,
"preview": ".. _The Rendering Process:\n\nThe Rendering Process 🚧\n========================\n\n.. refer to https://beta.reactjs.org/learn"
},
{
"path": "docs/source/guides/understanding-reactpy/what-are-components.rst",
"chars": 110,
"preview": ".. _What Are Components:\n\nWhat Are Components? 🚧\n=======================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst",
"chars": 117,
"preview": ".. _Why ReactPy Needs Keys:\n\nWhy ReactPy Needs Keys 🚧\n=========================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/guides/understanding-reactpy/writing-tests.rst",
"chars": 90,
"preview": ".. _Writing Tests:\n\nWriting Tests 🚧\n================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/index.rst",
"chars": 6787,
"preview": ".. card::\n\n This documentation is still under construction 🚧. We welcome your `feedback\n <https://github.com/react"
},
{
"path": "docs/source/reference/_examples/character_movement/main.py",
"chars": 2005,
"preview": "from pathlib import Path\nfrom typing import NamedTuple\n\nfrom reactpy import component, html, run, use_state\nfrom reactpy"
},
{
"path": "docs/source/reference/_examples/click_count.py",
"chars": 253,
"preview": "import reactpy\n\n\n@reactpy.component\ndef ClickCount():\n count, set_count = reactpy.hooks.use_state(0)\n\n return reac"
},
{
"path": "docs/source/reference/_examples/material_ui_switch.py",
"chars": 511,
"preview": "import reactpy\n\nmui = reactpy.web.module_from_template(\"react\", \"@material-ui/core@^5.0\", fallback=\"⌛\")\nSwitch = reactpy"
},
{
"path": "docs/source/reference/_examples/matplotlib_plot.py",
"chars": 2103,
"preview": "from io import BytesIO\n\nimport matplotlib.pyplot as plt\n\nimport reactpy\nfrom reactpy.widgets import image\n\n\n@reactpy.com"
},
{
"path": "docs/source/reference/_examples/network_graph.py",
"chars": 919,
"preview": "import random\n\nimport reactpy\n\nreact_cytoscapejs = reactpy.web.module_from_template(\n \"react\",\n \"react-cytoscapejs"
},
{
"path": "docs/source/reference/_examples/pigeon_maps.py",
"chars": 1156,
"preview": "import reactpy\n\npigeon_maps = reactpy.web.module_from_template(\"react\", \"pigeon-maps\", fallback=\"⌛\")\nMap, Marker = react"
},
{
"path": "docs/source/reference/_examples/simple_dashboard.py",
"chars": 2687,
"preview": "import asyncio\nimport random\nimport time\n\nimport reactpy\nfrom reactpy.widgets import Input\n\nvictory = reactpy.web.module"
},
{
"path": "docs/source/reference/_examples/slideshow.py",
"chars": 385,
"preview": "import reactpy\n\n\n@reactpy.component\ndef Slideshow():\n index, set_index = reactpy.hooks.use_state(0)\n\n def next_ima"
},
{
"path": "docs/source/reference/_examples/snake_game.py",
"chars": 5372,
"preview": "import asyncio\nimport enum\nimport random\nimport time\n\nimport reactpy\n\n\nclass GameState(enum.Enum):\n init = 0\n lost"
},
{
"path": "docs/source/reference/_examples/todo.py",
"chars": 898,
"preview": "import reactpy\n\n\n@reactpy.component\ndef Todo():\n items, set_items = reactpy.hooks.use_state([])\n\n async def add_ne"
},
{
"path": "docs/source/reference/_examples/use_reducer_counter.py",
"chars": 721,
"preview": "import reactpy\n\n\ndef reducer(count, action):\n if action == \"increment\":\n return count + 1\n elif action == \""
},
{
"path": "docs/source/reference/_examples/use_state_counter.py",
"chars": 612,
"preview": "import reactpy\n\n\ndef increment(last_count):\n return last_count + 1\n\n\ndef decrement(last_count):\n return last_count"
},
{
"path": "docs/source/reference/_examples/victory_chart.py",
"chars": 300,
"preview": "import reactpy\n\nvictory = reactpy.web.module_from_template(\"react\", \"victory-bar\", fallback=\"⌛\")\nVictoryBar = reactpy.we"
},
{
"path": "docs/source/reference/_static/vdom-json-schema.json",
"chars": 2243,
"preview": "{\n \"$ref\": \"#/definitions/element\",\n \"$schema\": \"http://json-schema.org/draft-07/schema\",\n \"definitions\": {\n \"elem"
},
{
"path": "docs/source/reference/browser-events.rst",
"chars": 887,
"preview": ".. _Browser Events:\n\nBrowser Events 🚧\n=================\n\nThe event types below are triggered by an event in the bubbling"
},
{
"path": "docs/source/reference/hooks-api.rst",
"chars": 13006,
"preview": "=========\nHooks API\n=========\n\nHooks are functions that allow you to \"hook into\" the life cycle events and state of\nComp"
},
{
"path": "docs/source/reference/html-attributes.rst",
"chars": 7501,
"preview": ".. testcode::\n\n from reactpy import html\n\n\nHTML Attributes\n===============\n\nIn ReactPy, HTML attributes are specified"
},
{
"path": "docs/source/reference/javascript-api.rst",
"chars": 93,
"preview": ".. _Javascript API:\n\nJavascript API 🚧\n=================\n\n.. note::\n\n Under construction 🚧\n"
},
{
"path": "docs/source/reference/specifications.rst",
"chars": 5016,
"preview": "Specifications\n==============\n\nDescribes various data structures and protocols used to define and communicate virtual\ndo"
},
{
"path": "pyproject.toml",
"chars": 9448,
"preview": "[build-system]\nbuild-backend = \"hatchling.build\"\nrequires = [\"hatchling\", \"hatch-build-scripts\", \"hatch\"]\n\n#############"
},
{
"path": "src/build_scripts/build_js_app.py",
"chars": 629,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\nimport pathlib\nimport subprocess\nimport sys\n\ndev_mod"
},
{
"path": "src/build_scripts/build_js_client.py",
"chars": 741,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\nimport pathlib\nimport shutil\nimport subprocess\nimpor"
},
{
"path": "src/build_scripts/build_js_event_to_object.py",
"chars": 741,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\nimport pathlib\nimport shutil\nimport subprocess\nimpor"
},
{
"path": "src/build_scripts/clean_js_dir.py",
"chars": 1701,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\n\n# Deletes `dist`, `node_modules`, and `tsconfig.tsb"
},
{
"path": "src/build_scripts/copy_dir.py",
"chars": 1030,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\n\nimport logging\nimport shutil\nimport sys\nfrom pathli"
},
{
"path": "src/build_scripts/delete_old_coverage.py",
"chars": 575,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\n\nimport logging\nfrom glob import glob\nfrom pathlib i"
},
{
"path": "src/build_scripts/install_playwright.py",
"chars": 706,
"preview": "# /// script\n# requires-python = \">=3.11\"\n# dependencies = []\n# ///\n\nimport subprocess\n\nprint(\"Installing Playwright bro"
},
{
"path": "src/js/.gitignore",
"chars": 80,
"preview": "tsconfig.tsbuildinfo\npackages/**/package-lock.json\n**/dist/*\nnode_modules\n*.tgz\n"
},
{
"path": "src/js/eslint.config.mjs",
"chars": 662,
"preview": "import { default as eslint } from \"@eslint/js\";\nimport globals from \"globals\";\nimport tseslint from \"typescript-eslint\";"
},
{
"path": "src/js/package.json",
"chars": 739,
"preview": "{\n \"workspaces\": [\n \"packages/event-to-object\",\n \"packages/@reactpy/app\",\n \"packages/@reactpy/client\"\n ],\n \""
},
{
"path": "src/js/packages/@reactpy/app/package.json",
"chars": 873,
"preview": "{\n \"dependencies\": {\n \"@reactpy/client\": \"catalog:\",\n \"event-to-object\": \"catalog:\",\n \"preact\": \"catalog:\"\n }"
},
{
"path": "src/js/packages/@reactpy/app/src/index.ts",
"chars": 48,
"preview": "export { mountReactPy } from \"@reactpy/client\";\n"
},
{
"path": "src/js/packages/@reactpy/app/src/preact-dom.ts",
"chars": 163,
"preview": "import ReactDOM from \"preact/compat\";\n\n// @ts-ignore\nexport * from \"preact/compat\";\n\n// @ts-ignore\nexport * from \"preact"
},
{
"path": "src/js/packages/@reactpy/app/src/preact-jsx-runtime.ts",
"chars": 43,
"preview": "export * from \"preact/compat/jsx-runtime\";\n"
},
{
"path": "src/js/packages/@reactpy/app/src/preact.ts",
"chars": 104,
"preview": "import React from \"preact/compat\";\n\n// @ts-ignore\nexport * from \"preact/compat\";\n\nexport default React;\n"
},
{
"path": "src/js/packages/@reactpy/app/tsconfig.json",
"chars": 269,
"preview": "{\n \"compilerOptions\": {\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"composite\": true,\n \"noEmit\": false,\n \"es"
},
{
"path": "src/js/packages/@reactpy/client/README.md",
"chars": 61,
"preview": "# @reactpy/client\n\nA client for ReactPy implemented in React\n"
},
{
"path": "src/js/packages/@reactpy/client/package.json",
"chars": 775,
"preview": "{\n \"author\": \"Mark Bakhit\",\n \"contributors\": [\n \"Ryan Morshead\"\n ],\n \"dependencies\": {\n \"json-pointer\": \"catal"
},
{
"path": "src/js/packages/@reactpy/client/src/bind.tsx",
"chars": 1521,
"preview": "import * as preact from \"preact\";\n\nexport async function infer_bind_from_environment() {\n try {\n // @ts-ignore\n c"
},
{
"path": "src/js/packages/@reactpy/client/src/client.ts",
"chars": 2747,
"preview": "import logger from \"./logger\";\nimport type {\n ReactPyClientInterface,\n ReactPyModule,\n GenericReactPyClientProps,\n R"
},
{
"path": "src/js/packages/@reactpy/client/src/components.tsx",
"chars": 6956,
"preview": "import { set as setJsonPointer } from \"json-pointer\";\nimport type { MutableRefObject } from \"preact/compat\";\nimport {\n "
},
{
"path": "src/js/packages/@reactpy/client/src/index.ts",
"chars": 354,
"preview": "export * from \"./client\";\nexport * from \"./components\";\nexport * from \"./mount\";\nexport * from \"./types\";\nexport * from "
},
{
"path": "src/js/packages/@reactpy/client/src/logger.ts",
"chars": 300,
"preview": "export default {\n log: (...args: any[]): void => console.log(\"[ReactPy]\", ...args),\n info: (...args: any[]): void => c"
},
{
"path": "src/js/packages/@reactpy/client/src/mount.tsx",
"chars": 1368,
"preview": "import { render } from \"preact\";\nimport { ReactPyClient } from \"./client\";\nimport { Layout } from \"./components\";\nimport"
},
{
"path": "src/js/packages/@reactpy/client/src/types.ts",
"chars": 3633,
"preview": "import type { ComponentType } from \"preact\";\n\n// #### CONNECTION TYPES ####\n\nexport type ReconnectOptions = {\n interval"
},
{
"path": "src/js/packages/@reactpy/client/src/vdom.tsx",
"chars": 9325,
"preview": "import eventToObject from \"event-to-object\";\nimport { Fragment } from \"preact\";\nimport type {\n ReactPyVdom,\n ReactPyVd"
},
{
"path": "src/js/packages/@reactpy/client/src/websocket.ts",
"chars": 1877,
"preview": "import type { CreateReconnectingWebSocketProps } from \"./types\";\nimport log from \"./logger\";\n\nexport function createReco"
},
{
"path": "src/js/packages/@reactpy/client/tsconfig.json",
"chars": 252,
"preview": "{\n \"compilerOptions\": {\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"composite\": true,\n \"noEmit\": false\n },\n \""
},
{
"path": "src/js/packages/event-to-object/README.md",
"chars": 78,
"preview": "# Event to Object\n\nConverts a JavaScript events to JSON serializable objects.\n"
},
{
"path": "src/js/packages/event-to-object/package.json",
"chars": 764,
"preview": "{\n \"author\": \"Mark Bakhit\",\n \"contributors\": [\n \"Ryan Morshead\"\n ],\n \"dependencies\": {\n \"json-pointer\": \"catal"
},
{
"path": "src/js/packages/event-to-object/src/index.ts",
"chars": 8944,
"preview": "const maxDepthSignal = { __stop__: true };\n\n/**\n * Convert any class object (such as `Event`) to a plain object.\n */\nexp"
},
{
"path": "src/js/packages/event-to-object/tests/event-to-object.test.ts",
"chars": 16114,
"preview": "// @ts-ignore\nimport { window } from \"./tooling/setup\";\nimport { test, expect } from \"bun:test\";\nimport { Event } from \""
}
]
// ... and 139 more files (download for full content)
About this extraction
This page contains the full source code of the reactive-python/reactpy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 339 files (987.3 KB), approximately 247.4k tokens, and a symbol index with 1044 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.