diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 094c32693..000000000 --- a/.editorconfig +++ /dev/null @@ -1,35 +0,0 @@ -# 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 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 3fc786dff..000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -@maintainers diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 74094ade3..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# 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'] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 21f192233..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -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 diff --git a/.github/ISSUE_TEMPLATE/issue-form.yml b/.github/ISSUE_TEMPLATE/issue-form.yml deleted file mode 100644 index 3cad28d11..000000000 --- a/.github/ISSUE_TEMPLATE/issue-form.yml +++ /dev/null @@ -1,16 +0,0 @@ -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 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 6e44c0c26..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,391 +0,0 @@ -# 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 two packages: @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/@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/` -- Two main packages: @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 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index a55532008..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,14 +0,0 @@ -## Description - - - -## 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. - -By submitting this pull request I agree that all contributions comply with this project's open source license(s). diff --git a/.github/workflows/.hatch-run.yml b/.github/workflows/.hatch-run.yml deleted file mode 100644 index c5a7cd885..000000000 --- a/.github/workflows/.hatch-run.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: hatch-run - -on: - workflow_call: - inputs: - job-name: - required: true - type: string - 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 - -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 }} - run: ${{ inputs.run-cmd }} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml deleted file mode 100644 index 022e6ea84..000000000 --- a/.github/workflows/check.yml +++ /dev/null @@ -1,50 +0,0 @@ -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" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 4f829a5c2..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,71 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: codeql - -on: - push: - branches: [main] - pull_request: - # The branches below must be a subset of the branches above - branches: [main] - schedule: - - cron: "43 3 * * 3" - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ["javascript", "python"] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index c0c5305f6..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,40 +0,0 @@ -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-') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - 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 - - uses: actions/setup-python@v5 - with: - python-version: "3.13" - cache: "pip" - - name: Install Python Dependencies - run: pip install hatch - - name: Build Python Package - run: hatch build --clean - - name: Publish to PyPI (Trusted Publishing) - uses: pypa/gh-action-pypi-publish@release/v1 - - 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" diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3e4fd10f4..000000000 --- a/.gitignore +++ /dev/null @@ -1,51 +0,0 @@ -# --- Build Artifacts --- -src/reactpy/static/*.js* -src/reactpy/static/morphdom/ -src/reactpy/static/pyscript/ -src/reactpy/static/wheels/ -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 - diff --git a/docs/docs_app/__init__.py b/.nojekyll similarity index 100% rename from docs/docs_app/__init__.py rename to .nojekyll diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 0383cbb1d..000000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,34 +0,0 @@ -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)$ diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index c8d22583b..000000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "proseWrap": "never", - "trailingComma": "all", - "endOfLine": "auto" -} diff --git a/404.html b/404.html new file mode 100644 index 000000000..b8a5129c5 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ +
Notable changes to ReactPy, newest first. Read the Unreleased section for what has landed since the last release, and the versioned sections for what shipped in each.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Note
Entries use the Keep a Changelog categories — Added, Changed, Deprecated, Removed, Fixed, Security — and versions follow Semantic Versioning. Pre-release versions carry a bN suffix, and behaviour can shift between betas without a major-version bump, so pin an exact version if you depend on one.
The authoritative list of shipped artefacts, with downloads and commit ranges, is GitHub Releases; the published packages are on PyPI. This page is the curated narrative of what changed, not the release log.
ReactPy takes pull requests from anyone, and most of what gets rejected is not wrong — it is unreviewable. A short branch, a focused diff, and the checks run before you open the pull request is what makes a change easy to say yes to. This page covers the mechanics: where the code lives, how to branch, and what CI will run against you.
You will learn
This documentation site and the library are separate bodies of work, and the commands that work in one do nothing in the other.
| Change | Repository / branch |
|---|---|
| Library code, hooks, layout, backends | reactive-python/reactpy → main |
JavaScript client (src/js/) | Same, main |
These documentation pages (docs/src/, docs/examples/) | reactive-python/reactpy → new-docs |
The new-docs branch holds only docs/ — the package and tests/ were removed from it. A test command run there reports success without running anything.
Pitfall
Confirm the branch before you start typing, not after. A library fix committed onto the docs branch looks fine locally and becomes a rebase chore later. git branch --show-current costs nothing and settles it.
Look for an open discussion or issue that matches what you intend to do. If one exists, comment there before writing code — a maintainer who has already thought about the problem will usually redirect you in one sentence, and that sentence is cheaper than a rejected pull request. If none exists, open a discussion first.
For anything larger than a bug fix, expect the answer to be "show me the design first". A draft pull request is the right medium for that: it runs CI, it is reviewable, and it says plainly that it is not ready to merge.
Fork the repository if you do not have write access, then branch from an up-to-date main:
1 | |
Name the branch for the change, not for you. fix-key-not-resetting-on-provider beats patch-1 and survives a month of review.
Match the convention of the branch you are landing on; they differ. Documentation commits on new-docs use Conventional Commits subjects — docs(learn): author X for ReactPy v2, docs(examples): fix include-marker cropping. Library history on main instead uses plain sentence subjects, because pull requests there are squash-merged and the summary is written at merge time — Fix PyScript root component not hiding after render error (#1347). In both cases the subject is the thing a reader scans to find the functional change in a long history, so write it for that reader.
One commit per logical change. If a commit only makes sense next to a later one, squash them before you push rather than asking the reviewer to mentally reassemble them.
The repository ships a pull request template; fill it in rather than deleting it.
By submitting, you agree that your contribution complies with the project's open source licenses.
Tests are not optional for a behaviour change. A bug fix should arrive with the test that reproduces the bug — write it first and watch it fail, then fix and watch it pass. A test that has never failed proves nothing about the thing it claims to protect.
The changelog rule is narrower than it looks: CHANGELOG.md gets an entry under Unreleased for user-facing changes only. Test-only, docs-only, and CI-only changes get none. Adding one for those is a small annoyance to the person doing the release, who then has to notice and delete it. See Changelog.
Understanding the gate is cheaper than discovering it in a failed run.
1 +2 +3 +4 +5 +6 | |
Run the equivalents locally before you push — the same commands, not approximations of them:
1 +2 +3 | |
CI retries flaky tests, which is a mercy and a trap: a genuinely racy test can go green on the third attempt and stay in the tree. If a test fails once and passes on retry, that is a bug in the test — usually a bare asyncio.sleep where poll(...) belongs.
A pull request into new-docs gets no CI at all, for two independent reasons. GitHub runs pull_request workflows from the base branch, and the only workflow that exists on new-docs is the docs deploy, which triggers on push — so nothing matches a PR. And the test-documentation job in check.yml on main is disabled outright with if: 0 (temporarily, while the Sphinx-to-MkDocs transition finishes), so even a PR targeting main runs lint and Python tests but never builds the docs. A docs change is verified by you or by nobody — run the mkdocs build --strict set from Running Tests and read its exit code. The one thing that does run: pushing to new-docs deploys with mkdocs gh-deploy --force, no --strict — expect a change to be live the moment it merges, whether or not it builds cleanly.
Respond to every comment, including the ones you are not acting on. "Not taking this because X" is a complete reply and saves a round trip; silence invites a re-review of something you already considered.
Push fixes as new commits rather than force-pushing over the reviewed range. Reviewers read the delta between your last push and this one; a force-push that rewrites history they already read makes them re-read the whole diff, and they will notice when they skip it.
If a review asks for something you believe is wrong, say so once, with evidence, and then defer if the maintainer still wants it. Review is a conversation with an asymmetry: they own the file after you leave.
Note
When a review is posted by an automated tool, the maintainers still own the decision. Do not treat "the bot did not complain" as approval, and do not treat a bot comment as a demand — respond on the merits, the same as any other comment.
Documentation changes on new-docs deploy to the live site on push, so a mistake there is visible immediately rather than at the next release. That is a reason to run the build check locally, not a reason to be nervous about contributing.
Library changes reach users only through a release, which the maintainers cut. Do not ask when your fix will ship, and do not close a closed-issue loop by demanding a release — the tag is a maintainer's call.
main; documentation work on new-docs. Check the branch first.ReactPy is built in the open. Questions, bug reports, and ideas all have a home, and the same places are where you get help when an example on this site stops behaving. This page is a map of those places and what belongs in each.
You will learn
The ReactPy repository on GitHub is the centre of gravity. Everything else points back to it.
| You want to | Go to |
|---|---|
| Ask how to do something | Discussions → Question |
| Report something broken | Discussions → Problem |
| Propose a feature | Discussions → Ideas |
| Show off what you built | Discussions → Share |
| Publish a package you made | Discussions → Downloadable Packages |
| Track a confirmed defect | Issues |
| Chat in real time | Discord |
Discussions is the front door, not Issues. The repository's issue-template configuration disables blank issues and points new visitors at Discussions instead, so a question opened as an issue gets redirected rather than answered. Open an issue once a defect is confirmed and someone has reproduced it — the discussion that came first usually becomes the link inside it.
There is a Discord server for ReactPy, announced in Discussion #997 and linked from the repository README. It is the fastest place to get an answer to a "why is this doing that" question, and quiet enough that nobody minds a beginner question.
Note
An invite URL is mutable third-party state. If discord.gg/uNb5P4hA9X ever stops resolving, read the current invite from the README badge or from Discussion #997 — those two are maintained together — rather than trusting a search result, which may point at a cloned server.
Discord is good for conversation and bad for searchability. A solution that exists only in chat is invisible to the next person with the same problem. If a chat thread solves something real, post it to Discussions afterwards; if it exposes an error on this site, that is a documentation contribution.
Participation is governed by the Contributor Covenant code of conduct, which lives at the repository root. It applies everywhere the project is discussed — Discussions, Issues, pull requests, Discord, and any other space where ReactPy maintainers show up.
The practical version: assume good faith, criticise code rather than people, and accept that "this is obvious" is rarely true of the person asking. The enforcement section of the document names the contact route for escalating a problem.
ReactPy's core deliberately stays small, so much of what you might expect to find here lives in sibling repositories. Each has its own issue tracker — do not file a Django problem against the core repo.
Community-built packages are announced in Discussions → Downloadable Packages. Those are third-party: they are not reviewed before being posted, they are not pinned against a ReactPy release, and an unanswered thread there says nothing about the library. See Tools, Libraries, and Packages for how to evaluate one.
ReactPy is maintained by volunteers on their own time. GitHub Sponsors is the route the project uses, matching the maintainers listed in the repository's funding configuration.
Sponsoring buys maintenance, not priority. It does not queue your issue and does not entitle anyone to a feature. If you need a specific behaviour on a deadline, the reliable path is to write it — or fund someone to write it — and open a pull request.
Most unanswered questions are unanswered because they cannot be reproduced. Four things turn a vague report into one someone can act on:
pip show reactpy and paste it. Behaviour shifts between pre-releases, so 2.0.0b13 and 2.0.0 are different answers.Pitfall
"It doesn't work" with no traceback is not a question anyone can answer, and the error you think is irrelevant often is not the relevant one. Include the full traceback and the server-side console output. ReactPy logs failing event handlers and effects on the server while the browser stays silent, so a UI that merely stops responding usually has a server line that explains it — see Creating VDOM Event Handlers.
Most of these pages were ported from the ReactJS documentation, and the port is the whole job: a page is done when every claim in it is true of ReactPy and every snippet in it runs. That is a lower bound on effort and an upper bound on trustworthiness, which is the trade this project made deliberately. This page is how to do the work and how to know it is finished.
You will learn
🚧 marker worksdocs/examples/ instead of inside the markdown1 +2 +3 +4 +5 +6 +7 +8 | |
Page files are kebab-case.md; example directories are the same name with underscores (learn/scaling-up-with-reducer-and-context.md → examples/scaling_up_with_reducer_and_context/). A new page must be added to nav: in mkdocs.yml, but the build will not tell you when you forget: a page missing from nav: is still built and served, and mkdocs build --strict exits 0. The only trace is an INFO line listing the unlisted pages. Read it: every page currently on that list carries a Planned / Undeveloped banner in its body and can be ignored, which is exactly how a page you did mean to publish would hide among them. Adding the entry is what puts the page in the site's navigation, which is the actual reason to do it.
A page is authored, not migrated. Concretely:
className= outside an attribute dict, no hook that does not exist.🚧 badge is gone from its nav: entry.Note
🚧 in the navigation means "this is still a ReactJS page with the React names swapped". It is a promise to the reader that the content is unreliable, so removing it is the meaningful act — the badge should never come off before the checks above are true. 🚫 means the page is blocked on something outside this project and is parked; commented-out nav: entries work the same way.
The pages derive from react.dev. Editing means deleting what is false as much as replacing what is named differently, and the failure mode is a page that reads fluently and is wrong.
Things that do not survive a rename:
use_id, use_transition, use_deferred_value, use_layout_effect, use_insertion_effect, use_imperative_handle, or use_sync_external_store. Some of the react.dev pages for them are parked as 🚫; use_id has no page at all, because the upstream issue was closed as not planned. Never port a page that depends on one as if it worked.my_button, not MyButton. React's capital-letter rule does not apply, because ReactPy distinguishes a component from a tag by the call, not the case. Attribute keys stay camelCase (className, onClick), because those pass through to the DOM untouched.memo. Neither exists. React's double-invoke warnings and its "skip re-rendering unchanged children" optimisation have no ReactPy equivalent, so port those sections as a warning that ReactPy will not catch the problem for you.&& rendering. {cond && <X/>} has no safe Python spelling — False and X leaks the word False into the page, since only None renders nothing. Rewrite such examples rather than transliterating them.use_effect, use_memo, and use_callback infer dependencies from the closure when you omit the argument. React's "omit the array to run after every render" is simply wrong here.Pitfall
A page can pass every automated check while teaching something false, because the checks are syntactic. The ReactJS FIXME that sat in thinking-in-react.md claiming checked was not delivered was wrong — a checkbox onChange handler reads it back as await event.target.checked. The only defence is running the example against a real layout and reading what the client actually answered.
Every authored page opens the same way: an ## Overview heading, an intro paragraph in a <p class="intro"> block, then a You will learn summary.
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 | |
Then the body as ## and ### sections, closing with ## Recap (bulleted, factual) and ## Related (links with a dash and a phrase explaining each).
Admonitions come from the Material theme plus assets/css/admonition.css. The set in use:
| Admonition | For |
|---|---|
!!! summary "You will learn" | Objectives, once, after the intro |
!!! abstract "Note" | Clarification that is not a warning |
!!! warning "Pitfall" | A mistake the reader is likely to make |
!!! info "Deep Dive" | Deeper mechanics a reader can skip |
!!! example "Terminal" | A shell session |
ReactJS's <Pitfall>, <DeepDive>, <Recap>, <Challenges>, <Solution>, and <Hint> JSX tags have no MkDocs equivalent. Convert them to admonitions or headings; never copy them through, since an unrecognised tag renders as literal text.
Links are relative .md paths from the current file — ../reference/use-state.md, ./extracting-state-logic-into-a-reducer.md#the-rule-reactpy-adds — not site-absolute paths like /learn/..., which break under versioned builds and make --strict unable to verify them.
Code fences: ```python linenums="0" for fragments, bare ```python for full-file tabs, and hl_lines="3-4 7" to mark the lines under discussion. Inline code that is Python gets the #!python prefix (\#!python use_state``) so it highlights.
Snippets are not typed into the markdown. They are real files under docs/examples/<page_slug>/, pulled in by the include plugin:
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 | |
The rules that make this work:
# start marker on its own line is what the include begins after; # end stops it. Crop at the marker so the marker itself never renders on the page.task_state_props/, task_context/, task_wiring/), one tabbed group per example.:material-play: Run tab is a standing placeholder. Keep it.Because the file is included, a snippet cannot drift from a runnable example — but nothing in the build proves the example behaves. Mount it and check the DOM before you write "this renders a list". When a page compares several versions, drive all of them through the same click sequence and assert the same observable output; "it looks the same" is how two versions of an example quietly disagree.
Also list children of a component with key= when they come from a list — html.ul([row(item, key=item["id"]) for item in items]). Omitting it renders fine and emits a debug-mode warning, which is exactly the lesson the page about keys is trying to teach.
From the repository root:
1 +2 +3 | |
Then run the examples themselves and open the built page in a browser. mkdocs build --strict catches broken internal links and unresolvable includes — both abort the build with a nonzero exit. Two things it does not catch: a page missing from nav: (an INFO line, exit 0), and a link whose #anchor does not exist in an otherwise valid target file (also exit 0), so check both by eye. And it cannot catch a snippet that renders nothing.
You are the only gate. The single workflow on new-docs runs mkdocs gh-deploy --force on push, and gh-deploy does not pass --strict — a build with broken links exits 0 and publishes. A miss does not fail a pipeline; it goes live.
Pitfall
Do not trust a component that renders without error. Set REACTPY_DEBUG=1 when probing: with debug off, a component that raised produces {"tagName": "", "error": ""} — a blank region and a 200 response. See Running Tests for the harness traps that make a broken example look green.
Branch from new-docs, one page per branch, and use the same Conventional Commit subjects as the history there — docs(learn): author X for ReactPy v2. A commit that removes a 🚧 badge from nav: should be the same commit that authored the page, never a separate "mark done" commit that could be cherry-picked alone.
No CHANGELOG.md entry: documentation work is not user-facing in the sense the changelog means. See Changelog.
Note
docs/src/dictionary.txt exists for the spellcheck plugin, which is currently commented out in mkdocs.yml. New domain words belong there if the plugin is ever re-enabled; nothing fails while it stays off.
🚧 badge.memo, && rendering, and React's dependency-array semantics.You will learn, sections, Recap, Related..md links; admonitions instead of ReactJS's JSX tags.docs/examples/, cropped at # start, formatted at 88 columns.--strict checks links and nothing about behaviour. Run the example.ReactPy's source is MIT-licensed. Much of the prose you are reading is derived from the ReactJS documentation, which carries a separate Creative Commons licence, and the two do not cover the same things. This page states which licence applies to what, and what you owe when you reuse either.
You will learn
The ReactPy package is released under the MIT licence, copyright Reactive Python and affiliates. The full text lives in the license file at the repository root and is reproduced below.
MIT is permissive: use, copy, modify, merge, publish, distribute, sublicense, and sell, subject to keeping the copyright notice and licence text in copies of the software. It comes with no warranty, and liability is excluded.
1 +2 +3 +4 +5 +6 +7 +8 +9 | |
These pages are a different body of work from the code, and they are separately encumbered.
Much of this documentation — including its layout and much of its content — was created with heavy influence from react.dev, which is licensed under the Creative Commons Attribution 4.0 International licence. ReactPy's behaviour often mirrors React's closely enough that the documents paraphrase, and in places copy, React's language and examples.
Note
CC BY 4.0 is also permissive, but its condition is attribution rather than keeping a licence block. If you copy substantial prose from these pages into your own documentation, credit ReactPy and, where the text is close to React's original, acknowledge the ReactJS documentation as the further source. Attributing to ReactPy alone is not sufficient for the passages that came from React.
The practical boundary: code you write against ReactPy is yours, unencumbered by either licence. These pages are text under a credit-bearing licence. A snippet in docs/examples/ is code that happens to be inside a document — treat it as MIT, since its purpose is to be pasted into a project.
A working ReactPy install bundles more than this repository's source: the browser-side client, the vendored JavaScript dependencies, and, for client-side rendering, PyScript. Each keeps its own upstream licence, shipped with the distribution.
Check what a release actually contains rather than trusting a summary — the package metadata and the static/ directory of the release are authoritative, and the licence files travel inside the wheel.
The pull request template ends with an agreement that contributions comply with the project's open source licences. It is a checkbox, but it is the whole of the provenance control this project has, so it is worth taking literally:
LICENSE.md at the repository root.docs/examples/ exist to be pasted; treat them as MIT code. There are two test suites in this project, and they live in different places. The Python and JavaScript suites exercise ReactPy itself and run through hatch; the documentation checks exercise the pages you are reading and run through mkdocs. Knowing which one a failing command belongs to is most of debugging it.
You will learn
Note
The ReactPy package and its tests/ directory live on the main branch. This documentation site is authored on the new-docs branch, which contains only docs/ — running a test command there collects zero tests rather than failing, which reads like a pass and is not one. If hatch test reports no tests ran, you are on the wrong branch, not on a clean tree.
The development environment is managed by Hatch, which creates and maintains its own virtual environments. Install Hatch, then clone:
1 +2 | |
You also need Bun for the JavaScript packages. Python must be 3.11 or newer — requires-python in pyproject.toml is >=3.11, and the test matrix runs 3.11 through 3.14.
Pitfall
Do not install the project's dependencies with pip install -r into your own environment. Hatch owns those environments, and a hand-built one drifts from CI in ways that look like your fault. The one exception is the documentation requirements, which are installed separately below.
The suite is pytest-based and runs under Hatch:
1 +2 +3 | |
CI runs hatch test --parallel --retries 10 across three operating systems and four Python versions, and a separate coverage job runs hatch test --parallel --cover --retries 10.
The suite needs a browser. Each test run installs Playwright's Chromium, builds the JavaScript client, and builds a wheel before it invokes pytest — that is what the hatch-test environment's run script does. A first run is therefore slow, and a failure inside Chromium installation looks identical to a failure in the test itself. Read the top of the output, not the bottom.
1 +2 | |
Tests are session-scoped around one browser and one backend server, defined in tests/conftest.py, and that file also turns on REACTPY_DEBUG and REACTPY_ASYNC_RENDERING for the whole session. Two consequences:
-k before you chase it.REACTPY_CHECK_VDOM_SPEC are exactly this.Configuration lives under [tool.pytest.ini_options] in pyproject.toml: --strict-config, --strict-markers, xfail_strict, asyncio_mode = "auto", testpaths = "tests", and a 120-second per-test timeout. xfail_strict means an expected failure that unexpectedly passes is itself a failure, so an xfail you fixed must be deleted rather than left alone.
reactpy.testing exists because a UI is only correct once it renders in a browser. The acceptance bar is that the real artefact behaves, not that a function returned the expected shape.
DisplayFixture — serves a component and gives you a Playwright page to drive it: await display.show(my_component), then display.page.BackendFixture — the server side, usually paired with DisplayFixture as in conftest.py.poll(...) — retries a condition until it holds or the timeout expires. Use it instead of asyncio.sleep, which passes vacuously on a slow machine and flakes on a fast one.HookCatcher and StaticEventHandler — capture a component's life-cycle hook and an event handler's target, for delivering events without a click.capture_reactpy_logs, assert_reactpy_did_log, assert_reactpy_did_not_log — assert on server-side log output, which is where ReactPy puts swallowed handler and effect errors.conftest.py also installs an autouse fixture that fails a test if any ReactPy log record carries an exception. That is deliberate: an error logged server-side while the browser stays quiet is the most common way a ReactPy bug hides. If a new test trips it, the exception is real even when the assertion you wrote passes.
The client packages are tested with Bun:
1 +2 +3 +4 | |
Changes under src/js/ need a rebuild before Python tests can see them. The test script does this itself, but a manual run against a stale bundle will not reflect your edit.
1 +2 +3 | |
CI runs hatch fmt src/reactpy --check followed by hatch run python:type_check.
Pitfall
Run hatch fmt --check before you commit, not hatch fmt. Bare hatch fmt rewrites files, so a run that "passes" has already edited your working tree — including files you did not intend to touch. Scope it (hatch fmt docs/) if you only want part of the tree.
Formatting is Ruff at a line length of 88. The rules that bite most in this repository are set in pyproject.toml; docs/**/*.py already ignores E402, S102, T201, TID252, RUF059, and INP001, because example files are teaching snippets rather than importable modules.
Run these from the repository root before you commit.
Pitfall
Nothing checks the documentation for you. The only workflow on new-docs installs the requirements and runs mkdocs gh-deploy --force on push — which does not pass --strict, so a broken internal link exits 0 and deploys anyway. The commands below are the gate only because you run them; there is no CI to catch a miss, and the live site is where an uncaught one appears.
1 +2 | |
Plus the type check over the example files, which are real Python:
1 | |
mkdocs build --strict is the important one: it turns broken internal links and unresolvable includes into build failures, both of which abort with a nonzero exit. It does not fail on two things you would expect it to: a page missing from nav: (only an INFO line, and the build still exits 0 while serving the orphaned page), and a link whose #anchor does not exist inside an otherwise valid target file (also exit 0). Read that nav: list yourself and check anchors by eye. Nor can it tell you whether a snippet works: the examples in docs/examples/ are the source of truth for every snippet on the site, and a snippet that does not run is a docs bug the build will not catch on its own. That part is on you.
Pitfall
Do not judge a build by its log output alone. A grep-style check on the output hides a nonzero exit:
1 | |
In a bash pipeline, use echo "${PIPESTATUS[0]}" instead — a trailing | grep that finds a match makes the pipeline's exit code meaningless.
1 +2 | |
Then follow the printed URL. Editing any file under docs/src/ or docs/examples/ reloads the page.
The requirements file pins mkdocs-material==9.7.7. Earlier revisions pinned 9.4.0, which pulled in regex~=2022.4 and resolved to a release shipping wheels only through CPython 3.11 — on newer Pythons that meant a source build and a hard failure without a C compiler. The current pin dropped the regex dependency altogether, so the install works on 3.12 and up. If you are on an older branch and the docs install dies inside regex, that is the reason: use Python 3.11 or raise the pin.
Before calling anything done, run the gates in this order and read every failure:
hatch fmt --check docs/ — formatting and lintpyright docs/ — types in the examplescd docs && mkdocs build --strict — links, includes, navSteps 1–4 can all pass while the thing you built does not work. A status is not an acceptance test: run the artefact. For a docs example that means mounting it in a browser and confirming the DOM changed, because a ReactPy component can render, error server-side, and show a blank region while the HTTP response still reads 200.
Deep Dive
Two harness traps have made broken examples look green more than once. First, the decorated function is a constructor: counter itself has no .render() and Layout(counter) raises TypeError: Expected a ReactPy component, so you must pass an instance. The instance does have .render(), but calling it outside a Layout raises RuntimeError: No life cycle hook is active. Are you rendering in a layout? for any component that uses a hook — which silently skips the behaviour you meant to test. Render through async with Layout(counter()) as layout: await layout.render(). Second, ReactPy formats exception text into the model only when REACTPY_DEBUG is on; with it off, a component that raised renders as {"tagName": "", "error": ""} — an empty region and no message. Set the variable when you are probing failures, and unset it when you are checking what a user would actually see.
hatch test for ReactPy (on main), mkdocs build --strict for the docs.hatch test reporting no tests ran means the suite is not there, not that it passed.pytest starts.conftest.py forces debug mode and fails any test that logs an exception.hatch fmt --check, never bare hatch fmt, before committing.0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o