Merge branch 'main' into lendemor/test_stateless_app

This commit is contained in:
Lendemor 2024-10-23 17:46:15 +02:00
commit 001b01f541
463 changed files with 33180 additions and 33844 deletions

View File

@ -11,7 +11,7 @@ omit =
[report] [report]
show_missing = true show_missing = true
# TODO bump back to 79 # TODO bump back to 79
fail_under = 60 fail_under = 70
precision = 2 precision = 2
# Regexes for lines to exclude from consideration # Regexes for lines to exclude from consideration

View File

@ -18,7 +18,7 @@ inputs:
poetry-version: poetry-version:
description: 'Poetry version to install' description: 'Poetry version to install'
required: false required: false
default: '1.3.1' default: '1.8.3'
run-poetry-install: run-poetry-install:
description: 'Whether to run poetry install on current dir' description: 'Whether to run poetry install on current dir'
required: false required: false

View File

@ -81,17 +81,13 @@ jobs:
matrix: matrix:
# Show OS combos first in GUI # Show OS combos first in GUI
os: [ubuntu-latest, windows-latest, macos-12] os: [ubuntu-latest, windows-latest, macos-12]
python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0'] python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
exclude: exclude:
- os: windows-latest - os: windows-latest
python-version: '3.10.13' python-version: '3.10.13'
- os: windows-latest - os: windows-latest
python-version: '3.9.18' python-version: '3.9.18'
- os: windows-latest
python-version: '3.8.18'
# keep only one python version for MacOS # keep only one python version for MacOS
- os: macos-latest
python-version: '3.8.18'
- os: macos-latest - os: macos-latest
python-version: '3.9.18' python-version: '3.9.18'
- os: macos-latest - os: macos-latest
@ -103,8 +99,6 @@ jobs:
python-version: '3.10.11' python-version: '3.10.11'
- os: windows-latest - os: windows-latest
python-version: '3.9.13' python-version: '3.9.13'
- os: windows-latest
python-version: '3.8.10'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

40
.github/workflows/check_node_latest.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: integration-node-latest
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
TELEMETRY_ENABLED: false
REFLEX_USE_SYSTEM_NODE: true
jobs:
check_latest_node:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ['3.12']
node-version: ['node']
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env
with:
python-version: ${{ matrix.python-version }}
run-poetry-install: true
create-venv-at-path: .venv
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: |
poetry run uv pip install pyvirtualdisplay pillow
poetry run playwright install --with-deps
- run: |
poetry run pytest tests/test_node_version.py
poetry run pytest tests/integration

View File

@ -0,0 +1,88 @@
name: check-outdated-dependencies
on:
push: # This will trigger the action when a pull request is opened or updated.
branches:
- 'release/**' # This will trigger the action when any branch starting with "release/" is created.
workflow_dispatch: # Allow manual triggering if needed.
jobs:
backend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- uses: ./.github/actions/setup_build_env
with:
python-version: '3.9'
run-poetry-install: true
create-venv-at-path: .venv
- name: Check outdated backend dependencies
run: |
outdated=$(poetry show -oT)
echo "Outdated:"
echo "$outdated"
filtered_outdated=$(echo "$outdated" | grep -vE 'pyright|ruff' || true)
if [ ! -z "$filtered_outdated" ]; then
echo "Outdated dependencies found:"
echo "$filtered_outdated"
exit 1
else
echo "All dependencies are up to date. (pyright and ruff are ignored)"
fi
frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env
with:
python-version: '3.10.11'
run-poetry-install: true
create-venv-at-path: .venv
- name: Clone Reflex Website Repo
uses: actions/checkout@v4
with:
repository: reflex-dev/reflex-web
ref: main
path: reflex-web
- name: Install Requirements for reflex-web
working-directory: ./reflex-web
run: poetry run uv pip install -r requirements.txt
- name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg2-binary
- name: Init Website for reflex-web
working-directory: ./reflex-web
run: poetry run reflex init
- name: Run Website and Check for errors
run: |
poetry run bash scripts/integration.sh ./reflex-web dev
- name: Check outdated frontend dependencies
working-directory: ./reflex-web/.web
run: |
raw_outdated=$(/home/runner/.local/share/reflex/bun/bin/bun outdated)
outdated=$(echo "$raw_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\|' || true)
echo "Outdated:"
echo "$outdated"
# Ignore 3rd party dependencies that are not updated.
filtered_outdated=$(echo "$outdated" | grep -vE 'Package|@chakra-ui|lucide-react|@splinetool/runtime|ag-grid-react|framer-motion|react-markdown|remark-math|remark-gfm|rehype-katex|rehype-raw|remark-unwrap-images' || true)
no_extra=$(echo "$filtered_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-' || true)
if [ ! -z "$no_extra" ]; then
echo "Outdated dependencies found:"
echo "$filtered_outdated"
exit 1
else
echo "All dependencies are up to date. (3rd party packages are ignored)"
fi

View File

@ -23,8 +23,8 @@ jobs:
strategy: strategy:
matrix: matrix:
state_manager: ['redis', 'memory'] state_manager: ['redis', 'memory']
python-version: ['3.8.18', '3.11.5', '3.12.0'] python-version: ['3.11.5', '3.12.0']
runs-on: ubuntu-latest runs-on: ubuntu-22.04
services: services:
# Label used to access the service container # Label used to access the service container
redis: redis:
@ -47,16 +47,11 @@ jobs:
create-venv-at-path: .venv create-venv-at-path: .venv
- run: poetry run uv pip install pyvirtualdisplay pillow playwright pytest-playwright - run: poetry run uv pip install pyvirtualdisplay pillow playwright pytest-playwright
- name: Run app harness tests - name: Run app harness tests
uses: nick-fields/retry@v3
env: env:
SCREENSHOT_DIR: /tmp/screenshots SCREENSHOT_DIR: /tmp/screenshots
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
with: run: |
timeout_minutes: 30 poetry run pytest tests/integration
max_attempts: 2
command: |
poetry run playwright install --with-deps
poetry run pytest integration
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: Upload failed test screenshots name: Upload failed test screenshots
if: always() if: always()

View File

@ -42,22 +42,18 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
# Show OS combos first in GUI # Show OS combos first in GUI
os: [ubuntu-latest, windows-latest, macos-12] os: [ubuntu-latest, windows-latest]
python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0'] python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
exclude: exclude:
- os: windows-latest - os: windows-latest
python-version: '3.10.13' python-version: '3.10.13'
- os: windows-latest - os: windows-latest
python-version: '3.9.18' python-version: '3.9.18'
- os: windows-latest
python-version: '3.8.18'
include: include:
- os: windows-latest - os: windows-latest
python-version: '3.10.11' python-version: '3.10.11'
- os: windows-latest - os: windows-latest
python-version: '3.9.13' python-version: '3.9.13'
- os: windows-latest
python-version: '3.8.10'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
@ -126,7 +122,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
# Show OS combos first in GUI # Show OS combos first in GUI
os: [ubuntu-latest, windows-latest, macos-12] os: [ubuntu-latest, windows-latest]
python-version: ['3.10.11', '3.11.4'] python-version: ['3.10.11', '3.11.4']
env: env:
@ -156,11 +152,7 @@ jobs:
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run reflex init run: poetry run reflex init
- name: Run Website and Check for errors - name: Run Website and Check for errors
uses: nick-fields/retry@v3 run: |
with:
timeout_minutes: 30
max_attempts: 2
command: |
# Check that npm is home # Check that npm is home
npm -v npm -v
poetry run bash scripts/integration.sh ./reflex-web prod poetry run bash scripts/integration.sh ./reflex-web prod
@ -169,4 +161,45 @@ jobs:
poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}" poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
--pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
--app-name "reflex-web" --path ./reflex-web/.web --app-name "reflex-web" --path ./reflex-web/.web
reflex-web-macos:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
python-version: ['3.11.5', '3.12.0']
runs-on: macos-12
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env
with:
python-version: ${{ matrix.python-version }}
run-poetry-install: true
create-venv-at-path: .venv
- name: Clone Reflex Website Repo
uses: actions/checkout@v4
with:
repository: reflex-dev/reflex-web
ref: main
path: reflex-web
- name: Install Requirements for reflex-web
working-directory: ./reflex-web
run: poetry run uv pip install -r requirements.txt
- name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg2-binary
- name: Init Website for reflex-web
working-directory: ./reflex-web
run: poetry run reflex init
- name: Run Website and Check for errors
run: |
# Check that npm is home
npm -v
poetry run bash scripts/integration.sh ./reflex-web prod
- name: Measure and upload .web size
run:
poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
--pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
--app-name "reflex-web" --path ./reflex-web/.web

View File

@ -37,6 +37,8 @@ jobs:
path: reflex-examples path: reflex-examples
- uses: Vampire/setup-wsl@v3 - uses: Vampire/setup-wsl@v3
with:
distribution: Ubuntu-24.04
- name: Install Python - name: Install Python
shell: wsl-bash {0} shell: wsl-bash {0}

View File

@ -28,5 +28,5 @@ jobs:
# Run reflex init in a docker container # Run reflex init in a docker container
# cwd is repo root # cwd is repo root
docker build -f integration/init-test/Dockerfile -t reflex-init-test integration/init-test docker build -f tests/integration/init-test/Dockerfile -t reflex-init-test tests/integration/init-test
docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/integration/init-test/in_docker_test_script.sh docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/tests/integration/init-test/in_docker_test_script.sh

View File

@ -27,24 +27,21 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-12] os: [ubuntu-latest, windows-latest]
python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0'] python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
# Windows is a bit behind on Python version availability in Github # Windows is a bit behind on Python version availability in Github
exclude: exclude:
- os: windows-latest - os: windows-latest
python-version: '3.10.13' python-version: '3.10.13'
- os: windows-latest - os: windows-latest
python-version: '3.9.18' python-version: '3.9.18'
- os: windows-latest
python-version: '3.8.18'
include: include:
- os: windows-latest - os: windows-latest
python-version: '3.10.11' python-version: '3.10.11'
- os: windows-latest - os: windows-latest
python-version: '3.9.13' python-version: '3.9.13'
- os: windows-latest
python-version: '3.8.10'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
# Service containers to run with `runner-job` # Service containers to run with `runner-job`
services: services:
# Label used to access the service container # Label used to access the service container
@ -69,17 +66,43 @@ jobs:
- name: Run unit tests - name: Run unit tests
run: | run: |
export PYTHONUNBUFFERED=1 export PYTHONUNBUFFERED=1
poetry run pytest tests --cov --no-cov-on-fail --cov-report= poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
- name: Run unit tests w/ redis - name: Run unit tests w/ redis
if: ${{ matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'ubuntu-latest' }}
run: | run: |
export PYTHONUNBUFFERED=1 export PYTHONUNBUFFERED=1
export REDIS_URL=redis://localhost:6379 export REDIS_URL=redis://localhost:6379
poetry run pytest tests --cov --no-cov-on-fail --cov-report= poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
# Change to explicitly install v1 when reflex-hosting-cli is compatible with v2 # Change to explicitly install v1 when reflex-hosting-cli is compatible with v2
- name: Run unit tests w/ pydantic v1 - name: Run unit tests w/ pydantic v1
run: | run: |
export PYTHONUNBUFFERED=1 export PYTHONUNBUFFERED=1
poetry run uv pip install "pydantic~=1.10" poetry run uv pip install "pydantic~=1.10"
poetry run pytest tests --cov --no-cov-on-fail --cov-report= poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
- run: poetry run coverage html - name: Generate coverage report
run: poetry run coverage html
unit-tests-macos:
timeout-minutes: 30
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
runs-on: macos-12
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env
with:
python-version: ${{ matrix.python-version }}
run-poetry-install: true
create-venv-at-path: .venv
- name: Run unit tests
run: |
export PYTHONUNBUFFERED=1
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
- name: Run unit tests w/ pydantic v1
run: |
export PYTHONUNBUFFERED=1
poetry run uv pip install "pydantic~=1.10"
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=

View File

@ -3,10 +3,10 @@ fail_fast: true
repos: repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit - repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.4.10 rev: v0.7.0
hooks: hooks:
- id: ruff-format - id: ruff-format
args: [integration, reflex, tests] args: [reflex, tests]
- id: ruff - id: ruff
args: ["--fix", "--exit-non-zero-on-fix"] args: ["--fix", "--exit-non-zero-on-fix"]
exclude: '^integration/benchmarks/' exclude: '^integration/benchmarks/'
@ -25,7 +25,7 @@ repos:
rev: v1.1.313 rev: v1.1.313
hooks: hooks:
- id: pyright - id: pyright
args: [integration, reflex, tests] args: [reflex, tests]
language: system language: system
- repo: https://github.com/terrencepreilly/darglint - repo: https://github.com/terrencepreilly/darglint

View File

@ -8,7 +8,7 @@ Here is a quick guide on how to run Reflex repo locally so you can start contrib
**Prerequisites:** **Prerequisites:**
- Python >= 3.8 - Python >= 3.9
- Poetry version >= 1.4.0 and add it to your path (see [Poetry Docs](https://python-poetry.org/docs/#installation) for more info). - Poetry version >= 1.4.0 and add it to your path (see [Poetry Docs](https://python-poetry.org/docs/#installation) for more info).
**1. Fork this repository:** **1. Fork this repository:**
@ -69,7 +69,7 @@ In your `reflex` directory run make sure all the unit tests are still passing us
This will fail if code coverage is below 70%. This will fail if code coverage is below 70%.
``` bash ``` bash
poetry run pytest tests --cov --no-cov-on-fail --cov-report= poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
``` ```
Next make sure all the following tests pass. This ensures that every new change has proper documentation and type checking. Next make sure all the following tests pass. This ensures that every new change has proper documentation and type checking.
@ -87,7 +87,7 @@ poetry run ruff format .
``` ```
Consider installing git pre-commit hooks so Ruff, Pyright, Darglint and `make_pyi` will run automatically before each commit. Consider installing git pre-commit hooks so Ruff, Pyright, Darglint and `make_pyi` will run automatically before each commit.
Note that pre-commit will only be installed when you use a Python version >= 3.8. Note that pre-commit will only be installed when you use a Python version >= 3.9.
``` bash ``` bash
pre-commit install pre-commit install

View File

@ -10,7 +10,6 @@
### **✨ Performant, customizable web apps in pure Python. Deploy in seconds. ✨** ### **✨ Performant, customizable web apps in pure Python. Deploy in seconds. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -18,7 +17,7 @@
--- ---
[English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md)
--- ---
@ -35,7 +34,7 @@ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architectu
## ⚙️ Installation ## ⚙️ Installation
Open a terminal and run (Requires Python 3.8+): Open a terminal and run (Requires Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -3,8 +3,8 @@
from __future__ import annotations from __future__ import annotations
import json import json
import os
import sys import sys
from pathlib import Path
from utils import send_data_to_posthog from utils import send_data_to_posthog
@ -28,7 +28,7 @@ def insert_benchmarking_data(
send_data_to_posthog("lighthouse_benchmark", properties) send_data_to_posthog("lighthouse_benchmark", properties)
def get_lighthouse_scores(directory_path: str) -> dict: def get_lighthouse_scores(directory_path: str | Path) -> dict:
"""Extracts the Lighthouse scores from the JSON files in the specified directory. """Extracts the Lighthouse scores from the JSON files in the specified directory.
Args: Args:
@ -38,24 +38,21 @@ def get_lighthouse_scores(directory_path: str) -> dict:
dict: The Lighthouse scores. dict: The Lighthouse scores.
""" """
scores = {} scores = {}
directory_path = Path(directory_path)
try: try:
for filename in os.listdir(directory_path): for filename in directory_path.iterdir():
if filename.endswith(".json") and filename != "manifest.json": if filename.suffix == ".json" and filename.stem != "manifest":
file_path = os.path.join(directory_path, filename) file_path = directory_path / filename
with open(file_path, "r") as file: data = json.loads(file_path.read_text())
data = json.load(file) # Extract scores and add them to the dictionary with the filename as key
# Extract scores and add them to the dictionary with the filename as key scores[data["finalUrl"].replace("http://localhost:3000/", "/")] = {
scores[data["finalUrl"].replace("http://localhost:3000/", "/")] = { "performance_score": data["categories"]["performance"]["score"],
"performance_score": data["categories"]["performance"]["score"], "accessibility_score": data["categories"]["accessibility"]["score"],
"accessibility_score": data["categories"]["accessibility"][ "best_practices_score": data["categories"]["best-practices"][
"score" "score"
], ],
"best_practices_score": data["categories"]["best-practices"][ "seo_score": data["categories"]["seo"]["score"],
"score" }
],
"seo_score": data["categories"]["seo"]["score"],
}
except Exception as e: except Exception as e:
return {"error": e} return {"error": e}

View File

@ -2,11 +2,12 @@
import argparse import argparse
import os import os
from pathlib import Path
from utils import get_directory_size, get_python_version, send_data_to_posthog from utils import get_directory_size, get_python_version, send_data_to_posthog
def get_package_size(venv_path, os_name): def get_package_size(venv_path: Path, os_name):
"""Get the size of a specified package. """Get the size of a specified package.
Args: Args:
@ -26,14 +27,12 @@ def get_package_size(venv_path, os_name):
is_windows = "windows" in os_name is_windows = "windows" in os_name
full_path = ( package_dir: Path = (
["lib", f"python{python_version}", "site-packages"] venv_path / "lib" / f"python{python_version}" / "site-packages"
if not is_windows if not is_windows
else ["Lib", "site-packages"] else venv_path / "Lib" / "site-packages"
) )
if not package_dir.exists():
package_dir = os.path.join(venv_path, *full_path)
if not os.path.exists(package_dir):
raise ValueError( raise ValueError(
"Error: Virtual environment does not exist or is not activated." "Error: Virtual environment does not exist or is not activated."
) )
@ -63,9 +62,9 @@ def insert_benchmarking_data(
path: The path to the dir or file to check size. path: The path to the dir or file to check size.
""" """
if "./dist" in path: if "./dist" in path:
size = get_directory_size(path) size = get_directory_size(Path(path))
else: else:
size = get_package_size(path, os_type_version) size = get_package_size(Path(path), os_type_version)
# Prepare the event data # Prepare the event data
properties = { properties = {

View File

@ -2,6 +2,7 @@
import argparse import argparse
import os import os
from pathlib import Path
from utils import get_directory_size, send_data_to_posthog from utils import get_directory_size, send_data_to_posthog
@ -28,7 +29,7 @@ def insert_benchmarking_data(
pr_id: The id of the PR. pr_id: The id of the PR.
path: The path to the dir or file to check size. path: The path to the dir or file to check size.
""" """
size = get_directory_size(path) size = get_directory_size(Path(path))
# Prepare the event data # Prepare the event data
properties = { properties = {

View File

@ -130,7 +130,6 @@ def render_multiple_pages(app, num: int):
def AppWithOnePage(): def AppWithOnePage():
"""A reflex app with one page.""" """A reflex app with one page."""
import reflex_chakra as rc
from rxconfig import config # type: ignore from rxconfig import config # type: ignore
import reflex as rx import reflex as rx
@ -145,7 +144,7 @@ def AppWithOnePage():
def index() -> rx.Component: def index() -> rx.Component:
return rx.center( return rx.center(
rc.input( rx.input(
id="token", value=State.router.session.client_token, is_read_only=True id="token", value=State.router.session.client_token, is_read_only=True
), ),
rx.vstack( rx.vstack(

View File

@ -2,12 +2,13 @@
import os import os
import subprocess import subprocess
from pathlib import Path
import httpx import httpx
from httpx import HTTPError from httpx import HTTPError
def get_python_version(venv_path, os_name): def get_python_version(venv_path: Path, os_name):
"""Get the python version of python in a virtual env. """Get the python version of python in a virtual env.
Args: Args:
@ -18,13 +19,13 @@ def get_python_version(venv_path, os_name):
The python version. The python version.
""" """
python_executable = ( python_executable = (
os.path.join(venv_path, "bin", "python") venv_path / "bin" / "python"
if "windows" not in os_name if "windows" not in os_name
else os.path.join(venv_path, "Scripts", "python.exe") else venv_path / "Scripts" / "python.exe"
) )
try: try:
output = subprocess.check_output( output = subprocess.check_output(
[python_executable, "--version"], stderr=subprocess.STDOUT [str(python_executable), "--version"], stderr=subprocess.STDOUT
) )
python_version = output.decode("utf-8").strip().split()[1] python_version = output.decode("utf-8").strip().split()[1]
return ".".join(python_version.split(".")[:-1]) return ".".join(python_version.split(".")[:-1])
@ -32,7 +33,7 @@ def get_python_version(venv_path, os_name):
return None return None
def get_directory_size(directory): def get_directory_size(directory: Path):
"""Get the size of a directory in bytes. """Get the size of a directory in bytes.
Args: Args:
@ -44,8 +45,8 @@ def get_directory_size(directory):
total_size = 0 total_size = 0
for dirpath, _, filenames in os.walk(directory): for dirpath, _, filenames in os.walk(directory):
for f in filenames: for f in filenames:
fp = os.path.join(dirpath, f) fp = Path(dirpath) / f
total_size += os.path.getsize(fp) total_size += fp.stat().st_size
return total_size return total_size

View File

@ -1,133 +1,30 @@
# Reflex Docker Container # Reflex Docker Examples
This example describes how to create and use a container image for Reflex with your own code. This directory contains several examples of how to deploy Reflex apps using docker.
## Update Requirements In all cases, ensure that your `requirements.txt` file is up to date and
includes the `reflex` package.
The `requirements.txt` includes the reflex package which is needed to install ## `simple-two-port`
Reflex framework. If you use additional packages in your project you have to add
this in the `requirements.txt` first. Copy the `Dockerfile`, `.dockerignore` and
the `requirements.txt` file in your project folder.
## Build Simple Reflex Container Image The most basic production deployment exposes two HTTP ports and relies on an
existing load balancer to forward the traffic appropriately.
The main `Dockerfile` is intended to build a very simple, single container deployment that runs ## `simple-one-port`
the Reflex frontend and backend together, exposing ports 3000 and 8000.
To build your container image run the following command: This deployment exports the frontend statically and serves it via a single HTTP
port using Caddy. This is useful for platforms that only support a single port
or where running a node server in the container is undesirable.
```bash ## `production-compose`
docker build -t reflex-app:latest .
```
## Start Container Service This deployment is intended for use with a standalone VPS that is only hosting a
single Reflex app. It provides the entire stack in a single `compose.yaml`
including a webserver, one or more backend instances, redis, and a postgres
database.
Finally, you can start your Reflex container service as follows: ## `production-app-platform`
```bash This example deployment is intended for use with App hosting platforms, like
docker run -it --rm -p 3000:3000 -p 8000:8000 --name app reflex-app:latest Azure, AWS, or Google Cloud Run. It is the backend of the deployment, which
``` depends on a separately hosted redis instance and static frontend deployment.
It may take a few seconds for the service to become available.
Access your app at http://localhost:3000.
Note that this container has _no persistence_ and will lose all data when
stopped. You can use bind mounts or named volumes to persist the database and
uploaded_files directories as needed.
# Production Service with Docker Compose and Caddy
An example production deployment uses automatic TLS with Caddy serving static files
for the frontend and proxying requests to both the frontend and backend.
Copy the following files to your project directory:
* `compose.yaml`
* `compose.prod.yaml`
* `compose.tools.yaml`
* `prod.Dockerfile`
* `Caddy.Dockerfile`
* `Caddyfile`
The production app container, based on `prod.Dockerfile`, builds and exports the
frontend statically (to be served by Caddy). The resulting image only runs the
backend service.
The `webserver` service, based on `Caddy.Dockerfile`, copies the static frontend
and `Caddyfile` into the container to configure the reverse proxy routes that will
forward requests to the backend service. Caddy will automatically provision TLS
for localhost or the domain specified in the environment variable `DOMAIN`.
This type of deployment should use less memory and be more performant since
nodejs is not required at runtime.
## Customize `Caddyfile` (optional)
If the app uses additional backend API routes, those should be added to the
`@backend_routes` path matcher to ensure they are forwarded to the backend.
## Build Reflex Production Service
During build, set `DOMAIN` environment variable to the domain where the app will
be hosted! (Do not include http or https, it will always use https).
**If `DOMAIN` is not provided, the service will default to `localhost`.**
```bash
DOMAIN=example.com docker compose build
```
This will build both the `app` service from the `prod.Dockerfile` and the `webserver`
service via `Caddy.Dockerfile`.
## Run Reflex Production Service
```bash
DOMAIN=example.com docker compose up
```
The app should be available at the specified domain via HTTPS. Certificate
provisioning will occur automatically and may take a few minutes.
### Data Persistence
Named docker volumes are used to persist the app database (`db-data`),
uploaded_files (`upload-data`), and caddy TLS keys and certificates
(`caddy-data`).
## More Robust Deployment
For a more robust deployment, consider bringing the service up with
`compose.prod.yaml` which includes postgres database and redis cache, allowing
the backend to run with multiple workers and service more requests.
```bash
DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml up -d
```
Postgres uses its own named docker volume for data persistence.
## Admin Tools
When needed, the services in `compose.tools.yaml` can be brought up, providing
graphical database administration (Adminer on http://localhost:8080) and a
redis cache browser (redis-commander on http://localhost:8081). It is not recommended
to deploy these services if they are not in active use.
```bash
DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml -f compose.tools.yaml up -d
```
# Container Hosting
Most container hosting services automatically terminate TLS and expect the app
to be listening on a single port (typically `$PORT`).
To host a Reflex app on one of these platforms, like Google Cloud Run, Render,
Railway, etc, use `app.Dockerfile` to build a single image containing a reverse
proxy that will serve that frontend as static files and proxy requests to the
backend for specific endpoints.
If the chosen platform does not support buildx and thus heredoc, you can copy
the Caddyfile configuration into a separate Caddyfile in the root of the
project.

View File

@ -0,0 +1,5 @@
.web
.git
__pycache__/*
Dockerfile
uploaded_files

View File

@ -0,0 +1,65 @@
# This docker file is intended to be used with container hosting services
#
# After deploying this image, get the URL pointing to the backend service
# and run API_URL=https://path-to-my-container.example.com reflex export frontend
# then copy the contents of `frontend.zip` to your static file server (github pages, s3, etc).
#
# Azure Static Web App example:
# npx @azure/static-web-apps-cli deploy --env production --app-location .web/_static
#
# For dynamic routes to function properly, ensure that 404s are redirected to /404 on the
# static file host (for github pages, this works out of the box; remember to create .nojekyll).
#
# For azure static web apps, add `staticwebapp.config.json` to to `.web/_static` with the following:
# {
# "responseOverrides": {
# "404": {
# "rewrite": "/404.html"
# }
# }
# }
#
# Note: many container hosting platforms require amd64 images, so when building on an M1 Mac
# for example, pass `docker build --platform=linux/amd64 ...`
# Stage 1: init
FROM python:3.11 as init
ARG uv=/root/.cargo/bin/uv
# Install `uv` for faster package boostrapping
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
RUN /install.sh && rm /install.sh
# Copy local context to `/app` inside container (see .dockerignore)
WORKDIR /app
COPY . .
RUN mkdir -p /app/data /app/uploaded_files
# Create virtualenv which will be copied into final container
ENV VIRTUAL_ENV=/app/.venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
RUN $uv venv
# Install app requirements and reflex inside virtualenv
RUN $uv pip install -r requirements.txt
# Deploy templates and prepare app
RUN reflex init
# Stage 2: copy artifacts into slim image
FROM python:3.11-slim
WORKDIR /app
RUN adduser --disabled-password --home /app reflex
COPY --chown=reflex --from=init /app /app
# Install libpq-dev for psycopg2 (skip if not using postgres).
RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/*
USER reflex
ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1
# Needed until Reflex properly passes SIGTERM on backend.
STOPSIGNAL SIGKILL
# Always apply migrations before starting the backend.
CMD [ -d alembic ] && reflex db migrate; \
exec reflex run --env prod --backend-only --backend-port ${PORT:-8000}

View File

@ -0,0 +1,113 @@
# production-app-platform
This example deployment is intended for use with App hosting platforms, like
Azure, AWS, or Google Cloud Run.
## Architecture
The production deployment consists of a few pieces:
* Backend container - built by `Dockerfile` Runs the Reflex backend
service on port 8000 and is scalable to multiple instances.
* Redis container - A single instance the standard `redis` docker image should
share private networking with the backend
* Static frontend - HTML/CSS/JS files that are hosted via a CDN or static file
server. This is not included in the docker image.
## Deployment
These general steps do not cover the specifics of each platform, but all platforms should
support the concepts described here.
### Vnet
All containers in the deployment should be hooked up to the same virtual private
network so they can access the redis service and optionally the database server.
The vnet should not be exposed to the internet, use an ingress rule to terminate
TLS at the load balancer and forward the traffic to a backend service replica.
### Redis
Deploy a `redis` instance on the vnet.
### Backend
The backend is built by the `Dockerfile` in this directory. When deploying the
backend, be sure to set REDIS_URL=redis://internal-redis-hostname to connect to
the redis service.
### Ingress
Configure the load balancer for the app to forward traffic to port 8000 on the
backend service replicas. Most platforms will generate an ingress hostname
automatically. Make sure when you access the ingress endpoint on `/ping` that it
returns "pong", indicating that the backend is up an available.
### Frontend
The frontend should be hosted on a static file server or CDN.
**Important**: when exporting the frontend, set the API_URL environment variable
to the ingress hostname of the backend service.
If you will host the frontend from a path other than the root, set the
`FRONTEND_PATH` environment variable appropriately when exporting the frontend.
Most static hosts will automatically use the `/404.html` file to handle 404
errors. _This is essential for dynamic routes to work correctly._ Ensure that
missing routes return the `/404.html` content to the user if this is not the
default behavior.
_For Github Pages_: ensure the file `.nojekyll` is present in the root of the repo
to avoid special processing of underscore-prefix directories, like `_next`.
## Platform Notes
The following sections are currently a work in progress and may be incomplete.
### Azure
In the Azure load balancer, per-message deflate is not supported. Add the following
to your `rxconfig.py` to workaround this issue.
```python
import uvicorn.workers
import reflex as rx
class NoWSPerMessageDeflate(uvicorn.workers.UvicornH11Worker):
CONFIG_KWARGS = {
**uvicorn.workers.UvicornH11Worker.CONFIG_KWARGS,
"ws_per_message_deflate": False,
}
config = rx.Config(
app_name="my_app",
gunicorn_worker_class="rxconfig.NoWSPerMessageDeflate",
)
```
#### Persistent Storage
If you need to use a database or upload files, you cannot save them to the
container volume. Use Azure Files and mount it into the container at /app/uploaded_files.
#### Resource Types
* Create a new vnet with 10.0.0.0/16
* Create a new subnet for redis, database, and containers
* Deploy redis as a Container Instances
* Deploy database server as "Azure Database for PostgreSQL"
* Create a new database for the app
* Set db-url as a secret containing the db user/password connection string
* Deploy Storage account for uploaded files
* Enable access from the vnet and container subnet
* Create a new file share
* In the environment, create a new files share (get the storage key)
* Deploy the backend as a Container App
* Create a custom Container App Environment linked up to the same vnet as the redis container.
* Set REDIS_URL and DB_URL environment variables
* Add the volume from the environment
* Add the volume mount to the container
* Deploy the frontend as a Static Web App

View File

@ -42,10 +42,11 @@ COPY --chown=reflex --from=init /app /app
# Install libpq-dev for psycopg2 (skip if not using postgres). # Install libpq-dev for psycopg2 (skip if not using postgres).
RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/*
USER reflex USER reflex
ENV PATH="/app/.venv/bin:$PATH" ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1
# Needed until Reflex properly passes SIGTERM on backend. # Needed until Reflex properly passes SIGTERM on backend.
STOPSIGNAL SIGKILL STOPSIGNAL SIGKILL
# Always apply migrations before starting the backend. # Always apply migrations before starting the backend.
CMD reflex db migrate && reflex run --env prod --backend-only CMD [ -d alembic ] && reflex db migrate; \
exec reflex run --env prod --backend-only

View File

@ -0,0 +1,75 @@
# production-compose
This example production deployment uses automatic TLS with Caddy serving static
files for the frontend and proxying requests to both the frontend and backend.
It is intended for use with a standalone VPS that is only hosting a single
Reflex app.
The production app container (`Dockerfile`), builds and exports the frontend
statically (to be served by Caddy). The resulting image only runs the backend
service.
The `webserver` service, based on `Caddy.Dockerfile`, copies the static frontend
and `Caddyfile` into the container to configure the reverse proxy routes that will
forward requests to the backend service. Caddy will automatically provision TLS
for localhost or the domain specified in the environment variable `DOMAIN`.
This type of deployment should use less memory and be more performant since
nodejs is not required at runtime.
## Customize `Caddyfile` (optional)
If the app uses additional backend API routes, those should be added to the
`@backend_routes` path matcher to ensure they are forwarded to the backend.
## Build Reflex Production Service
During build, set `DOMAIN` environment variable to the domain where the app will
be hosted! (Do not include http or https, it will always use https).
**If `DOMAIN` is not provided, the service will default to `localhost`.**
```bash
DOMAIN=example.com docker compose build
```
This will build both the `app` service from the `prod.Dockerfile` and the `webserver`
service via `Caddy.Dockerfile`.
## Run Reflex Production Service
```bash
DOMAIN=example.com docker compose up
```
The app should be available at the specified domain via HTTPS. Certificate
provisioning will occur automatically and may take a few minutes.
### Data Persistence
Named docker volumes are used to persist the app database (`db-data`),
uploaded_files (`upload-data`), and caddy TLS keys and certificates
(`caddy-data`).
## More Robust Deployment
For a more robust deployment, consider bringing the service up with
`compose.prod.yaml` which includes postgres database and redis cache, allowing
the backend to run with multiple workers and service more requests.
```bash
DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml up -d
```
Postgres uses its own named docker volume for data persistence.
## Admin Tools
When needed, the services in `compose.tools.yaml` can be brought up, providing
graphical database administration (Adminer on http://localhost:8080) and a
redis cache browser (redis-commander on http://localhost:8081). It is not recommended
to deploy these services if they are not in active use.
```bash
DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml -f compose.tools.yaml up -d
```

View File

@ -12,7 +12,6 @@ services:
DB_URL: sqlite:///data/reflex.db DB_URL: sqlite:///data/reflex.db
build: build:
context: . context: .
dockerfile: prod.Dockerfile
volumes: volumes:
- db-data:/app/data - db-data:/app/data
- upload-data:/app/uploaded_files - upload-data:/app/uploaded_files

View File

@ -1 +0,0 @@
reflex

View File

@ -0,0 +1,5 @@
.web
.git
__pycache__/*
Dockerfile
uploaded_files

View File

@ -0,0 +1,14 @@
:{$PORT}
encode gzip
@backend_routes path /_event/* /ping /_upload /_upload/*
handle @backend_routes {
reverse_proxy localhost:8000
}
root * /srv
route {
try_files {path} {path}/ /404.html
file_server
}

View File

@ -10,31 +10,13 @@ FROM python:3.11
ARG PORT=8080 ARG PORT=8080
# Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend. # Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend.
ARG API_URL ARG API_URL
ENV PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} ENV PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} REDIS_URL=redis://localhost PYTHONUNBUFFERED=1
# Install Caddy server inside image # Install Caddy and redis server inside image
RUN apt-get update -y && apt-get install -y caddy && rm -rf /var/lib/apt/lists/* RUN apt-get update -y && apt-get install -y caddy redis-server && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
# Create a simple Caddyfile to serve as reverse proxy
RUN cat > Caddyfile <<EOF
:{\$PORT}
encode gzip
@backend_routes path /_event/* /ping /_upload /_upload/*
handle @backend_routes {
reverse_proxy localhost:8000
}
root * /srv
route {
try_files {path} {path}/ /404.html
file_server
}
EOF
# Copy local context to `/app` inside container (see .dockerignore) # Copy local context to `/app` inside container (see .dockerignore)
COPY . . COPY . .
@ -54,4 +36,6 @@ EXPOSE $PORT
# Apply migrations before starting the backend. # Apply migrations before starting the backend.
CMD [ -d alembic ] && reflex db migrate; \ CMD [ -d alembic ] && reflex db migrate; \
caddy start && reflex run --env prod --backend-only --loglevel debug caddy start && \
redis-server --daemonize yes && \
exec reflex run --env prod --backend-only

View File

@ -0,0 +1,36 @@
# simple-one-port
This docker deployment runs Reflex in prod mode, exposing a single HTTP port:
* `8080` (`$PORT`) - Caddy server hosting the frontend statically and proxying requests to the backend.
The deployment also runs a local Redis server to store state for each user.
Using this method may be preferable for deploying in memory constrained
environments, because it serves a static frontend export, rather than running
the NextJS server via node.
For platforms which only terminate TLS to a single port, this container can be
deployed instead of the `simple-two-port` example.
## Build
```console
docker build -t reflex-simple-one-port .
```
## Run
```console
docker run -p 8080:8080 reflex-simple-one-port
```
Note that this container has _no persistence_ and will lose all data when
stopped. You can use bind mounts or named volumes to persist the database and
uploaded_files directories as needed.
## Usage
This container should be used with an existing load balancer or reverse proxy to
terminate TLS.
It is also useful for deploying to simple app platforms, such as Render or Heroku.

View File

@ -0,0 +1,5 @@
.web
.git
__pycache__/*
Dockerfile
uploaded_files

View File

@ -1,5 +1,8 @@
# This Dockerfile is used to deploy a simple single-container Reflex app instance. # This Dockerfile is used to deploy a simple single-container Reflex app instance.
FROM python:3.11 FROM python:3.12
RUN apt-get update && apt-get install -y redis-server && rm -rf /var/lib/apt/lists/*
ENV REDIS_URL=redis://localhost PYTHONUNBUFFERED=1
# Copy local context to `/app` inside container (see .dockerignore) # Copy local context to `/app` inside container (see .dockerignore)
WORKDIR /app WORKDIR /app
@ -18,4 +21,6 @@ RUN reflex export --frontend-only --no-zip
STOPSIGNAL SIGKILL STOPSIGNAL SIGKILL
# Always apply migrations before starting the backend. # Always apply migrations before starting the backend.
CMD [ -d alembic ] && reflex db migrate; reflex run --env prod CMD [ -d alembic ] && reflex db migrate; \
redis-server --daemonize yes && \
exec reflex run --env prod

View File

@ -0,0 +1,44 @@
# simple-two-port
This docker deployment runs Reflex in prod mode, exposing two HTTP ports:
* `3000` - node NextJS server using optimized production build
* `8000` - python gunicorn server hosting the Reflex backend
The deployment also runs a local Redis server to store state for each user.
## Build
```console
docker build -t reflex-simple-two-port .
```
## Run
```console
docker run -p 3000:3000 -p 8000:8000 reflex-simple-two-port
```
Note that this container has _no persistence_ and will lose all data when
stopped. You can use bind mounts or named volumes to persist the database and
uploaded_files directories as needed.
## Usage
This container should be used with an existing load balancer or reverse proxy to
route traffic to the appropriate port inside the container.
For example, the following Caddyfile can be used to terminate TLS and forward
traffic to the frontend and backend from outside the container.
```
my-domain.com
encode gzip
@backend_routes path /_event/* /ping /_upload /_upload/*
handle @backend_routes {
reverse_proxy localhost:8000
}
reverse_proxy localhost:3000
```

View File

@ -10,7 +10,6 @@
### **✨ Performante, anpassbare Web-Apps in purem Python. Bereitstellung in Sekunden. ✨** ### **✨ Performante, anpassbare Web-Apps in purem Python. Bereitstellung in Sekunden. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -35,7 +34,7 @@ Auf unserer [Architektur-Seite](https://reflex.dev/blog/2024-03-21-reflex-archit
## ⚙️ Installation ## ⚙️ Installation
Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.8+): Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -35,7 +35,7 @@ Consulta nuestra [página de arquitectura](https://reflex.dev/blog/2024-03-21-re
## ⚙️ Instalación ## ⚙️ Instalación
Abra un terminal y ejecute (Requiere Python 3.8+): Abra un terminal y ejecute (Requiere Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -11,7 +11,6 @@ Pynecone की तलाश हैं? आप सही रेपो में
### **✨ प्रदर्शनकारी, अनुकूलित वेब ऐप्स, शुद्ध Python में। सेकंडों में तैनात करें। ✨** ### **✨ प्रदर्शनकारी, अनुकूलित वेब ऐप्स, शुद्ध Python में। सेकंडों में तैनात करें। ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -36,7 +35,7 @@ Reflex के अंदर के कामकाज को जानने क
## ⚙️ इंस्टॉलेशन (Installation) ## ⚙️ इंस्टॉलेशन (Installation)
एक टर्मिनल खोलें और चलाएं (Python 3.8+ की आवश्यकता है): एक टर्मिनल खोलें और चलाएं (Python 3.9+ की आवश्यकता है):
```bash ```bash
pip install reflex pip install reflex

View File

@ -10,7 +10,6 @@
### **✨ App web performanti e personalizzabili in puro Python. Distribuisci in pochi secondi. ✨** ### **✨ App web performanti e personalizzabili in puro Python. Distribuisci in pochi secondi. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -23,7 +22,7 @@
## ⚙️ Installazione ## ⚙️ Installazione
Apri un terminale ed esegui (Richiede Python 3.8+): Apri un terminale ed esegui (Richiede Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -11,7 +11,6 @@
### **✨ 即時デプロイが可能な、Pure Python で作ったパフォーマンスと汎用性が高い Web アプリケーション ✨** ### **✨ 即時デプロイが可能な、Pure Python で作ったパフォーマンスと汎用性が高い Web アプリケーション ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -38,7 +37,7 @@ Reflex がどのように動作しているかを知るには、[アーキテク
## ⚙️ インストール ## ⚙️ インストール
ターミナルを開いて以下のコマンドを実行してください。Python 3.8 以上が必要です。): ターミナルを開いて以下のコマンドを実行してください。Python 3.9 以上が必要です。):
```bash ```bash
pip install reflex pip install reflex

View File

@ -10,7 +10,6 @@
### **✨ 순수 Python으로 고성능 사용자 정의 웹앱을 만들어 보세요. 몇 초만에 배포 가능합니다. ✨** ### **✨ 순수 Python으로 고성능 사용자 정의 웹앱을 만들어 보세요. 몇 초만에 배포 가능합니다. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -21,7 +20,7 @@
--- ---
## ⚙️ 설치 ## ⚙️ 설치
터미널을 열고 실행하세요. (Python 3.8+ 필요): 터미널을 열고 실행하세요. (Python 3.9+ 필요):
```bash ```bash
pip install reflex pip install reflex

View File

@ -10,7 +10,6 @@
### **✨ برنامه های تحت وب قابل تنظیم، کارآمد تماما پایتونی که در چند ثانیه مستقر(دپلوی) می‎شود. ✨** ### **✨ برنامه های تحت وب قابل تنظیم، کارآمد تماما پایتونی که در چند ثانیه مستقر(دپلوی) می‎شود. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -35,7 +34,7 @@
## ⚙️ Installation - نصب و راه اندازی ## ⚙️ Installation - نصب و راه اندازی
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.8+): یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -21,7 +21,7 @@
--- ---
## ⚙️ Instalação ## ⚙️ Instalação
Abra um terminal e execute (Requer Python 3.8+): Abra um terminal e execute (Requer Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -11,7 +11,6 @@
### **✨ Saf Python'da performanslı, özelleştirilebilir web uygulamaları. Saniyeler içinde dağıtın. ✨** ### **✨ Saf Python'da performanslı, özelleştirilebilir web uygulamaları. Saniyeler içinde dağıtın. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -25,7 +24,7 @@
## ⚙️ Kurulum ## ⚙️ Kurulum
Bir terminal açın ve çalıştırın (Python 3.8+ gerekir): Bir terminal açın ve çalıştırın (Python 3.9+ gerekir):
```bash ```bash
pip install reflex pip install reflex

267
docs/vi/README.md Normal file
View File

@ -0,0 +1,267 @@
```diff
+ Bạn đang tìm kiếm Pynecone? Bạn đã tìm đúng. Pynecone đã được đổi tên thành Reflex. +
```
<div align="center">
<img src="https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/images/reflex_dark.svg#gh-light-mode-only" alt="Reflex Logo" width="300px">
<img src="https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/images/reflex_light.svg#gh-dark-mode-only" alt="Reflex Logo" width="300px">
<hr>
### **✨ Ứng dụng web hiệu suất cao, tùy chỉnh bằng Python thuần. Deploy trong vài giây. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
</div>
---
[English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md)
---
# Reflex
Reflex là một thư viện để xây dựng ứng dụng web toàn bộ bằng Python thuần.
Các tính năng chính:
* **Python thuần tuý** - Viết toàn bộ ứng dụng cả backend và frontend hoàn toàn bằng Python, không cần học JavaScript.
* **Full Flexibility** - Reflex dễ dàng để bắt đầu, nhưng cũng có thể mở rộng lên các ứng dụng phức tạp.
* **Deploy Instantly** - Sau khi xây dựng ứng dụng, bạn có thể triển khai bằng [một dòng lệnh](https://reflex.dev/docs/hosting/deploy-quick-start/) hoặc triển khai trên server của riêng bạn.
Đọc [bài viết về kiến trúc hệ thống](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) để hiểu rõ các hoạt động của Reflex.
## ⚙️ Cài đặt
Mở cửa sổ lệnh và chạy (Yêu cầu Python phiên bản 3.9+):
```bash
pip install reflex
```
## 🥳 Tạo ứng dụng đầu tiên
Cài đặt `reflex` cũng như cài đặt công cụ dòng lệnh `reflex`.
Kiểm tra việc cài đặt đã thành công hay chưa bằng cách tạo mới một ứng dụng. (Thay `my_app_name` bằng tên ứng dụng của bạn):
```bash
mkdir my_app_name
cd my_app_name
reflex init
```
Lệnh này tạo ra một ứng dụng mẫu trong một thư mục mới.
Bạn có thể chạy ứng dụng ở chế độ phát triển.
```bash
reflex run
```
Bạn có thể xem ứng dụng của bạn ở địa chỉ http://localhost:3000.
Bạn có thể thay đổi mã nguồn ở `my_app_name/my_app_name.py`. Reflex nhanh chóng làm mới và bạn có thể thấy thay đổi trên ứng dụng của bạn ngay lập tức khi bạn lưu file.
## 🫧 Ứng dụng ví dụ
Bắt đầu với ví dụ: tạo một ứng dụng tạo ảnh bằng [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). Để cho đơn giản, chúng ta sẽ sử dụng [OpenAI API](https://platform.openai.com/docs/api-reference/authentication), nhưng bạn có thể sử dụng model của chính bạn được triển khai trên local.
&nbsp;
<div align="center">
<img src="https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/images/dalle.gif" alt="A frontend wrapper for DALL·E, shown in the process of generating an image." width="550" />
</div>
&nbsp;
Đây là toàn bộ đoạn mã để xây dựng ứng dụng trên. Nó được viết hoàn toàn trong một file Python!
```python
import reflex as rx
import openai
openai_client = openai.OpenAI()
class State(rx.State):
"""The app state."""
prompt = ""
image_url = ""
processing = False
complete = False
def get_image(self):
"""Get the image from the prompt."""
if self.prompt == "":
return rx.window_alert("Prompt Empty")
self.processing, self.complete = True, False
yield
response = openai_client.images.generate(
prompt=self.prompt, n=1, size="1024x1024"
)
self.image_url = response.data[0].url
self.processing, self.complete = False, True
def index():
return rx.center(
rx.vstack(
rx.heading("DALL-E", font_size="1.5em"),
rx.input(
placeholder="Enter a prompt..",
on_blur=State.set_prompt,
width="25em",
),
rx.button(
"Generate Image",
on_click=State.get_image,
width="25em",
loading=State.processing
),
rx.cond(
State.complete,
rx.image(src=State.image_url, width="20em"),
),
align="center",
),
width="100%",
height="100vh",
)
# Add state and page to the app.
app = rx.App()
app.add_page(index, title="Reflex:DALL-E")
```
## Hãy phân tích chi tiết.
<div align="center">
<img src="../images/dalle_colored_code_example.png" alt="Explaining the differences between backend and frontend parts of the DALL-E app." width="900" />
</div>
### **Reflex UI**
Bắt đầu với giao diện chính.
```python
def index():
return rx.center(
...
)
```
Hàm `index` định nghĩa phần giao diện chính của ứng dụng.
Chúng tôi sử dụng các component (thành phần) khác nhau như `center`, `vstack`, `input``button` để xây dựng giao diện phía trước.
Các component có thể được lồng vào nhau để tạo ra các bố cục phức tạp. Và bạn cũng có thể sử dụng từ khoá `args` để tận dụng đầy đủ sức mạnh của CSS.
Reflex có đến hơn [60 component được xây dựng sẵn](https://reflex.dev/docs/library) để giúp bạn bắt đầu. Chúng ta có thể tạo ra một component mới khá dễ dàng, thao khảo: [xây dựng component của riêng bạn](https://reflex.dev/docs/wrapping-react/overview/).
### **State**
Reflex biểu diễn giao diện bằng các hàm của state (trạng thái).
```python
class State(rx.State):
"""The app state."""
prompt = ""
image_url = ""
processing = False
complete = False
```
Một state định nghĩa các biến (được gọi là vars) có thể thay đổi trong một ứng dụng và cho phép các hàm có thể thay đổi chúng.
Tại đây state được cấu thành từ một `prompt``image_url`.
Có cũng những biến boolean `processing``complete`
để chỉ ra khi nào tắt nút (trong quá trình tạo hình ảnh)
và khi nào hiển thị hình ảnh kết quả.
### **Event Handlers**
```python
def get_image(self):
"""Get the image from the prompt."""
if self.prompt == "":
return rx.window_alert("Prompt Empty")
self.processing, self.complete = True, False
yield
response = openai_client.images.generate(
prompt=self.prompt, n=1, size="1024x1024"
)
self.image_url = response.data[0].url
self.processing, self.complete = False, True
```
Với các state, chúng ta định nghĩa các hàm có thể thay đổi state vars được gọi là event handlers. Event handler là cách chúng ta có thể thay đổi state trong Reflex. Chúng có thể là phản hồi khi người dùng thao tác, chằng hạn khi nhấn vào nút hoặc khi đang nhập trong text box. Các hành động này được gọi là event.
Ứng dụng DALL·E. của chúng ta có một event handler, `get_image` để lấy hình ảnh từ OpenAI API. Sử dụng từ khoá `yield` in ở giữa event handler để cập nhật giao diện. Hoặc giao diện có thể cập nhật ở cuối event handler.
### **Routing**
Cuối cùng, chúng ta định nghĩa một ứng dụng.
```python
app = rx.App()
```
Chúng ta thêm một trang ở đầu ứng dụng bằng index component. Chúng ta cũng thêm tiêu đề của ứng dụng để hiển thị lên trình duyệt.
```python
app.add_page(index, title="DALL-E")
```
Bạn có thể tạo một ứng dụng nhiều trang bằng cách thêm trang.
## 📑 Tài liệu
<div align="center">
📑 [Docs](https://reflex.dev/docs/getting-started/introduction) &nbsp; | &nbsp; 🗞️ [Blog](https://reflex.dev/blog) &nbsp; | &nbsp; 📱 [Component Library](https://reflex.dev/docs/library) &nbsp; | &nbsp; 🖼️ [Gallery](https://reflex.dev/docs/gallery) &nbsp; | &nbsp; 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start) &nbsp;
</div>
## ✅ Status
Reflex phát hành vào tháng 12/2022 với tên là Pynecone.
Đến tháng 02/2024, chúng tôi tạo ra dịch vụ dưới phiên bản alpha! Trong thời gian này mọi người có thể triển khai ứng dụng hoàn toàn miễn phí. Xem [roadmap](https://github.com/reflex-dev/reflex/issues/2727) để biết thêm chi tiết.
Reflex ra phiên bản mới với các tính năng mới hàng tuần! Hãy :star: star và :eyes: watch repo này để thấy các cập nhật mới nhất.
## Contributing
Chúng tôi chào đón mọi đóng góp dù lớn hay nhỏ. Dưới đây là các cách để bắt đầu với cộng đồng Reflex.
- **Discord**: [Discord](https://discord.gg/T5WSbC2YtQ) của chúng tôi là nơi tốt nhất để nhờ sự giúp đỡ và thảo luận các bạn có thể đóng góp.
- **GitHub Discussions**: Là cách tốt nhất để thảo luận về các tính năng mà bạn có thể đóng góp hoặc những điều bạn chưa rõ.
- **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) là nơi tốt nhất để thông báo. Ngoài ra bạn có thể sửa chữa các vấn đề bằng cách tạo PR.
Chúng tôi luôn sẵn sàng tìm kiếm các contributor, bất kể kinh nghiệm. Để tham gia đóng góp, xin mời xem
[CONTIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md)
## Xin cảm ơn các Contributors:
<a href="https://github.com/reflex-dev/reflex/graphs/contributors">
<img src="https://contrib.rocks/image?repo=reflex-dev/reflex" />
</a>
## License
Reflex là mã nguồn mở và sử dụng giấy phép [Apache License 2.0](LICENSE).

View File

@ -10,7 +10,6 @@
### **✨ 使用 Python 创建高效且可自定义的网页应用程序,几秒钟内即可部署.✨** ### **✨ 使用 Python 创建高效且可自定义的网页应用程序,几秒钟内即可部署.✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -35,7 +34,7 @@ Reflex 是一个使用纯Python构建全栈web应用的库。
## ⚙️ 安装 ## ⚙️ 安装
打开一个终端并且运行(要求Python3.8+): 打开一个终端并且运行(要求Python3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -11,7 +11,6 @@
**✨ 使用 Python 建立高效且可自訂的網頁應用程式,幾秒鐘內即可部署。✨** **✨ 使用 Python 建立高效且可自訂的網頁應用程式,幾秒鐘內即可部署。✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -37,7 +36,7 @@ Reflex 是一個可以用純 Python 構建全端網頁應用程式的函式庫
## ⚙️ 安裝 ## ⚙️ 安裝
開啟一個終端機並且執行 (需要 Python 3.8+): 開啟一個終端機並且執行 (需要 Python 3.9+):
```bash ```bash
pip install reflex pip install reflex

View File

@ -1,107 +0,0 @@
"""Test that per-component state scaffold works and operates independently."""
from typing import Generator
import pytest
from selenium.webdriver.common.by import By
from reflex.testing import AppHarness
from . import utils
def ComponentStateApp():
"""App using per component state."""
import reflex as rx
class MultiCounter(rx.ComponentState):
count: int = 0
def increment(self):
self.count += 1
@classmethod
def get_component(cls, *children, **props):
return rx.vstack(
*children,
rx.heading(cls.count, id=f"count-{props.get('id', 'default')}"),
rx.button(
"Increment",
on_click=cls.increment,
id=f"button-{props.get('id', 'default')}",
),
**props,
)
app = rx.App(state=rx.State) # noqa
@rx.page()
def index():
mc_a = MultiCounter.create(id="a")
mc_b = MultiCounter.create(id="b")
assert mc_a.State != mc_b.State
return rx.vstack(
mc_a,
mc_b,
rx.button(
"Inc A",
on_click=mc_a.State.increment, # type: ignore
id="inc-a",
),
)
@pytest.fixture()
def component_state_app(tmp_path) -> Generator[AppHarness, None, None]:
"""Start ComponentStateApp app at tmp_path via AppHarness.
Args:
tmp_path: pytest tmp_path fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path,
app_source=ComponentStateApp, # type: ignore
) as harness:
yield harness
@pytest.mark.asyncio
async def test_component_state_app(component_state_app: AppHarness):
"""Increment counters independently.
Args:
component_state_app: harness for ComponentStateApp app
"""
assert component_state_app.app_instance is not None, "app is not running"
driver = component_state_app.frontend()
ss = utils.SessionStorage(driver)
assert AppHarness._poll_for(lambda: ss.get("token") is not None), "token not found"
count_a = driver.find_element(By.ID, "count-a")
count_b = driver.find_element(By.ID, "count-b")
button_a = driver.find_element(By.ID, "button-a")
button_b = driver.find_element(By.ID, "button-b")
button_inc_a = driver.find_element(By.ID, "inc-a")
assert count_a.text == "0"
button_a.click()
assert component_state_app.poll_for_content(count_a, exp_not_equal="0") == "1"
button_a.click()
assert component_state_app.poll_for_content(count_a, exp_not_equal="1") == "2"
button_inc_a.click()
assert component_state_app.poll_for_content(count_a, exp_not_equal="2") == "3"
assert count_b.text == "0"
button_b.click()
assert component_state_app.poll_for_content(count_b, exp_not_equal="0") == "1"
button_b.click()
assert component_state_app.poll_for_content(count_b, exp_not_equal="1") == "2"

View File

@ -1,173 +0,0 @@
"""Integration tests for table and related components."""
from typing import Generator
import pytest
from selenium.webdriver.common.by import By
from reflex.testing import AppHarness
def Table():
"""App using table component."""
from typing import List
import reflex_chakra as rc
import reflex as rx
class TableState(rx.State):
rows: List[List[str]] = [
["John", "30", "New York"],
["Jane", "31", "San Fransisco"],
["Joe", "32", "Los Angeles"],
]
headers: List[str] = ["Name", "Age", "Location"]
footers: List[str] = ["footer1", "footer2", "footer3"]
caption: str = "random caption"
app = rx.App(state=rx.State)
@app.add_page
def index():
return rx.center(
rc.input(
id="token",
value=TableState.router.session.client_token,
is_read_only=True,
),
rc.table_container(
rc.table(
headers=TableState.headers,
rows=TableState.rows,
footers=TableState.footers,
caption=TableState.caption,
variant="striped",
color_scheme="blue",
width="100%",
),
),
)
@app.add_page
def another():
return rx.center(
rc.table_container(
rc.table( # type: ignore
rc.thead( # type: ignore
rc.tr( # type: ignore
rc.th("Name"),
rc.th("Age"),
rc.th("Location"),
)
),
rc.tbody( # type: ignore
rc.tr( # type: ignore
rc.td("John"),
rc.td(30),
rc.td("New York"),
),
rc.tr( # type: ignore
rc.td("Jane"),
rc.td(31),
rc.td("San Francisco"),
),
rc.tr( # type: ignore
rc.td("Joe"),
rc.td(32),
rc.td("Los Angeles"),
),
),
rc.tfoot( # type: ignore
rc.tr(
rc.td("footer1"),
rc.td("footer2"),
rc.td("footer3"),
) # type: ignore
),
rc.table_caption("random caption"),
variant="striped",
color_scheme="teal",
)
)
)
@pytest.fixture()
def table(tmp_path_factory) -> Generator[AppHarness, None, None]:
"""Start Table app at tmp_path via AppHarness.
Args:
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path_factory.mktemp("table"),
app_source=Table, # type: ignore
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness
@pytest.fixture
def driver(table: AppHarness):
"""GEt an instance of the browser open to the table app.
Args:
table: harness for Table app
Yields:
WebDriver instance.
"""
driver = table.frontend()
try:
token_input = driver.find_element(By.ID, "token")
assert token_input
# wait for the backend connection to send the token
token = table.poll_for_value(token_input)
assert token is not None
yield driver
finally:
driver.quit()
@pytest.mark.parametrize("route", ["", "/another"])
def test_table(driver, table: AppHarness, route):
"""Test that a table component is rendered properly.
Args:
driver: Selenium WebDriver open to the app
table: Harness for Table app
route: Page route or path.
"""
driver.get(f"{table.frontend_url}/{route}")
assert table.app_instance is not None, "app is not running"
thead = driver.find_element(By.TAG_NAME, "thead")
# poll till page is fully loaded.
table.poll_for_content(element=thead)
# check headers
assert thead.find_element(By.TAG_NAME, "tr").text == "NAME AGE LOCATION"
# check first row value
assert (
driver.find_element(By.TAG_NAME, "tbody")
.find_elements(By.TAG_NAME, "tr")[0]
.text
== "John 30 New York"
)
# check footer
assert (
driver.find_element(By.TAG_NAME, "tfoot")
.find_element(By.TAG_NAME, "tr")
.text.lower()
== "footer1 footer2 footer3"
)
# check caption
assert driver.find_element(By.TAG_NAME, "caption").text == "random caption"

View File

@ -1,68 +0,0 @@
"""Integration tests for all urls in Reflex."""
import os
import re
from pathlib import Path
import pytest
import requests
def check_urls(repo_dir):
"""Check that all URLs in the repo are valid and secure.
Args:
repo_dir: The directory of the repo.
Returns:
A list of errors.
"""
url_pattern = re.compile(r'http[s]?://reflex\.dev[^\s")]*')
errors = []
for root, _dirs, files in os.walk(repo_dir):
if "__pycache__" in root:
continue
for file_name in files:
if not file_name.endswith(".py") and not file_name.endswith(".md"):
continue
file_path = os.path.join(root, file_name)
try:
with open(file_path, "r", encoding="utf-8", errors="ignore") as file:
for line in file:
urls = url_pattern.findall(line)
for url in set(urls):
if url.startswith("http://"):
errors.append(
f"Found insecure HTTP URL: {url} in {file_path}"
)
url = url.strip('"\n')
try:
response = requests.head(
url, allow_redirects=True, timeout=5
)
response.raise_for_status()
except requests.RequestException as e:
errors.append(
f"Error accessing URL: {url} in {file_path} | Error: {e}, , Check your path ends with a /"
)
except Exception as e:
errors.append(f"Error reading file: {file_path} | Error: {e}")
return errors
@pytest.mark.parametrize(
"repo_dir",
[Path(__file__).resolve().parent.parent / "reflex"],
)
def test_find_and_check_urls(repo_dir):
"""Test that all URLs in the repo are valid and secure.
Args:
repo_dir: The directory of the repo.
"""
errors = check_urls(repo_dir)
assert not errors, "\n".join(errors)

2048
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "reflex" name = "reflex"
version = "0.6.0a1" version = "0.6.4dev1"
description = "Web apps in pure Python." description = "Web apps in pure Python."
license = "Apache-2.0" license = "Apache-2.0"
authors = [ authors = [
@ -26,14 +26,14 @@ packages = [
] ]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = "^3.9"
dill = ">=0.3.8,<0.4"
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1" fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
gunicorn = ">=20.1.0,<24.0" gunicorn = ">=20.1.0,<24.0"
jinja2 = ">=3.1.2,<4.0" jinja2 = ">=3.1.2,<4.0"
psutil = ">=5.9.4,<7.0" psutil = ">=5.9.4,<7.0"
pydantic = ">=1.10.2,<3.0" pydantic = ">=1.10.2,<3.0"
python-multipart = ">=0.0.5,<0.1" python-multipart = ">=0.0.5,<0.1"
python-dotenv = ">=1.0.1"
python-socketio = ">=5.7.0,<6.0" python-socketio = ">=5.7.0,<6.0"
redis = ">=4.3.5,<6.0" redis = ">=4.3.5,<6.0"
rich = ">=13.0.0,<14.0" rich = ">=13.0.0,<14.0"
@ -54,37 +54,31 @@ reflex-hosting-cli = ">=0.1.2,<2.0"
charset-normalizer = ">=3.3.2,<4.0" charset-normalizer = ">=3.3.2,<4.0"
wheel = ">=0.42.0,<1.0" wheel = ">=0.42.0,<1.0"
build = ">=1.0.3,<2.0" build = ">=1.0.3,<2.0"
setuptools = ">=69.1.1,<70.2" setuptools = ">=75.0"
httpx = ">=0.25.1,<1.0" httpx = ">=0.25.1,<1.0"
twine = ">=4.0.0,<6.0" twine = ">=4.0.0,<6.0"
tomlkit = ">=0.12.4,<1.0" tomlkit = ">=0.12.4,<1.0"
lazy_loader = ">=0.4" lazy_loader = ">=0.4"
reflex-chakra = ">=0.6.0a" reflex-chakra = ">=0.6.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
pytest = ">=7.1.2,<8.0" pytest = ">=7.1.2,<9.0"
pytest-mock = ">=3.10.0,<4.0" pytest-mock = ">=3.10.0,<4.0"
pyright = ">=1.1.229,<1.1.335" pyright = ">=1.1.229,<1.1.335"
darglint = ">=1.8.1,<2.0" darglint = ">=1.8.1,<2.0"
toml = ">=0.10.2,<1.0" toml = ">=0.10.2,<1.0"
pytest-asyncio = ">=0.20.1,<0.22.0" # https://github.com/pytest-dev/pytest-asyncio/issues/706 pytest-asyncio = ">=0.24.0"
pytest-cov = ">=4.0.0,<5.0" pytest-cov = ">=4.0.0,<6.0"
ruff = "^0.4.9" ruff = "^0.7.0"
pandas = [ pandas = ">=2.1.1,<3.0"
{version = ">=2.1.1,<3.0", python = ">=3.9,<3.13"}, pillow = ">=10.0.0,<12.0"
{version = ">=1.5.3,<2.0", python = ">=3.8,<3.9"},
]
pillow = [
{version = ">=10.0.0,<11.0", python = ">=3.8,<4.0"}
]
plotly = ">=5.13.0,<6.0" plotly = ">=5.13.0,<6.0"
asynctest = ">=0.13.0,<1.0" asynctest = ">=0.13.0,<1.0"
pre-commit = {version = ">=3.2.1", python = ">=3.8,<4.0"} pre-commit = ">=3.2.1"
selenium = ">=4.11.0,<5.0" selenium = ">=4.11.0,<5.0"
pytest-benchmark = ">=4.0.0,<5.0" pytest-benchmark = ">=4.0.0,<5.0"
playwright = "^1.46.0" playwright = ">=1.46.0"
pytest-playwright = "^0.5.1" pytest-playwright = ">=0.5.1"
[tool.poetry.scripts] [tool.poetry.scripts]
reflex = "reflex.reflex:cli" reflex = "reflex.reflex:cli"
@ -96,9 +90,9 @@ build-backend = "poetry.core.masonry.api"
[tool.pyright] [tool.pyright]
[tool.ruff] [tool.ruff]
target-version = "py38" target-version = "py39"
lint.select = ["B", "D", "E", "F", "I", "SIM", "W"] lint.select = ["B", "D", "E", "F", "I", "SIM", "W"]
lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541"] lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541", "SIM115"]
lint.pydocstyle.convention = "google" lint.pydocstyle.convention = "google"
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
@ -107,3 +101,7 @@ lint.pydocstyle.convention = "google"
"reflex/.templates/*.py" = ["D100", "D103", "D104"] "reflex/.templates/*.py" = ["D100", "D103", "D104"]
"*.pyi" = ["D301", "D415", "D417", "D418", "E742"] "*.pyi" = ["D301", "D415", "D417", "D418", "E742"]
"*/blank.py" = ["I001"] "*/blank.py" = ["I001"]
[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto"

View File

@ -8,11 +8,11 @@ version = "0.0.1"
description = "Reflex custom component {{ module_name }}" description = "Reflex custom component {{ module_name }}"
readme = "README.md" readme = "README.md"
license = { text = "Apache-2.0" } license = { text = "Apache-2.0" }
requires-python = ">=3.8" requires-python = ">=3.9"
authors = [{ name = "", email = "YOUREMAIL@domain.com" }] authors = [{ name = "", email = "YOUREMAIL@domain.com" }]
keywords = ["reflex","reflex-custom-components"] keywords = ["reflex","reflex-custom-components"]
dependencies = ["reflex>=0.4.2"] dependencies = ["reflex>={{ reflex_version }}"]
classifiers = ["Development Status :: 4 - Beta"] classifiers = ["Development Status :: 4 - Beta"]

View File

@ -7,6 +7,9 @@ import '/styles/styles.css'
{% block declaration %} {% block declaration %}
import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js"; import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js";
import { ThemeProvider } from 'next-themes' import { ThemeProvider } from 'next-themes'
{% for library_alias, library_path in window_libraries %}
import * as {{library_alias}} from "{{library_path}}";
{% endfor %}
{% for custom_code in custom_codes %} {% for custom_code in custom_codes %}
{{custom_code}} {{custom_code}}
@ -26,8 +29,17 @@ function AppWrap({children}) {
} }
export default function MyApp({ Component, pageProps }) { export default function MyApp({ Component, pageProps }) {
React.useEffect(() => {
// Make contexts and state objects available globally for dynamic eval'd components
let windowImports = {
{% for library_alias, library_path in window_libraries %}
"{{library_path}}": {{library_alias}},
{% endfor %}
};
window["__reflex"] = windowImports;
}, []);
return ( return (
<ThemeProvider defaultTheme={ defaultColorMode } storageKey="chakra-ui-color-mode" attribute="class"> <ThemeProvider defaultTheme={ defaultColorMode } attribute="class">
<AppWrap> <AppWrap>
<StateProvider> <StateProvider>
<EventLoopProvider> <EventLoopProvider>

View File

@ -64,11 +64,11 @@
{# Args: #} {# Args: #}
{# component: component dictionary #} {# component: component dictionary #}
{% macro render_iterable_tag(component) %} {% macro render_iterable_tag(component) %}
{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => ( <>{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
{% for child in component.children %} {% for child in component.children %}
{{ render(child) }} {{ render(child) }}
{% endfor %} {% endfor %}
))} ))}</>
{%- endmacro %} {%- endmacro %}
@ -85,10 +85,10 @@
{% macro render_match_tag(component) %} {% macro render_match_tag(component) %}
{ {
(() => { (() => {
switch (JSON.stringify({{ component.cond._var_name_unwrapped }})) { switch (JSON.stringify({{ component.cond._js_expr }})) {
{% for case in component.match_cases %} {% for case in component.match_cases %}
{% for condition in case[:-1] %} {% for condition in case[:-1] %}
case JSON.stringify({{ condition._var_name_unwrapped }}): case JSON.stringify({{ condition._js_expr }}):
{% endfor %} {% endfor %}
return {{ case[-1] }}; return {{ case[-1] }};
break; break;

View File

@ -1 +1 @@
export default {{ theme|json_dumps }} export default {{ theme }}

View File

@ -1,36 +0,0 @@
import { useColorMode as chakraUseColorMode } from "@chakra-ui/react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { ColorModeContext, defaultColorMode } from "/utils/context.js";
export default function ChakraColorModeProvider({ children }) {
const { theme, resolvedTheme, setTheme } = useTheme();
const { colorMode, toggleColorMode } = chakraUseColorMode();
const [resolvedColorMode, setResolvedColorMode] = useState(colorMode);
useEffect(() => {
if (colorMode != resolvedTheme) {
toggleColorMode();
}
setResolvedColorMode(resolvedTheme);
}, [theme, resolvedTheme]);
const rawColorMode = colorMode;
const setColorMode = (mode) => {
const allowedModes = ["light", "dark", "system"];
if (!allowedModes.includes(mode)) {
console.error(
`Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`
);
mode = defaultColorMode;
}
setTheme(mode);
};
return (
<ColorModeContext.Provider
value={{ rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }}
>
{children}
</ColorModeContext.Provider>
);
}

View File

@ -0,0 +1,29 @@
import { useEffect, useState } from "react"
import { codeToHtml} from "shiki"
export function Code ({code, theme, language, transformers, ...divProps}) {
const [codeResult, setCodeResult] = useState("")
useEffect(() => {
async function fetchCode() {
let final_code;
if (Array.isArray(code)) {
final_code = code[0];
} else {
final_code = code;
}
const result = await codeToHtml(final_code, {
lang: language,
theme,
transformers
});
setCodeResult(result);
}
fetchCode();
}, [code, language, theme, transformers]
)
return (
<div dangerouslySetInnerHTML={{__html: codeResult}} {...divProps} ></div>
)
}

View File

@ -15,6 +15,7 @@ import {
} from "utils/context.js"; } from "utils/context.js";
import debounce from "/utils/helpers/debounce"; import debounce from "/utils/helpers/debounce";
import throttle from "/utils/helpers/throttle"; import throttle from "/utils/helpers/throttle";
import * as Babel from "@babel/standalone";
// Endpoint URLs. // Endpoint URLs.
const EVENTURL = env.EVENT; const EVENTURL = env.EVENT;
@ -117,8 +118,8 @@ export const isStateful = () => {
if (event_queue.length === 0) { if (event_queue.length === 0) {
return false; return false;
} }
return event_queue.some(event => event.name.startsWith("reflex___state")); return event_queue.some((event) => event.name.startsWith("reflex___state"));
} };
/** /**
* Apply a delta to the state. * Apply a delta to the state.
@ -129,6 +130,22 @@ export const applyDelta = (state, delta) => {
return { ...state, ...delta }; return { ...state, ...delta };
}; };
/**
* Evaluate a dynamic component.
* @param component The component to evaluate.
* @returns The evaluated component.
*/
export const evalReactComponent = async (component) => {
if (!window.React && window.__reflex) {
window.React = window.__reflex.react;
}
const output = Babel.transform(component, { presets: ["react"] }).code;
const encodedJs = encodeURIComponent(output);
const dataUri = "data:text/javascript;charset=utf-8," + encodedJs;
const module = await eval(`import(dataUri)`);
return module.default;
};
/** /**
* Only Queue and process events when websocket connection exists. * Only Queue and process events when websocket connection exists.
* @param event The event to queue. * @param event The event to queue.
@ -141,7 +158,7 @@ export const queueEventIfSocketExists = async (events, socket) => {
return; return;
} }
await queueEvents(events, socket); await queueEvents(events, socket);
} };
/** /**
* Handle frontend event or send the event to the backend via Websocket. * Handle frontend event or send the event to the backend via Websocket.
@ -208,7 +225,10 @@ export const applyEvent = async (event, socket) => {
const a = document.createElement("a"); const a = document.createElement("a");
a.hidden = true; a.hidden = true;
// Special case when linking to uploaded files // Special case when linking to uploaded files
a.href = event.payload.url.replace("${getBackendURL(env.UPLOAD)}", getBackendURL(env.UPLOAD)) a.href = event.payload.url.replace(
"${getBackendURL(env.UPLOAD)}",
getBackendURL(env.UPLOAD)
);
a.download = event.payload.filename; a.download = event.payload.filename;
a.click(); a.click();
a.remove(); a.remove();
@ -249,7 +269,7 @@ export const applyEvent = async (event, socket) => {
} catch (e) { } catch (e) {
console.log("_call_script", e); console.log("_call_script", e);
if (window && window?.onerror) { if (window && window?.onerror) {
window.onerror(e.message, null, null, null, e) window.onerror(e.message, null, null, null, e);
} }
} }
return false; return false;
@ -290,10 +310,9 @@ export const applyEvent = async (event, socket) => {
export const applyRestEvent = async (event, socket) => { export const applyRestEvent = async (event, socket) => {
let eventSent = false; let eventSent = false;
if (event.handler === "uploadFiles") { if (event.handler === "uploadFiles") {
if (event.payload.files === undefined || event.payload.files.length === 0) { if (event.payload.files === undefined || event.payload.files.length === 0) {
// Submit the event over the websocket to trigger the event handler. // Submit the event over the websocket to trigger the event handler.
return await applyEvent(Event(event.name), socket) return await applyEvent(Event(event.name), socket);
} }
// Start upload, but do not wait for it, which would block other events. // Start upload, but do not wait for it, which would block other events.
@ -397,7 +416,7 @@ export const connect = async (
console.log("Disconnect backend before bfcache on navigation"); console.log("Disconnect backend before bfcache on navigation");
socket.current.disconnect(); socket.current.disconnect();
} }
} };
// Once the socket is open, hydrate the page. // Once the socket is open, hydrate the page.
socket.current.on("connect", () => { socket.current.on("connect", () => {
@ -416,7 +435,7 @@ export const connect = async (
}); });
// On each received message, queue the updates and events. // On each received message, queue the updates and events.
socket.current.on("event", (message) => { socket.current.on("event", async (message) => {
const update = JSON5.parse(message); const update = JSON5.parse(message);
for (const substate in update.delta) { for (const substate in update.delta) {
dispatch[substate](update.delta[substate]); dispatch[substate](update.delta[substate]);
@ -525,13 +544,19 @@ export const uploadFiles = async (
/** /**
* Create an event object. * Create an event object.
* @param name The name of the event. * @param {string} name The name of the event.
* @param payload The payload of the event. * @param {Object.<string, Any>} payload The payload of the event.
* @param handler The client handler to process event. * @param {Object.<string, (number|boolean)>} event_actions The actions to take on the event.
* @param {string} handler The client handler to process event.
* @returns The event object. * @returns The event object.
*/ */
export const Event = (name, payload = {}, handler = null) => { export const Event = (
return { name, payload, handler }; name,
payload = {},
event_actions = {},
handler = null
) => {
return { name, payload, handler, event_actions };
}; };
/** /**
@ -574,7 +599,11 @@ export const hydrateClientStorage = (client_storage) => {
} }
} }
} }
if (client_storage.cookies || client_storage.local_storage || client_storage.session_storage) { if (
client_storage.cookies ||
client_storage.local_storage ||
client_storage.session_storage
) {
return client_storage_values; return client_storage_values;
} }
return {}; return {};
@ -614,15 +643,17 @@ const applyClientStorageDelta = (client_storage, delta) => {
) { ) {
const options = client_storage.local_storage[state_key]; const options = client_storage.local_storage[state_key];
localStorage.setItem(options.name || state_key, delta[substate][key]); localStorage.setItem(options.name || state_key, delta[substate][key]);
} else if( } else if (
client_storage.session_storage && client_storage.session_storage &&
state_key in client_storage.session_storage && state_key in client_storage.session_storage &&
typeof window !== "undefined" typeof window !== "undefined"
) { ) {
const session_options = client_storage.session_storage[state_key]; const session_options = client_storage.session_storage[state_key];
sessionStorage.setItem(session_options.name || state_key, delta[substate][key]); sessionStorage.setItem(
session_options.name || state_key,
delta[substate][key]
);
} }
} }
} }
}; };
@ -651,7 +682,13 @@ export const useEventLoop = (
if (!(args instanceof Array)) { if (!(args instanceof Array)) {
args = [args]; args = [args];
} }
const _e = args.filter((o) => o?.preventDefault !== undefined)[0]
event_actions = events.reduce(
(acc, e) => ({ ...acc, ...e.event_actions }),
event_actions ?? {}
);
const _e = args.filter((o) => o?.preventDefault !== undefined)[0];
if (event_actions?.preventDefault && _e?.preventDefault) { if (event_actions?.preventDefault && _e?.preventDefault) {
_e.preventDefault(); _e.preventDefault();
@ -671,7 +708,7 @@ export const useEventLoop = (
debounce( debounce(
combined_name, combined_name,
() => queueEvents(events, socket), () => queueEvents(events, socket),
event_actions.debounce, event_actions.debounce
); );
} else { } else {
queueEvents(events, socket); queueEvents(events, socket);
@ -696,30 +733,34 @@ export const useEventLoop = (
} }
}, [router.isReady]); }, [router.isReady]);
// Handle frontend errors and send them to the backend via websocket. // Handle frontend errors and send them to the backend via websocket.
useEffect(() => { useEffect(() => {
if (typeof window === "undefined") {
if (typeof window === 'undefined') { return;
return; }
}
window.onerror = function (msg, url, lineNo, columnNo, error) {
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, {
stack: error.stack,
})])
return false;
}
//NOTE: Only works in Chrome v49+ window.onerror = function (msg, url, lineNo, columnNo, error) {
//https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events addEvents([
window.onunhandledrejection = function (event) { Event(`${exception_state_name}.handle_frontend_exception`, {
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, { stack: error.stack,
stack: event.reason.stack, component_stack: "",
})]) }),
return false; ]);
} return false;
};
},[])
//NOTE: Only works in Chrome v49+
//https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events
window.onunhandledrejection = function (event) {
addEvents([
Event(`${exception_state_name}.handle_frontend_exception`, {
stack: event.reason.stack,
component_stack: "",
}),
]);
return false;
};
}, []);
// Main event loop. // Main event loop.
useEffect(() => { useEffect(() => {
@ -782,11 +823,11 @@ export const useEventLoop = (
// Route after the initial page hydration. // Route after the initial page hydration.
useEffect(() => { useEffect(() => {
const change_start = () => { const change_start = () => {
const main_state_dispatch = dispatch["reflex___state____state"] const main_state_dispatch = dispatch["reflex___state____state"];
if (main_state_dispatch !== undefined) { if (main_state_dispatch !== undefined) {
main_state_dispatch({ is_hydrated: false }) main_state_dispatch({ is_hydrated: false });
} }
} };
const change_complete = () => addEvents(onLoadInternalEvent()); const change_complete = () => addEvents(onLoadInternalEvent());
router.events.on("routeChangeStart", change_start); router.events.on("routeChangeStart", change_start);
router.events.on("routeChangeComplete", change_complete); router.events.on("routeChangeComplete", change_complete);
@ -805,7 +846,9 @@ export const useEventLoop = (
* @returns True if the value is truthy, false otherwise. * @returns True if the value is truthy, false otherwise.
*/ */
export const isTrue = (val) => { export const isTrue = (val) => {
return Array.isArray(val) ? val.length > 0 : !!val; if (Array.isArray(val)) return val.length > 0;
if (val === Object(val)) return Object.keys(val).length > 0;
return Boolean(val);
}; };
/** /**

View File

@ -89,6 +89,8 @@ from reflex.utils import (
lazy_loader, lazy_loader,
) )
from .event import event as event
# import this here explicitly to avoid returning the page module since page attr has the # import this here explicitly to avoid returning the page module since page attr has the
# same name as page module(page.py) # same name as page module(page.py)
from .page import page as page from .page import page as page
@ -206,6 +208,13 @@ RADIX_PRIMITIVES_MAPPING: dict = {
"components.radix.primitives.form": [ "components.radix.primitives.form": [
"form", "form",
], ],
"components.radix.primitives.progress": [
"progress",
],
}
RADIX_PRIMITIVES_SHORTCUT_MAPPING: dict = {
k: v for k, v in RADIX_PRIMITIVES_MAPPING.items() if "progress" not in k
} }
COMPONENTS_CORE_MAPPING: dict = { COMPONENTS_CORE_MAPPING: dict = {
@ -248,7 +257,7 @@ RADIX_MAPPING: dict = {
**RADIX_THEMES_COMPONENTS_MAPPING, **RADIX_THEMES_COMPONENTS_MAPPING,
**RADIX_THEMES_TYPOGRAPHY_MAPPING, **RADIX_THEMES_TYPOGRAPHY_MAPPING,
**RADIX_THEMES_LAYOUT_MAPPING, **RADIX_THEMES_LAYOUT_MAPPING,
**RADIX_PRIMITIVES_MAPPING, **RADIX_PRIMITIVES_SHORTCUT_MAPPING,
} }
_MAPPING: dict = { _MAPPING: dict = {
@ -311,25 +320,27 @@ _MAPPING: dict = {
"upload_files", "upload_files",
"window_alert", "window_alert",
], ],
"istate.storage": [
"Cookie",
"LocalStorage",
"SessionStorage",
],
"middleware": ["middleware", "Middleware"], "middleware": ["middleware", "Middleware"],
"model": ["session", "Model"], "model": ["session", "Model"],
"state": [ "state": [
"var", "var",
"Cookie",
"LocalStorage",
"SessionStorage",
"ComponentState", "ComponentState",
"State", "State",
"dynamic",
], ],
"style": ["Style", "toggle_color_mode"], "style": ["Style", "toggle_color_mode"],
"utils.imports": ["ImportVar"], "utils.imports": ["ImportVar"],
"utils.serializers": ["serializer"], "utils.serializers": ["serializer"],
"vars": ["Var"], "vars": ["Var", "field", "Field"],
} }
_SUBMODULES: set[str] = { _SUBMODULES: set[str] = {
"components", "components",
"event",
"app", "app",
"style", "style",
"admin", "admin",
@ -338,7 +349,6 @@ _SUBMODULES: set[str] = {
"testing", "testing",
"utils", "utils",
"vars", "vars",
"ivars",
"config", "config",
"compiler", "compiler",
} }

View File

@ -11,8 +11,6 @@ from . import base as base
from . import compiler as compiler from . import compiler as compiler
from . import components as components from . import components as components
from . import config as config from . import config as config
from . import event as event
from . import ivars as ivars
from . import model as model from . import model as model
from . import style as style from . import style as style
from . import testing as testing from . import testing as testing
@ -132,6 +130,7 @@ from .components.radix.themes.layout.container import container as container
from .components.radix.themes.layout.flex import flex as flex from .components.radix.themes.layout.flex import flex as flex
from .components.radix.themes.layout.grid import grid as grid from .components.radix.themes.layout.grid import grid as grid
from .components.radix.themes.layout.list import list_item as list_item from .components.radix.themes.layout.list import list_item as list_item
from .components.radix.themes.layout.list import list_ns as list # noqa
from .components.radix.themes.layout.list import ordered_list as ordered_list from .components.radix.themes.layout.list import ordered_list as ordered_list
from .components.radix.themes.layout.list import unordered_list as unordered_list from .components.radix.themes.layout.list import unordered_list as unordered_list
from .components.radix.themes.layout.section import section as section from .components.radix.themes.layout.section import section as section
@ -161,6 +160,7 @@ from .event import clear_local_storage as clear_local_storage
from .event import clear_session_storage as clear_session_storage from .event import clear_session_storage as clear_session_storage
from .event import console_log as console_log from .event import console_log as console_log
from .event import download as download from .event import download as download
from .event import event as event
from .event import prevent_default as prevent_default from .event import prevent_default as prevent_default
from .event import redirect as redirect from .event import redirect as redirect
from .event import remove_cookie as remove_cookie from .event import remove_cookie as remove_cookie
@ -174,22 +174,25 @@ from .event import stop_propagation as stop_propagation
from .event import upload_files as upload_files from .event import upload_files as upload_files
from .event import window_alert as window_alert from .event import window_alert as window_alert
from .experimental import _x as _x from .experimental import _x as _x
from .istate.storage import Cookie as Cookie
from .istate.storage import LocalStorage as LocalStorage
from .istate.storage import SessionStorage as SessionStorage
from .middleware import Middleware as Middleware from .middleware import Middleware as Middleware
from .middleware import middleware as middleware from .middleware import middleware as middleware
from .model import Model as Model from .model import Model as Model
from .model import session as session from .model import session as session
from .page import page as page from .page import page as page
from .state import ComponentState as ComponentState from .state import ComponentState as ComponentState
from .state import Cookie as Cookie
from .state import LocalStorage as LocalStorage
from .state import SessionStorage as SessionStorage
from .state import State as State from .state import State as State
from .state import dynamic as dynamic
from .state import var as var from .state import var as var
from .style import Style as Style from .style import Style as Style
from .style import toggle_color_mode as toggle_color_mode from .style import toggle_color_mode as toggle_color_mode
from .utils.imports import ImportVar as ImportVar from .utils.imports import ImportVar as ImportVar
from .utils.serializers import serializer as serializer from .utils.serializers import serializer as serializer
from .vars import Field as Field
from .vars import Var as Var from .vars import Var as Var
from .vars import field as field
del compat del compat
RADIX_THEMES_MAPPING: dict RADIX_THEMES_MAPPING: dict
@ -197,6 +200,7 @@ RADIX_THEMES_COMPONENTS_MAPPING: dict
RADIX_THEMES_LAYOUT_MAPPING: dict RADIX_THEMES_LAYOUT_MAPPING: dict
RADIX_THEMES_TYPOGRAPHY_MAPPING: dict RADIX_THEMES_TYPOGRAPHY_MAPPING: dict
RADIX_PRIMITIVES_MAPPING: dict RADIX_PRIMITIVES_MAPPING: dict
RADIX_PRIMITIVES_SHORTCUT_MAPPING: dict
COMPONENTS_CORE_MAPPING: dict COMPONENTS_CORE_MAPPING: dict
COMPONENTS_BASE_MAPPING: dict COMPONENTS_BASE_MAPPING: dict
RADIX_MAPPING: dict RADIX_MAPPING: dict

View File

@ -9,6 +9,7 @@ import copy
import functools import functools
import inspect import inspect
import io import io
import json
import multiprocessing import multiprocessing
import os import os
import platform import platform
@ -63,7 +64,7 @@ from reflex.components.core.client_side_routing import (
) )
from reflex.components.core.upload import Upload, get_upload_dir from reflex.components.core.upload import Upload, get_upload_dir
from reflex.components.radix import themes from reflex.components.radix import themes
from reflex.config import get_config from reflex.config import environment, get_config
from reflex.event import Event, EventHandler, EventSpec, window_alert from reflex.event import Event, EventHandler, EventSpec, window_alert
from reflex.model import Model, get_db_status from reflex.model import Model, get_db_status
from reflex.page import ( from reflex.page import (
@ -269,13 +270,12 @@ class App(MiddlewareMixin, LifespanMixin, Base):
"`connect_error_component` is deprecated, use `overlay_component` instead" "`connect_error_component` is deprecated, use `overlay_component` instead"
) )
super().__init__(**kwargs) super().__init__(**kwargs)
base_state_subclasses = BaseState.__subclasses__()
# Special case to allow test cases have multiple subclasses of rx.BaseState. # Special case to allow test cases have multiple subclasses of rx.BaseState.
if not is_testing_env() and len(base_state_subclasses) > 1: if not is_testing_env() and BaseState.__subclasses__() != [State]:
# Only one Base State class is allowed. # Only rx.State is allowed as Base State subclass.
raise ValueError( raise ValueError(
"rx.BaseState cannot be subclassed multiple times. use rx.State instead" "rx.BaseState cannot be subclassed directly. Use rx.State instead"
) )
if "breakpoints" in self.style: if "breakpoints" in self.style:
@ -431,25 +431,12 @@ class App(MiddlewareMixin, LifespanMixin, Base):
The generated component. The generated component.
Raises: Raises:
VarOperationTypeError: When an invalid component var related function is passed.
TypeError: When an invalid component function is passed.
exceptions.MatchTypeError: If the return types of match cases in rx.match are different. exceptions.MatchTypeError: If the return types of match cases in rx.match are different.
""" """
from reflex.utils.exceptions import VarOperationTypeError
try: try:
return component if isinstance(component, Component) else component() return component if isinstance(component, Component) else component()
except exceptions.MatchTypeError: except exceptions.MatchTypeError:
raise raise
except TypeError as e:
message = str(e)
if "Var" in message:
raise VarOperationTypeError(
"You may be trying to use an invalid Python function on a state var. "
"When referencing a var inside your render code, only limited var operations are supported. "
"See the var operation docs here: https://reflex.dev/docs/vars/var-operations/"
) from e
raise e
def add_page( def add_page(
self, self,
@ -482,9 +469,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
""" """
# If the route is not set, get it from the callable. # If the route is not set, get it from the callable.
if route is None: if route is None:
assert isinstance( if not isinstance(component, Callable):
component, Callable raise ValueError("Route must be set if component is not a callable.")
), "Route must be set if component is not a callable."
# Format the route. # Format the route.
route = format.format_route(component.__name__) route = format.format_route(component.__name__)
else: else:
@ -824,7 +810,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
for dep in deps: for dep in deps:
if dep not in state.vars and dep not in state.backend_vars: if dep not in state.vars and dep not in state.backend_vars:
raise exceptions.VarDependencyError( raise exceptions.VarDependencyError(
f"ComputedVar {var._var_name} on state {state.__name__} has an invalid dependency {dep}" f"ComputedVar {var._js_expr} on state {state.__name__} has an invalid dependency {dep}"
) )
for substate in state.class_subclasses: for substate in state.class_subclasses:
@ -971,15 +957,16 @@ class App(MiddlewareMixin, LifespanMixin, Base):
executor = None executor = None
if ( if (
platform.system() in ("Linux", "Darwin") platform.system() in ("Linux", "Darwin")
and os.environ.get("REFLEX_COMPILE_PROCESSES") is not None and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES)
is not None
): ):
executor = concurrent.futures.ProcessPoolExecutor( executor = concurrent.futures.ProcessPoolExecutor(
max_workers=int(os.environ.get("REFLEX_COMPILE_PROCESSES", 0)) or None, max_workers=number_of_processes,
mp_context=multiprocessing.get_context("fork"), mp_context=multiprocessing.get_context("fork"),
) )
else: else:
executor = concurrent.futures.ThreadPoolExecutor( executor = concurrent.futures.ThreadPoolExecutor(
max_workers=int(os.environ.get("REFLEX_COMPILE_THREADS", 0)) or None, max_workers=environment.REFLEX_COMPILE_THREADS
) )
with executor: with executor:
@ -1528,20 +1515,28 @@ class EventNamespace(AsyncNamespace):
async def on_event(self, sid, data): async def on_event(self, sid, data):
"""Event for receiving front-end websocket events. """Event for receiving front-end websocket events.
Raises:
RuntimeError: If the Socket.IO is badly initialized.
Args: Args:
sid: The Socket.IO session id. sid: The Socket.IO session id.
data: The event data. data: The event data.
""" """
fields = json.loads(data)
# Get the event. # Get the event.
event = Event.parse_raw(data) event = Event(
**{k: v for k, v in fields.items() if k not in ("handler", "event_actions")}
)
self.token_to_sid[event.token] = sid self.token_to_sid[event.token] = sid
self.sid_to_token[sid] = event.token self.sid_to_token[sid] = event.token
# Get the event environment. # Get the event environment.
assert self.app.sio is not None if self.app.sio is None:
raise RuntimeError("Socket.IO is not initialized.")
environ = self.app.sio.get_environ(sid, self.namespace) environ = self.app.sio.get_environ(sid, self.namespace)
assert environ is not None if environ is None:
raise RuntimeError("Socket.IO environ is not initialized.")
# Get the client headers. # Get the client headers.
headers = { headers = {

View File

@ -6,11 +6,13 @@ import asyncio
import contextlib import contextlib
import functools import functools
import inspect import inspect
import sys
from typing import Callable, Coroutine, Set, Union from typing import Callable, Coroutine, Set, Union
from fastapi import FastAPI from fastapi import FastAPI
from reflex.utils import console
from reflex.utils.exceptions import InvalidLifespanTaskType
from .mixin import AppMixin from .mixin import AppMixin
@ -26,6 +28,7 @@ class LifespanMixin(AppMixin):
try: try:
async with contextlib.AsyncExitStack() as stack: async with contextlib.AsyncExitStack() as stack:
for task in self.lifespan_tasks: for task in self.lifespan_tasks:
run_msg = f"Started lifespan task: {task.__name__} as {{type}}" # type: ignore
if isinstance(task, asyncio.Task): if isinstance(task, asyncio.Task):
running_tasks.append(task) running_tasks.append(task)
else: else:
@ -35,15 +38,19 @@ class LifespanMixin(AppMixin):
_t = task() _t = task()
if isinstance(_t, contextlib._AsyncGeneratorContextManager): if isinstance(_t, contextlib._AsyncGeneratorContextManager):
await stack.enter_async_context(_t) await stack.enter_async_context(_t)
console.debug(run_msg.format(type="asynccontextmanager"))
elif isinstance(_t, Coroutine): elif isinstance(_t, Coroutine):
running_tasks.append(asyncio.create_task(_t)) task_ = asyncio.create_task(_t)
task_.add_done_callback(lambda t: t.result())
running_tasks.append(task_)
console.debug(run_msg.format(type="coroutine"))
else:
console.debug(run_msg.format(type="function"))
yield yield
finally: finally:
cancel_kwargs = (
{"msg": "lifespan_cleanup"} if sys.version_info >= (3, 9) else {}
)
for task in running_tasks: for task in running_tasks:
task.cancel(**cancel_kwargs) console.debug(f"Canceling lifespan task: {task}")
task.cancel(msg="lifespan_cleanup")
def register_lifespan_task(self, task: Callable | asyncio.Task, **task_kwargs): def register_lifespan_task(self, task: Callable | asyncio.Task, **task_kwargs):
"""Register a task to run during the lifespan of the app. """Register a task to run during the lifespan of the app.
@ -51,7 +58,18 @@ class LifespanMixin(AppMixin):
Args: Args:
task: The task to register. task: The task to register.
task_kwargs: The kwargs of the task. task_kwargs: The kwargs of the task.
Raises:
InvalidLifespanTaskType: If the task is a generator function.
""" """
if inspect.isgeneratorfunction(task) or inspect.isasyncgenfunction(task):
raise InvalidLifespanTaskType(
f"Task {task.__name__} of type generator must be decorated with contextlib.asynccontextmanager."
)
if task_kwargs: if task_kwargs:
original_task = task
task = functools.partial(task, **task_kwargs) # type: ignore task = functools.partial(task, **task_kwargs) # type: ignore
functools.update_wrapper(task, original_task) # type: ignore
self.lifespan_tasks.add(task) # type: ignore self.lifespan_tasks.add(task) # type: ignore
console.debug(f"Registered lifespan task: {task.__name__}") # type: ignore

View File

@ -15,7 +15,7 @@ if constants.CompileVars.APP != "app":
telemetry.send("compile") telemetry.send("compile")
app_module = get_app(reload=False) app_module = get_app(reload=False)
app = getattr(app_module, constants.CompileVars.APP) app = getattr(app_module, constants.CompileVars.APP)
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages # For py3.9 compatibility when redis is used, we MUST add any decorator pages
# before compiling the app in a thread to avoid event loop error (REF-2172). # before compiling the app in a thread to avoid event loop error (REF-2172).
app._apply_decorated_pages() app._apply_decorated_pages()
compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile) compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)

View File

@ -47,6 +47,9 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
# shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True) # shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
pydantic_main.validate_field_name = validate_field_name # type: ignore pydantic_main.validate_field_name = validate_field_name # type: ignore
if TYPE_CHECKING:
from reflex.vars import Var
class Base(BaseModel): # pyright: ignore [reportUnboundVariable] class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
"""The base class subclassed by all Reflex classes. """The base class subclassed by all Reflex classes.
@ -92,7 +95,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
return self return self
@classmethod @classmethod
def get_fields(cls) -> dict[str, Any]: def get_fields(cls) -> dict[str, ModelField]:
"""Get the fields of the object. """Get the fields of the object.
Returns: Returns:
@ -101,7 +104,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
return cls.__fields__ return cls.__fields__
@classmethod @classmethod
def add_field(cls, var: Any, default_value: Any): def add_field(cls, var: Var, default_value: Any):
"""Add a pydantic field after class definition. """Add a pydantic field after class definition.
Used by State.add_var() to correctly handle the new variable. Used by State.add_var() to correctly handle the new variable.
@ -110,7 +113,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
var: The variable to add a pydantic field for. var: The variable to add a pydantic field for.
default_value: The default value of the field default_value: The default value of the field
""" """
var_name = var._var_name.split(".")[-1] var_name = var._var_field_name
new_field = ModelField.infer( new_field = ModelField.infer(
name=var_name, name=var_name,
value=default_value, value=default_value,
@ -133,13 +136,4 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
# Seems like this function signature was wrong all along? # Seems like this function signature was wrong all along?
# If the user wants a field that we know of, get it and pass it off to _get_value # If the user wants a field that we know of, get it and pass it off to _get_value
key = getattr(self, key) key = getattr(self, key)
return self._get_value( return key
key,
to_dict=True,
by_alias=False,
include=None,
exclude=None,
exclude_unset=False,
exclude_defaults=False,
exclude_none=False,
)

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
import os
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict, Iterable, Optional, Type, Union from typing import Dict, Iterable, Optional, Type, Union
@ -16,14 +15,13 @@ from reflex.components.component import (
CustomComponent, CustomComponent,
StatefulComponent, StatefulComponent,
) )
from reflex.config import get_config from reflex.config import environment, get_config
from reflex.ivars.base import LiteralVar
from reflex.state import BaseState from reflex.state import BaseState
from reflex.style import SYSTEM_COLOR_MODE from reflex.style import SYSTEM_COLOR_MODE
from reflex.utils.exec import is_prod_mode from reflex.utils.exec import is_prod_mode
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.utils.prerequisites import get_web_dir from reflex.utils.prerequisites import get_web_dir
from reflex.vars import Var from reflex.vars.base import LiteralVar, Var
def _compile_document_root(root: Component) -> str: def _compile_document_root(root: Component) -> str:
@ -41,6 +39,20 @@ def _compile_document_root(root: Component) -> str:
) )
def _normalize_library_name(lib: str) -> str:
"""Normalize the library name.
Args:
lib: The library name to normalize.
Returns:
The normalized library name.
"""
if lib == "react":
return "React"
return lib.replace("@", "").replace("/", "_").replace("-", "_")
def _compile_app(app_root: Component) -> str: def _compile_app(app_root: Component) -> str:
"""Compile the app template component. """Compile the app template component.
@ -50,15 +62,25 @@ def _compile_app(app_root: Component) -> str:
Returns: Returns:
The compiled app. The compiled app.
""" """
from reflex.components.dynamic import bundled_libraries
window_libraries = [
(_normalize_library_name(name), name) for name in bundled_libraries
] + [
("utils_context", f"/{constants.Dirs.UTILS}/context"),
("utils_state", f"/{constants.Dirs.UTILS}/state"),
]
return templates.APP_ROOT.render( return templates.APP_ROOT.render(
imports=utils.compile_imports(app_root._get_all_imports()), imports=utils.compile_imports(app_root._get_all_imports()),
custom_codes=app_root._get_all_custom_code(), custom_codes=app_root._get_all_custom_code(),
hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()}, hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()},
window_libraries=window_libraries,
render=app_root.render(), render=app_root.render(),
) )
def _compile_theme(theme: dict) -> str: def _compile_theme(theme: str) -> str:
"""Compile the theme. """Compile the theme.
Args: Args:
@ -172,7 +194,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
stylesheet_full_path = ( stylesheet_full_path = (
Path.cwd() / constants.Dirs.APP_ASSETS / stylesheet.strip("/") Path.cwd() / constants.Dirs.APP_ASSETS / stylesheet.strip("/")
) )
if not os.path.exists(stylesheet_full_path): if not stylesheet_full_path.exists():
raise FileNotFoundError( raise FileNotFoundError(
f"The stylesheet file {stylesheet_full_path} does not exist." f"The stylesheet file {stylesheet_full_path} does not exist."
) )
@ -378,7 +400,7 @@ def compile_theme(style: ComponentStyle) -> tuple[str, str]:
theme = utils.create_theme(style) theme = utils.create_theme(style)
# Compile the theme. # Compile the theme.
code = _compile_theme(theme) code = _compile_theme(str(LiteralVar.create(theme)))
return output_path, code return output_path, code
@ -504,7 +526,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
def purge_web_pages_dir(): def purge_web_pages_dir():
"""Empty out .web/pages directory.""" """Empty out .web/pages directory."""
if not is_prod_mode() and os.environ.get("REFLEX_PERSIST_WEB_DIR"): if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR:
# Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set. # Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set.
return return

View File

@ -2,12 +2,12 @@
from __future__ import annotations from __future__ import annotations
import os
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, Optional, Type, Union from typing import Any, Callable, Dict, Optional, Type, Union
from urllib.parse import urlparse from urllib.parse import urlparse
from reflex.utils.prerequisites import get_web_dir from reflex.utils.prerequisites import get_web_dir
from reflex.vars.base import Var
try: try:
from pydantic.v1.fields import ModelField from pydantic.v1.fields import ModelField
@ -28,11 +28,11 @@ from reflex.components.base import (
Title, Title,
) )
from reflex.components.component import Component, ComponentStyle, CustomComponent from reflex.components.component import Component, ComponentStyle, CustomComponent
from reflex.state import BaseState, Cookie, LocalStorage, SessionStorage from reflex.istate.storage import Cookie, LocalStorage, SessionStorage
from reflex.state import BaseState
from reflex.style import Style from reflex.style import Style
from reflex.utils import console, format, imports, path_ops from reflex.utils import console, format, imports, path_ops
from reflex.utils.imports import ImportVar, ParsedImportDict from reflex.utils.imports import ImportVar, ParsedImportDict
from reflex.vars import Var
# To re-export this function. # To re-export this function.
merge_imports = imports.merge_imports merge_imports = imports.merge_imports
@ -44,6 +44,9 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
Args: Args:
fields: The set of fields to import from the library. fields: The set of fields to import from the library.
Raises:
ValueError: If there is more than one default import.
Returns: Returns:
The libraries for default and rest. The libraries for default and rest.
default: default library. When install "import def from library". default: default library. When install "import def from library".
@ -54,7 +57,8 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
# Check for default imports. # Check for default imports.
defaults = {field for field in fields_set if field.is_default} defaults = {field for field in fields_set if field.is_default}
assert len(defaults) < 2 if len(defaults) >= 2:
raise ValueError("Only one default import is allowed.")
# Get the default import, and the specific imports. # Get the default import, and the specific imports.
default = next(iter({field.name for field in defaults}), "") default = next(iter({field.name for field in defaults}), "")
@ -92,6 +96,9 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
Args: Args:
import_dict: The import dict to compile. import_dict: The import dict to compile.
Raises:
ValueError: If an import in the dict is invalid.
Returns: Returns:
The list of import dict. The list of import dict.
""" """
@ -106,8 +113,10 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
continue continue
if not lib: if not lib:
assert not default, "No default field allowed for empty library." if default:
assert rest is not None and len(rest) > 0, "No fields to import." raise ValueError("No default field allowed for empty library.")
if rest is None or len(rest) == 0:
raise ValueError("No fields to import.")
for module in sorted(rest): for module in sorted(rest):
import_dicts.append(get_import_dict(module)) import_dicts.append(get_import_dict(module))
continue continue
@ -152,8 +161,10 @@ def compile_state(state: Type[BaseState]) -> dict:
console.warn( console.warn(
f"Failed to compile initial state with computed vars, excluding them: {e}" f"Failed to compile initial state with computed vars, excluding them: {e}"
) )
initial_state = state(_reflex_internal_init=True).dict(include_computed=False) initial_state = state(_reflex_internal_init=True).dict(
return format.format_state(initial_state) initial=True, include_computed=False
)
return initial_state
def _compile_client_storage_field( def _compile_client_storage_field(
@ -266,7 +277,7 @@ def compile_custom_component(
} }
# Concatenate the props. # Concatenate the props.
props = [prop._var_name for prop in component.get_prop_vars()] props = [prop._js_expr for prop in component.get_prop_vars()]
# Compile the component. # Compile the component.
return ( return (
@ -427,11 +438,11 @@ def add_meta(
Returns: Returns:
The component with the metadata added. The component with the metadata added.
""" """
meta_tags = [Meta.create(**item) for item in meta] meta_tags = [
item if isinstance(item, Component) else Meta.create(**item) for item in meta
children: list[Any] = [
Title.create(title),
] ]
children: list[Any] = [Title.create(title)]
if description: if description:
children.append(Description.create(content=description)) children.append(Description.create(content=description))
children.append(Image.create(content=image)) children.append(Image.create(content=image))
@ -446,16 +457,16 @@ def add_meta(
return page return page
def write_page(path: str, code: str): def write_page(path: str | Path, code: str):
"""Write the given code to the given path. """Write the given code to the given path.
Args: Args:
path: The path to write the code to. path: The path to write the code to.
code: The code to write. code: The code to write.
""" """
path_ops.mkdir(os.path.dirname(path)) path = Path(path)
with open(path, "w", encoding="utf-8") as f: path_ops.mkdir(path.parent)
f.write(code) path.write_text(code, encoding="utf-8")
def empty_dir(path: str | Path, keep_files: list[str] | None = None): def empty_dir(path: str | Path, keep_files: list[str] | None = None):

View File

@ -2,7 +2,7 @@
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.components.component import Component from reflex.components.component import Component
from reflex.ivars.base import ImmutableVar from reflex.vars.base import Var
class AppWrap(Fragment): class AppWrap(Fragment):
@ -15,4 +15,4 @@ class AppWrap(Fragment):
Returns: Returns:
A new AppWrap component containing {children}. A new AppWrap component containing {children}.
""" """
return super().create(ImmutableVar.create("children")) return super().create(Var(_js_expr="children"))

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class AppWrap(Fragment): class AppWrap(Fragment):
@overload @overload
@ -22,41 +22,21 @@ class AppWrap(Fragment):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "AppWrap": ) -> "AppWrap":
"""Create a new AppWrap component. """Create a new AppWrap component.

View File

@ -7,8 +7,7 @@ from typing import Any, Iterator
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.tags import Tag from reflex.components.tags import Tag
from reflex.components.tags.tagless import Tagless from reflex.components.tags.tagless import Tagless
from reflex.ivars.base import ImmutableVar from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var
from reflex.vars import Var
class Bare(Component): class Bare(Component):
@ -26,16 +25,16 @@ class Bare(Component):
Returns: Returns:
The component. The component.
""" """
if isinstance(contents, ImmutableVar):
return cls(contents=contents)
if isinstance(contents, Var): if isinstance(contents, Var):
contents = contents.to(str) return cls(contents=contents)
else: else:
contents = str(contents) if contents is not None else "" contents = str(contents) if contents is not None else ""
return cls(contents=contents) # type: ignore return cls(contents=contents) # type: ignore
def _render(self) -> Tag: def _render(self) -> Tag:
if isinstance(self.contents, ImmutableVar): if isinstance(self.contents, Var):
if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)):
return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
return Tagless(contents=f"{{{str(self.contents)}}}") return Tagless(contents=f"{{{str(self.contents)}}}")
return Tagless(contents=str(self.contents)) return Tagless(contents=str(self.contents))

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class Body(Component): class Body(Component):
@overload @overload
@ -22,41 +22,21 @@ class Body(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Body": ) -> "Body":
"""Create the component. """Create the component.

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class NextDocumentLib(Component): class NextDocumentLib(Component):
@overload @overload
@ -22,41 +22,21 @@ class NextDocumentLib(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "NextDocumentLib": ) -> "NextDocumentLib":
"""Create the component. """Create the component.
@ -89,41 +69,21 @@ class Html(NextDocumentLib):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Html": ) -> "Html":
"""Create the component. """Create the component.
@ -155,41 +115,21 @@ class DocumentHead(NextDocumentLib):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "DocumentHead": ) -> "DocumentHead":
"""Create the component. """Create the component.
@ -221,41 +161,21 @@ class Main(NextDocumentLib):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Main": ) -> "Main":
"""Create the component. """Create the component.
@ -287,41 +207,21 @@ class NextScript(NextDocumentLib):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "NextScript": ) -> "NextScript":
"""Create the component. """Create the component.

View File

@ -2,17 +2,32 @@
from __future__ import annotations from __future__ import annotations
from typing import List from typing import Dict, List, Tuple
from reflex.compiler.compiler import _compile_component from reflex.compiler.compiler import _compile_component
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.el import div, p from reflex.components.el import div, p
from reflex.constants import Hooks, Imports from reflex.event import EventHandler
from reflex.event import EventChain, EventHandler from reflex.state import FrontendEventExceptionState
from reflex.ivars.base import ImmutableVar from reflex.vars.base import Var
from reflex.ivars.function import FunctionVar
from reflex.utils.imports import ImportVar
from reflex.vars import Var def on_error_spec(
error: Var[Dict[str, str]], info: Var[Dict[str, str]]
) -> Tuple[Var[str], Var[str]]:
"""The spec for the on_error event handler.
Args:
error: The error message.
info: Additional information about the error.
Returns:
The arguments for the event handler.
"""
return (
error.stack,
info.componentStack,
)
class ErrorBoundary(Component): class ErrorBoundary(Component):
@ -22,31 +37,13 @@ class ErrorBoundary(Component):
tag = "ErrorBoundary" tag = "ErrorBoundary"
# Fired when the boundary catches an error. # Fired when the boundary catches an error.
on_error: EventHandler[lambda error, info: [error, info]] = ImmutableVar( # type: ignore on_error: EventHandler[on_error_spec]
"logFrontendError"
).to(FunctionVar, EventChain)
# Rendered instead of the children when an error is caught. # Rendered instead of the children when an error is caught.
Fallback_component: Var[Component] = ImmutableVar.create_safe("Fallback")._replace( Fallback_component: Var[Component] = Var(_js_expr="Fallback")._replace(
_var_type=Component _var_type=Component
) )
def add_imports(self) -> dict[str, list[ImportVar]]:
"""Add imports for the component.
Returns:
The imports to add.
"""
return Imports.EVENTS
def add_hooks(self) -> List[str | Var]:
"""Add hooks for the component.
Returns:
The hooks to add.
"""
return [Hooks.EVENTS, Hooks.FRONTEND_ERRORS]
def add_custom_code(self) -> List[str]: def add_custom_code(self) -> List[str]:
"""Add custom Javascript code into the page that contains this component. """Add custom Javascript code into the page that contains this component.
@ -58,7 +55,7 @@ class ErrorBoundary(Component):
fallback_container = div( fallback_container = div(
p("Ooops...Unknown Reflex error has occured:"), p("Ooops...Unknown Reflex error has occured:"),
p( p(
ImmutableVar.create("error.message"), Var(_js_expr="error.message"),
color="red", color="red",
), ),
p("Please contact the support."), p("Please contact the support."),
@ -76,5 +73,20 @@ class ErrorBoundary(Component):
""" """
] ]
@classmethod
def create(cls, *children, **props):
"""Create an ErrorBoundary component.
Args:
*children: The children of the component.
**props: The props of the component.
Returns:
The ErrorBoundary component.
"""
if "on_error" not in props:
props["on_error"] = FrontendEventExceptionState.handle_frontend_exception
return super().create(*children, **props)
error_boundary = ErrorBoundary.create error_boundary = ErrorBoundary.create

View File

@ -3,69 +3,50 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, List, Optional, Union, overload from typing import Any, Dict, List, Optional, Tuple, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.utils.imports import ImportVar from reflex.vars.base import Var
from reflex.vars import Var
def on_error_spec(
error: Var[Dict[str, str]], info: Var[Dict[str, str]]
) -> Tuple[Var[str], Var[str]]: ...
class ErrorBoundary(Component): class ErrorBoundary(Component):
def add_imports(self) -> dict[str, list[ImportVar]]: ...
def add_hooks(self) -> List[str | Var]: ...
def add_custom_code(self) -> List[str]: ... def add_custom_code(self) -> List[str]: ...
@overload @overload
@classmethod @classmethod
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
Fallback_component: Optional[Union[Var[Component], Component]] = None, Fallback_component: Optional[Union[Component, Var[Component]]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_error: Optional[EventType[str, str]] = None,
on_double_click: Optional[ on_focus: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mount: Optional[EventType[[]]] = None,
] = None, on_mouse_down: Optional[EventType[[]]] = None,
on_error: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ErrorBoundary": ) -> "ErrorBoundary":
"""Create the component. """Create an ErrorBoundary component.
Args: Args:
*children: The children of the component. *children: The children of the component.
@ -79,7 +60,7 @@ class ErrorBoundary(Component):
**props: The props of the component. **props: The props of the component.
Returns: Returns:
The component. The ErrorBoundary component.
""" """
... ...

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class Fragment(Component): class Fragment(Component):
@overload @overload
@ -22,41 +22,21 @@ class Fragment(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Fragment": ) -> "Fragment":
"""Create the component. """Create the component.

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component, MemoizationLeaf from reflex.components.component import Component, MemoizationLeaf
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class NextHeadLib(Component): class NextHeadLib(Component):
@overload @overload
@ -22,41 +22,21 @@ class NextHeadLib(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "NextHeadLib": ) -> "NextHeadLib":
"""Create the component. """Create the component.
@ -88,41 +68,21 @@ class Head(NextHeadLib, MemoizationLeaf):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Head": ) -> "Head":
"""Create a new memoization leaf component. """Create a new memoization leaf component.

View File

@ -1,7 +1,7 @@
"""Display the title of the current page.""" """Display the title of the current page."""
from reflex.components.component import Component from reflex.components.component import Component
from reflex.vars import Var from reflex.vars.base import Var
class RawLink(Component): class RawLink(Component):

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class RawLink(Component): class RawLink(Component):
@overload @overload
@ -24,41 +24,21 @@ class RawLink(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "RawLink": ) -> "RawLink":
"""Create the component. """Create the component.
@ -99,41 +79,21 @@ class ScriptTag(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ScriptTag": ) -> "ScriptTag":
"""Create the component. """Create the component.

View File

@ -16,13 +16,15 @@ class Title(Component):
def render(self) -> dict: def render(self) -> dict:
"""Render the title component. """Render the title component.
Raises:
ValueError: If the title is not a single string.
Returns: Returns:
The rendered title component. The rendered title component.
""" """
# Make sure the title is a single string. # Make sure the title is a single string.
assert len(self.children) == 1 and isinstance( if len(self.children) != 1 or not isinstance(self.children[0], Bare):
self.children[0], Bare raise ValueError("Title must be a single string.")
), "Title must be a single string."
return super().render() return super().render()

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class Title(Component): class Title(Component):
def render(self) -> dict: ... def render(self) -> dict: ...
@ -23,41 +23,21 @@ class Title(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Title": ) -> "Title":
"""Create the component. """Create the component.
@ -94,41 +74,21 @@ class Meta(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Meta": ) -> "Meta":
"""Create the component. """Create the component.
@ -170,41 +130,21 @@ class Description(Meta):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Description": ) -> "Description":
"""Create the component. """Create the component.
@ -246,41 +186,21 @@ class Image(Meta):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Image": ) -> "Image":
"""Create the component. """Create the component.

View File

@ -8,9 +8,8 @@ from __future__ import annotations
from typing import Literal from typing import Literal
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler from reflex.event import EventHandler, empty_event
from reflex.ivars.base import LiteralVar from reflex.vars.base import LiteralVar, Var
from reflex.vars import Var
class Script(Component): class Script(Component):
@ -36,13 +35,13 @@ class Script(Component):
) )
# Triggered when the script is loading # Triggered when the script is loading
on_load: EventHandler[lambda: []] on_load: EventHandler[empty_event]
# Triggered when the script has loaded # Triggered when the script has loaded
on_ready: EventHandler[lambda: []] on_ready: EventHandler[empty_event]
# Triggered when the script has errored # Triggered when the script has errored
on_error: EventHandler[lambda: []] on_error: EventHandler[empty_event]
@classmethod @classmethod
def create(cls, *children, **props) -> Component: def create(cls, *children, **props) -> Component:

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Literal, Optional, Union, overload from typing import Any, Dict, Literal, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class Script(Component): class Script(Component):
@overload @overload
@ -19,8 +19,8 @@ class Script(Component):
src: Optional[Union[Var[str], str]] = None, src: Optional[Union[Var[str], str]] = None,
strategy: Optional[ strategy: Optional[
Union[ Union[
Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]],
Literal["afterInteractive", "beforeInteractive", "lazyOnload"], Literal["afterInteractive", "beforeInteractive", "lazyOnload"],
Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]],
] ]
] = None, ] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
@ -29,44 +29,24 @@ class Script(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_error: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_focus: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_load: Optional[EventType[[]]] = None,
] = None, on_mount: Optional[EventType[[]]] = None,
on_error: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_down: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_load: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_ready: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_scroll: Optional[EventType[[]]] = None,
] = None, on_unmount: Optional[EventType[[]]] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_ready: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Script": ) -> "Script":
"""Create an inline or user-defined script. """Create an inline or user-defined script.

View File

@ -25,6 +25,7 @@ import reflex.state
from reflex.base import Base from reflex.base import Base
from reflex.compiler.templates import STATEFUL_COMPONENT from reflex.compiler.templates import STATEFUL_COMPONENT
from reflex.components.core.breakpoints import Breakpoints from reflex.components.core.breakpoints import Breakpoints
from reflex.components.dynamic import load_dynamic_serializer
from reflex.components.tags import Tag from reflex.components.tags import Tag
from reflex.constants import ( from reflex.constants import (
Dirs, Dirs,
@ -35,20 +36,30 @@ from reflex.constants import (
MemoizationMode, MemoizationMode,
PageNames, PageNames,
) )
from reflex.constants.compiler import SpecialAttributes
from reflex.event import ( from reflex.event import (
EventChain, EventChain,
EventChainVar,
EventHandler, EventHandler,
EventSpec, EventSpec,
EventVar,
call_event_fn, call_event_fn,
call_event_handler, call_event_handler,
empty_event,
get_handler_args, get_handler_args,
) )
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.style import Style, format_as_emotion from reflex.style import Style, format_as_emotion
from reflex.utils import format, imports, types from reflex.utils import format, imports, types
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports from reflex.utils.imports import (
from reflex.utils.serializers import serializer ImmutableParsedImportDict,
from reflex.vars import BaseVar, ImmutableVarData, Var, VarData ImportDict,
ImportVar,
ParsedImportDict,
parse_imports,
)
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import LiteralArrayVar
class BaseComponent(Base, ABC): class BaseComponent(Base, ABC):
@ -189,7 +200,7 @@ class Component(BaseComponent, ABC):
class_name: Any = None class_name: Any = None
# Special component props. # Special component props.
special_props: Set[Var] = set() special_props: List[Var] = []
# Whether the component should take the focus once the page is loaded # Whether the component should take the focus once the page is loaded
autofocus: bool = False autofocus: bool = False
@ -441,9 +452,17 @@ class Component(BaseComponent, ABC):
not passed_types not passed_types
and not types._issubclass(passed_type, expected_type, value) and not types._issubclass(passed_type, expected_type, value)
): ):
value_name = value._var_name if isinstance(value, Var) else value value_name = value._js_expr if isinstance(value, Var) else value
additional_info = (
" You can call `.bool()` on the value to convert it to a boolean."
if expected_type is bool and isinstance(value, Var)
else ""
)
raise TypeError( raise TypeError(
f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_types or passed_type}." f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_type}."
+ additional_info
) )
# Check if the key is an event trigger. # Check if the key is an event trigger.
if key in component_specific_triggers: if key in component_specific_triggers:
@ -457,13 +476,24 @@ class Component(BaseComponent, ABC):
for key in kwargs["event_triggers"]: for key in kwargs["event_triggers"]:
del kwargs[key] del kwargs[key]
# Place data_ and aria_ attributes into custom_attrs
special_attributes = tuple(
key
for key in kwargs
if key not in fields and SpecialAttributes.is_special(key)
)
if special_attributes:
custom_attrs = kwargs.setdefault("custom_attrs", {})
for key in special_attributes:
custom_attrs[format.to_kebab_case(key)] = kwargs.pop(key)
# Add style props to the component. # Add style props to the component.
style = kwargs.get("style", {}) style = kwargs.get("style", {})
if isinstance(style, List): if isinstance(style, List):
# Merge styles, the later ones overriding keys in the earlier ones. # Merge styles, the later ones overriding keys in the earlier ones.
style = {k: v for style_dict in style for k, v in style_dict.items()} style = {k: v for style_dict in style for k, v in style_dict.items()}
if isinstance(style, Breakpoints): if isinstance(style, (Breakpoints, Var)):
style = { style = {
# Assign the Breakpoints to the self-referential selector to avoid squashing down to a regular dict. # Assign the Breakpoints to the self-referential selector to avoid squashing down to a regular dict.
"&": style, "&": style,
@ -476,13 +506,16 @@ class Component(BaseComponent, ABC):
**{attr: value for attr, value in kwargs.items() if attr not in fields}, **{attr: value for attr, value in kwargs.items() if attr not in fields},
} }
) )
if "custom_attrs" not in kwargs:
kwargs["custom_attrs"] = {}
# Convert class_name to str if it's list # Convert class_name to str if it's list
class_name = kwargs.get("class_name", "") class_name = kwargs.get("class_name", "")
if isinstance(class_name, (List, tuple)): if isinstance(class_name, (List, tuple)):
kwargs["class_name"] = " ".join(class_name) if any(isinstance(c, Var) for c in class_name):
kwargs["class_name"] = LiteralArrayVar.create(
class_name, _var_type=List[str]
).join(" ")
else:
kwargs["class_name"] = " ".join(class_name)
# Construct the component. # Construct the component.
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -491,7 +524,11 @@ class Component(BaseComponent, ABC):
self, self,
args_spec: Any, args_spec: Any,
value: Union[ value: Union[
Var, EventHandler, EventSpec, List[Union[EventHandler, EventSpec]], Callable Var,
EventHandler,
EventSpec,
List[Union[EventHandler, EventSpec, EventVar]],
Callable,
], ],
) -> Union[EventChain, Var]: ) -> Union[EventChain, Var]:
"""Create an event chain from a variety of input types. """Create an event chain from a variety of input types.
@ -508,11 +545,16 @@ class Component(BaseComponent, ABC):
""" """
# If it's an event chain var, return it. # If it's an event chain var, return it.
if isinstance(value, Var): if isinstance(value, Var):
if value._var_type is not EventChain: if isinstance(value, EventChainVar):
return value
elif isinstance(value, EventVar):
value = [value]
elif issubclass(value._var_type, (EventChain, EventSpec)):
return self._create_event_chain(args_spec, value.guess_type())
else:
raise ValueError( raise ValueError(
f"Invalid event chain: {repr(value)} of type {type(value)}" f"Invalid event chain: {str(value)} of type {value._var_type}"
) )
return value
elif isinstance(value, EventChain): elif isinstance(value, EventChain):
# Trust that the caller knows what they're doing passing an EventChain directly # Trust that the caller knows what they're doing passing an EventChain directly
return value return value
@ -523,7 +565,7 @@ class Component(BaseComponent, ABC):
# If the input is a list of event handlers, create an event chain. # If the input is a list of event handlers, create an event chain.
if isinstance(value, List): if isinstance(value, List):
events: list[EventSpec] = [] events: List[Union[EventSpec, EventVar]] = []
for v in value: for v in value:
if isinstance(v, (EventHandler, EventSpec)): if isinstance(v, (EventHandler, EventSpec)):
# Call the event handler to get the event. # Call the event handler to get the event.
@ -537,6 +579,8 @@ class Component(BaseComponent, ABC):
"lambda inside an EventChain list." "lambda inside an EventChain list."
) )
events.extend(result) events.extend(result)
elif isinstance(v, EventVar):
events.append(v)
else: else:
raise ValueError(f"Invalid event: {v}") raise ValueError(f"Invalid event: {v}")
@ -546,32 +590,30 @@ class Component(BaseComponent, ABC):
if isinstance(result, Var): if isinstance(result, Var):
# Recursively call this function if the lambda returned an EventChain Var. # Recursively call this function if the lambda returned an EventChain Var.
return self._create_event_chain(args_spec, result) return self._create_event_chain(args_spec, result)
events = result events = [*result]
# Otherwise, raise an error. # Otherwise, raise an error.
else: else:
raise ValueError(f"Invalid event chain: {value}") raise ValueError(f"Invalid event chain: {value}")
# Add args to the event specs if necessary. # Add args to the event specs if necessary.
events = [e.with_args(get_handler_args(e)) for e in events] events = [
(e.with_args(get_handler_args(e)) if isinstance(e, EventSpec) else e)
# Collect event_actions from each spec for e in events
event_actions = {} ]
for e in events:
event_actions.update(e.event_actions)
# Return the event chain. # Return the event chain.
if isinstance(args_spec, Var): if isinstance(args_spec, Var):
return EventChain( return EventChain(
events=events, events=events,
args_spec=None, args_spec=None,
event_actions=event_actions, event_actions={},
) )
else: else:
return EventChain( return EventChain(
events=events, events=events,
args_spec=args_spec, args_spec=args_spec,
event_actions=event_actions, event_actions={},
) )
def get_event_triggers(self) -> Dict[str, Any]: def get_event_triggers(self) -> Dict[str, Any]:
@ -582,21 +624,21 @@ class Component(BaseComponent, ABC):
""" """
default_triggers = { default_triggers = {
EventTriggers.ON_FOCUS: lambda: [], EventTriggers.ON_FOCUS: empty_event,
EventTriggers.ON_BLUR: lambda: [], EventTriggers.ON_BLUR: empty_event,
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_CONTEXT_MENU: lambda: [], EventTriggers.ON_CONTEXT_MENU: empty_event,
EventTriggers.ON_DOUBLE_CLICK: lambda: [], EventTriggers.ON_DOUBLE_CLICK: empty_event,
EventTriggers.ON_MOUSE_DOWN: lambda: [], EventTriggers.ON_MOUSE_DOWN: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
EventTriggers.ON_MOUSE_MOVE: lambda: [], EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OUT: lambda: [], EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_OVER: lambda: [], EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_UP: lambda: [], EventTriggers.ON_MOUSE_UP: empty_event,
EventTriggers.ON_SCROLL: lambda: [], EventTriggers.ON_SCROLL: empty_event,
EventTriggers.ON_MOUNT: lambda: [], EventTriggers.ON_MOUNT: empty_event,
EventTriggers.ON_UNMOUNT: lambda: [], EventTriggers.ON_UNMOUNT: empty_event,
} }
# Look for component specific triggers, # Look for component specific triggers,
@ -605,9 +647,9 @@ class Component(BaseComponent, ABC):
if types._issubclass(field.type_, EventHandler): if types._issubclass(field.type_, EventHandler):
args_spec = None args_spec = None
annotation = field.annotation annotation = field.annotation
if hasattr(annotation, "__metadata__"): if (metadata := getattr(annotation, "__metadata__", None)) is not None:
args_spec = annotation.__metadata__[0] args_spec = metadata[0]
default_triggers[field.name] = args_spec or (lambda: []) default_triggers[field.name] = args_spec or (empty_event) # type: ignore
return default_triggers return default_triggers
def __repr__(self) -> str: def __repr__(self) -> str:
@ -647,7 +689,7 @@ class Component(BaseComponent, ABC):
""" """
# Create the base tag. # Create the base tag.
tag = Tag( tag = Tag(
name=self.tag if not self.alias else self.alias, name=(self.tag if not self.alias else self.alias) or "",
special_props=self.special_props, special_props=self.special_props,
) )
@ -661,7 +703,7 @@ class Component(BaseComponent, ABC):
# Add ref to element if `id` is not None. # Add ref to element if `id` is not None.
ref = self.get_ref() ref = self.get_ref()
if ref is not None: if ref is not None:
props["ref"] = ImmutableVar.create(ref) props["ref"] = Var(_js_expr=ref)
else: else:
props = props.copy() props = props.copy()
@ -1006,8 +1048,11 @@ class Component(BaseComponent, ABC):
elif isinstance(event, EventChain): elif isinstance(event, EventChain):
event_args = [] event_args = []
for spec in event.events: for spec in event.events:
for args in spec.args: if isinstance(spec, EventSpec):
event_args.extend(args) for args in spec.args:
event_args.extend(args)
else:
event_args.append(spec)
yield event_trigger, event_args yield event_trigger, event_args
def _get_vars(self, include_children: bool = False) -> list[Var]: def _get_vars(self, include_children: bool = False) -> list[Var]:
@ -1036,10 +1081,10 @@ class Component(BaseComponent, ABC):
# Style keeps track of its own VarData instance, so embed in a temp Var that is yielded. # Style keeps track of its own VarData instance, so embed in a temp Var that is yielded.
if isinstance(self.style, dict) and self.style or isinstance(self.style, Var): if isinstance(self.style, dict) and self.style or isinstance(self.style, Var):
vars.append( vars.append(
ImmutableVar( Var(
_var_name="style", _js_expr="style",
_var_type=str, _var_type=str,
_var_data=ImmutableVarData.merge(self.style._var_data), _var_data=VarData.merge(self.style._var_data),
) )
) )
@ -1081,8 +1126,12 @@ class Component(BaseComponent, ABC):
for trigger in self.event_triggers.values(): for trigger in self.event_triggers.values():
if isinstance(trigger, EventChain): if isinstance(trigger, EventChain):
for event in trigger.events: for event in trigger.events:
if event.handler.state_full_name: if isinstance(event, EventSpec):
return True if event.handler.state_full_name:
return True
else:
if event._var_state:
return True
elif isinstance(trigger, Var) and trigger._var_state: elif isinstance(trigger, Var) and trigger._var_state:
return True return True
return False return False
@ -1303,9 +1352,15 @@ class Component(BaseComponent, ABC):
# Collect imports from Vars used directly by this component. # Collect imports from Vars used directly by this component.
var_datas = [var._get_all_var_data() for var in self._get_vars()] var_datas = [var._get_all_var_data() for var in self._get_vars()]
var_imports = [ var_imports: List[ImmutableParsedImportDict] = list(
var_data.imports for var_data in var_datas if var_data is not None map(
] lambda var_data: var_data.imports,
filter(
None,
var_datas,
),
)
)
added_import_dicts: list[ParsedImportDict] = [] added_import_dicts: list[ParsedImportDict] = []
for clz in self._iter_parent_classes_with_method("add_imports"): for clz in self._iter_parent_classes_with_method("add_imports"):
@ -1352,9 +1407,9 @@ class Component(BaseComponent, ABC):
on_mount = self.event_triggers.get(EventTriggers.ON_MOUNT, None) on_mount = self.event_triggers.get(EventTriggers.ON_MOUNT, None)
on_unmount = self.event_triggers.get(EventTriggers.ON_UNMOUNT, None) on_unmount = self.event_triggers.get(EventTriggers.ON_UNMOUNT, None)
if on_mount is not None: if on_mount is not None:
on_mount = format.format_event_chain(on_mount) on_mount = str(LiteralVar.create(on_mount)) + "()"
if on_unmount is not None: if on_unmount is not None:
on_unmount = format.format_event_chain(on_unmount) on_unmount = str(LiteralVar.create(on_unmount)) + "()"
if on_mount is not None or on_unmount is not None: if on_mount is not None or on_unmount is not None:
return f""" return f"""
useEffect(() => {{ useEffect(() => {{
@ -1372,7 +1427,7 @@ class Component(BaseComponent, ABC):
""" """
ref = self.get_ref() ref = self.get_ref()
if ref is not None: if ref is not None:
return f"const {ref} = useRef(null); {str(ImmutableVar.create_safe(ref).as_ref())} = {ref};" return f"const {ref} = useRef(null); {str(Var(_js_expr=ref).as_ref())} = {ref};"
def _get_vars_hooks(self) -> dict[str, None]: def _get_vars_hooks(self) -> dict[str, None]:
"""Get the hooks required by vars referenced in this component. """Get the hooks required by vars referenced in this component.
@ -1513,7 +1568,7 @@ class Component(BaseComponent, ABC):
The ref name. The ref name.
""" """
# do not create a ref if the id is dynamic or unspecified # do not create a ref if the id is dynamic or unspecified
if self.id is None or isinstance(self.id, (BaseVar, ImmutableVar)): if self.id is None or isinstance(self.id, Var):
return None return None
return format.format_ref(self.id) return format.format_ref(self.id)
@ -1651,7 +1706,7 @@ class CustomComponent(Component):
value = self._create_event_chain( value = self._create_event_chain(
value=value, value=value,
args_spec=event_triggers_in_component_declaration.get( args_spec=event_triggers_in_component_declaration.get(
key, lambda: [] key, empty_event
), ),
) )
self.props[format.to_camel_case(key)] = value self.props[format.to_camel_case(key)] = value
@ -1714,10 +1769,14 @@ class CustomComponent(Component):
Args: Args:
seen: The tags of the components that have already been seen. seen: The tags of the components that have already been seen.
Raises:
ValueError: If the tag is not set.
Returns: Returns:
The set of custom components. The set of custom components.
""" """
assert self.tag is not None, "The tag must be set." if self.tag is None:
raise ValueError("The tag must be set.")
# Store the seen components in a set to avoid infinite recursion. # Store the seen components in a set to avoid infinite recursion.
if seen is None: if seen is None:
@ -1752,15 +1811,15 @@ class CustomComponent(Component):
""" """
return super()._render(props=self.props) return super()._render(props=self.props)
def get_prop_vars(self) -> List[ImmutableVar]: def get_prop_vars(self) -> List[Var]:
"""Get the prop vars. """Get the prop vars.
Returns: Returns:
The prop vars. The prop vars.
""" """
return [ return [
ImmutableVar( Var(
_var_name=name, _js_expr=name,
_var_type=( _var_type=(
prop._var_type if types._isinstance(prop, Var) else type(prop) prop._var_type if types._isinstance(prop, Var) else type(prop)
), ),
@ -1866,19 +1925,6 @@ class NoSSRComponent(Component):
return "".join((library_import, mod_import, opts_fragment)) return "".join((library_import, mod_import, opts_fragment))
@serializer
def serialize_component(comp: Component):
"""Serialize a component.
Args:
comp: The component to serialize.
Returns:
The serialized component.
"""
return str(comp)
class StatefulComponent(BaseComponent): class StatefulComponent(BaseComponent):
"""A component that depends on state and is rendered outside of the page component. """A component that depends on state and is rendered outside of the page component.
@ -2130,9 +2176,7 @@ class StatefulComponent(BaseComponent):
# Get the actual EventSpec and render it. # Get the actual EventSpec and render it.
event = component.event_triggers[event_trigger] event = component.event_triggers[event_trigger]
rendered_chain = format.format_prop(event) rendered_chain = str(LiteralVar.create(event))
if isinstance(rendered_chain, str):
rendered_chain = rendered_chain.strip("{}")
# Hash the rendered EventChain to get a deterministic function name. # Hash the rendered EventChain to get a deterministic function name.
chain_hash = md5(str(rendered_chain).encode("utf-8")).hexdigest() chain_hash = md5(str(rendered_chain).encode("utf-8")).hexdigest()
@ -2141,9 +2185,10 @@ class StatefulComponent(BaseComponent):
# Calculate Var dependencies accessed by the handler for useCallback dep array. # Calculate Var dependencies accessed by the handler for useCallback dep array.
var_deps = ["addEvents", "Event"] var_deps = ["addEvents", "Event"]
for arg in event_args: for arg in event_args:
if arg._get_all_var_data() is None: var_data = arg._get_all_var_data()
if var_data is None:
continue continue
for hook in arg._get_all_var_data().hooks: for hook in var_data.hooks:
var_deps.extend(cls._get_hook_deps(hook)) var_deps.extend(cls._get_hook_deps(hook))
memo_var_data = VarData.merge( memo_var_data = VarData.merge(
*[var._get_all_var_data() for var in event_args], *[var._get_all_var_data() for var in event_args],
@ -2154,7 +2199,7 @@ class StatefulComponent(BaseComponent):
# Store the memoized function name and hook code for this event trigger. # Store the memoized function name and hook code for this event trigger.
trigger_memo[event_trigger] = ( trigger_memo[event_trigger] = (
ImmutableVar.create_safe(memo_name)._replace( Var(_js_expr=memo_name)._replace(
_var_type=EventChain, merge_var_data=memo_var_data _var_type=EventChain, merge_var_data=memo_var_data
), ),
f"const {memo_name} = useCallback({rendered_chain}, [{', '.join(var_deps)}])", f"const {memo_name} = useCallback({rendered_chain}, [{', '.join(var_deps)}])",
@ -2227,7 +2272,7 @@ class StatefulComponent(BaseComponent):
Returns: Returns:
The tag to render. The tag to render.
""" """
return dict(Tag(name=self.tag)) return dict(Tag(name=self.tag or ""))
def __str__(self) -> str: def __str__(self) -> str:
"""Represent the component in React. """Represent the component in React.
@ -2292,3 +2337,6 @@ class MemoizationLeaf(Component):
update={"disposition": MemoizationDisposition.ALWAYS} update={"disposition": MemoizationDisposition.ALWAYS}
) )
return comp return comp
load_dynamic_serializer()

View File

@ -18,57 +18,53 @@ from reflex.components.radix.themes.typography.text import Text
from reflex.components.sonner.toast import Toaster, ToastProps from reflex.components.sonner.toast import Toaster, ToastProps
from reflex.constants import Dirs, Hooks, Imports from reflex.constants import Dirs, Hooks, Imports
from reflex.constants.compiler import CompileVars from reflex.constants.compiler import CompileVars
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.ivars.function import FunctionStringVar
from reflex.ivars.number import BooleanVar
from reflex.ivars.sequence import LiteralArrayVar
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import ImmutableVarData, Var, VarData from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
from reflex.vars.function import FunctionStringVar
from reflex.vars.number import BooleanVar
from reflex.vars.sequence import LiteralArrayVar
connect_error_var_data: VarData = VarData( # type: ignore connect_error_var_data: VarData = VarData( # type: ignore
imports=Imports.EVENTS, imports=Imports.EVENTS,
hooks={Hooks.EVENTS: None}, hooks={Hooks.EVENTS: None},
) )
connect_errors: Var = ImmutableVar.create_safe( connect_errors = Var(
value=CompileVars.CONNECT_ERROR, _js_expr=CompileVars.CONNECT_ERROR, _var_data=connect_error_var_data
)
connection_error = Var(
_js_expr="((connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : '')",
_var_data=connect_error_var_data, _var_data=connect_error_var_data,
) )
connection_error: Var = ImmutableVar.create_safe( connection_errors_count = Var(
value="((connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : '')", _js_expr="connectErrors.length", _var_data=connect_error_var_data
_var_data=connect_error_var_data,
) )
connection_errors_count: Var = ImmutableVar.create_safe( has_connection_errors = Var(
value="connectErrors.length", _js_expr="(connectErrors.length > 0)", _var_data=connect_error_var_data
_var_data=connect_error_var_data,
)
has_connection_errors: Var = ImmutableVar.create_safe(
value="(connectErrors.length > 0)",
_var_data=connect_error_var_data,
).to(BooleanVar) ).to(BooleanVar)
has_too_many_connection_errors: Var = ImmutableVar.create_safe( has_too_many_connection_errors = Var(
value="(connectErrors.length >= 2)", _js_expr="(connectErrors.length >= 2)", _var_data=connect_error_var_data
_var_data=connect_error_var_data,
).to(BooleanVar) ).to(BooleanVar)
class WebsocketTargetURL(ImmutableVar): class WebsocketTargetURL(Var):
"""A component that renders the websocket target URL.""" """A component that renders the websocket target URL."""
@classmethod @classmethod
def create(cls) -> ImmutableVar: def create(cls) -> Var:
"""Create a websocket target URL component. """Create a websocket target URL component.
Returns: Returns:
The websocket target URL component. The websocket target URL component.
""" """
return ImmutableVar( return Var(
_var_name="getBackendURL(env.EVENT).href", _js_expr="getBackendURL(env.EVENT).href",
_var_data=ImmutableVarData( _var_data=VarData(
imports={ imports={
"/env.json": [ImportVar(tag="env", is_default=True)], "/env.json": [ImportVar(tag="env", is_default=True)],
f"/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")], f"/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")],
@ -125,8 +121,8 @@ class ConnectionToaster(Toaster):
), ),
).call( ).call(
# TODO: This breaks the assumption that Vars are JS expressions # TODO: This breaks the assumption that Vars are JS expressions
ImmutableVar.create_safe( Var(
f""" _js_expr=f"""
() => {{ () => {{
if ({str(has_too_many_connection_errors)}) {{ if ({str(has_too_many_connection_errors)}) {{
if (!userDismissed) {{ if (!userDismissed) {{
@ -238,7 +234,7 @@ class WifiOffPulse(Icon):
Returns: Returns:
The icon component with default props applied. The icon component with default props applied.
""" """
pulse_var = ImmutableVar.create("pulse") pulse_var = Var(_js_expr="pulse")
return super().create( return super().create(
"wifi_off", "wifi_off",
color=props.pop("color", "crimson"), color=props.pop("color", "crimson"),

View File

@ -3,28 +3,41 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Literal, Optional, Union, overload from typing import Any, Dict, Literal, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.el.elements.typography import Div from reflex.components.el.elements.typography import Div
from reflex.components.lucide.icon import Icon from reflex.components.lucide.icon import Icon
from reflex.components.sonner.toast import Toaster, ToastProps from reflex.components.sonner.toast import Toaster, ToastProps
from reflex.event import EventHandler, EventSpec from reflex.constants.compiler import CompileVars
from reflex.ivars.base import ImmutableVar from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import Var, VarData from reflex.vars import VarData
from reflex.vars.base import Var
from reflex.vars.number import BooleanVar
connect_error_var_data: VarData connect_error_var_data: VarData
connect_errors: Var connect_errors = Var(
connection_error: Var _js_expr=CompileVars.CONNECT_ERROR, _var_data=connect_error_var_data
connection_errors_count: Var )
has_connection_errors: Var connection_error = Var(
has_too_many_connection_errors: Var _js_expr="((connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : '')",
_var_data=connect_error_var_data,
)
connection_errors_count = Var(
_js_expr="connectErrors.length", _var_data=connect_error_var_data
)
has_connection_errors = Var(
_js_expr="(connectErrors.length > 0)", _var_data=connect_error_var_data
).to(BooleanVar)
has_too_many_connection_errors = Var(
_js_expr="(connectErrors.length >= 2)", _var_data=connect_error_var_data
).to(BooleanVar)
class WebsocketTargetURL(ImmutableVar): class WebsocketTargetURL(Var):
@classmethod @classmethod
def create(cls) -> ImmutableVar: ... # type: ignore def create(cls) -> Var: ... # type: ignore
def default_connection_error() -> list[str | Var | Component]: ... def default_connection_error() -> list[str | Var | Component]: ...
@ -41,24 +54,24 @@ class ConnectionToaster(Toaster):
visible_toasts: Optional[Union[Var[int], int]] = None, visible_toasts: Optional[Union[Var[int], int]] = None,
position: Optional[ position: Optional[
Union[ Union[
Literal[
"bottom-center",
"bottom-left",
"bottom-right",
"top-center",
"top-left",
"top-right",
],
Var[ Var[
Literal[ Literal[
"top-left",
"top-center",
"top-right",
"bottom-left",
"bottom-center", "bottom-center",
"bottom-left",
"bottom-right", "bottom-right",
"top-center",
"top-left",
"top-right",
] ]
], ],
Literal[
"top-left",
"top-center",
"top-right",
"bottom-left",
"bottom-center",
"bottom-right",
],
] ]
] = None, ] = None,
close_button: Optional[Union[Var[bool], bool]] = None, close_button: Optional[Union[Var[bool], bool]] = None,
@ -66,9 +79,9 @@ class ConnectionToaster(Toaster):
dir: Optional[Union[Var[str], str]] = None, dir: Optional[Union[Var[str], str]] = None,
hotkey: Optional[Union[Var[str], str]] = None, hotkey: Optional[Union[Var[str], str]] = None,
invert: Optional[Union[Var[bool], bool]] = None, invert: Optional[Union[Var[bool], bool]] = None,
toast_options: Optional[Union[Var[ToastProps], ToastProps]] = None, toast_options: Optional[Union[ToastProps, Var[ToastProps]]] = None,
gap: Optional[Union[Var[int], int]] = None, gap: Optional[Union[Var[int], int]] = None,
loading_icon: Optional[Union[Var[Icon], Icon]] = None, loading_icon: Optional[Union[Icon, Var[Icon]]] = None,
pause_when_page_is_hidden: Optional[Union[Var[bool], bool]] = None, pause_when_page_is_hidden: Optional[Union[Var[bool], bool]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
@ -76,41 +89,21 @@ class ConnectionToaster(Toaster):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ConnectionToaster": ) -> "ConnectionToaster":
"""Create a connection toaster component. """Create a connection toaster component.
@ -156,41 +149,21 @@ class ConnectionBanner(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ConnectionBanner": ) -> "ConnectionBanner":
"""Create a connection banner component. """Create a connection banner component.
@ -215,41 +188,21 @@ class ConnectionModal(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ConnectionModal": ) -> "ConnectionModal":
"""Create a connection banner component. """Create a connection banner component.
@ -275,41 +228,21 @@ class WifiOffPulse(Icon):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "WifiOffPulse": ) -> "WifiOffPulse":
"""Create a wifi_off icon with an animated opacity pulse. """Create a wifi_off icon with an animated opacity pulse.
@ -338,71 +271,51 @@ class ConnectionPulser(Div):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
access_key: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[ auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
content_editable: Optional[ content_editable: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
context_menu: Optional[ context_menu: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
dir: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[ enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, title: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ConnectionPulser": ) -> "ConnectionPulser":
"""Create a connection pulser component. """Create a connection pulser component.

View File

@ -13,9 +13,9 @@ from __future__ import annotations
from reflex import constants from reflex import constants
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.core.cond import cond from reflex.components.core.cond import cond
from reflex.vars import Var from reflex.vars.base import Var
route_not_found: Var = Var.create_safe(constants.ROUTE_NOT_FOUND, _var_is_string=False) route_not_found: Var = Var(_js_expr=constants.ROUTE_NOT_FOUND)
class ClientSideRouting(Component): class ClientSideRouting(Component):

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
route_not_found: Var route_not_found: Var
@ -26,41 +26,21 @@ class ClientSideRouting(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "ClientSideRouting": ) -> "ClientSideRouting":
"""Create the component. """Create the component.
@ -95,41 +75,21 @@ class Default404Page(Component):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Default404Page": ) -> "Default404Page":
"""Create the component. """Create the component.

View File

@ -2,14 +2,15 @@
from __future__ import annotations from __future__ import annotations
from typing import Dict, List, Union from typing import Dict, List, Tuple, Union
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.components.tags.tag import Tag from reflex.components.tags.tag import Tag
from reflex.event import EventChain, EventHandler from reflex.event import EventChain, EventHandler, identity_event
from reflex.utils.format import format_prop, wrap from reflex.utils.format import format_prop, wrap
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import Var, get_unique_variable_name from reflex.vars import get_unique_variable_name
from reflex.vars.base import Var
class Clipboard(Fragment): class Clipboard(Fragment):
@ -19,7 +20,7 @@ class Clipboard(Fragment):
targets: Var[List[str]] targets: Var[List[str]]
# Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri. # Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri.
on_paste: EventHandler[lambda data: [data]] on_paste: EventHandler[identity_event(List[Tuple[str, str]])]
# Save the original event actions for the on_paste event. # Save the original event actions for the on_paste event.
on_paste_event_actions: Var[Dict[str, Union[bool, int]]] on_paste_event_actions: Var[Dict[str, Union[bool, int]]]
@ -85,8 +86,8 @@ class Clipboard(Fragment):
return [ return [
"usePasteHandler(%s, %s, %s)" "usePasteHandler(%s, %s, %s)"
% ( % (
self.targets._var_name_unwrapped, str(self.targets),
self.on_paste_event_actions._var_name_unwrapped, str(self.on_paste_event_actions),
on_paste, on_paste,
) )
] ]

View File

@ -3,13 +3,13 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, List, Optional, Union, overload from typing import Any, Dict, List, Optional, Union, overload
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import Var from reflex.vars.base import Var
class Clipboard(Fragment): class Clipboard(Fragment):
@overload @overload
@ -17,9 +17,9 @@ class Clipboard(Fragment):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
targets: Optional[Union[Var[List[str]], List[str]]] = None, targets: Optional[Union[List[str], Var[List[str]]]] = None,
on_paste_event_actions: Optional[ on_paste_event_actions: Optional[
Union[Var[Dict[str, Union[bool, int]]], Dict[str, Union[bool, int]]] Union[Dict[str, Union[bool, int]], Var[Dict[str, Union[bool, int]]]]
] = None, ] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
@ -27,42 +27,22 @@ class Clipboard(Fragment):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_paste: Optional[EventType[list[tuple[str, str]]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_scroll: Optional[EventType[[]]] = None,
] = None, on_unmount: Optional[EventType[[]]] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_paste: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Clipboard": ) -> "Clipboard":
"""Create a Clipboard component. """Create a Clipboard component.

View File

@ -8,11 +8,11 @@ from reflex.components.base.fragment import Fragment
from reflex.components.component import BaseComponent, Component, MemoizationLeaf from reflex.components.component import BaseComponent, Component, MemoizationLeaf
from reflex.components.tags import CondTag, Tag from reflex.components.tags import CondTag, Tag
from reflex.constants import Dirs from reflex.constants import Dirs
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.ivars.number import ternary_operation
from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode
from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars import Var, VarData from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
from reflex.vars.number import ternary_operation
_IS_TRUE_IMPORT: ImportDict = { _IS_TRUE_IMPORT: ImportDict = {
f"/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], f"/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")],
@ -94,7 +94,7 @@ class Cond(MemoizationLeaf):
).set( ).set(
props=tag.format_props(), props=tag.format_props(),
), ),
cond_state=f"isTrue({self.cond._var_full_name})", cond_state=f"isTrue({str(self.cond)})",
) )
def add_imports(self) -> ImportDict: def add_imports(self) -> ImportDict:
@ -103,10 +103,11 @@ class Cond(MemoizationLeaf):
Returns: Returns:
The import dict for the component. The import dict for the component.
""" """
cond_imports: dict[str, str | ImportVar | list[str | ImportVar]] = getattr( var_data = VarData.merge(self.cond._get_all_var_data())
VarData.merge(self.cond._get_all_var_data()), "imports", {}
) imports = var_data.old_school_imports() if var_data else {}
return {**cond_imports, **_IS_TRUE_IMPORT}
return {**imports, **_IS_TRUE_IMPORT}
@overload @overload
@ -118,10 +119,10 @@ def cond(condition: Any, c1: Component) -> Component: ...
@overload @overload
def cond(condition: Any, c1: Any, c2: Any) -> ImmutableVar: ... def cond(condition: Any, c1: Any, c2: Any) -> Var: ...
def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar: def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
"""Create a conditional component or Prop. """Create a conditional component or Prop.
Args: Args:
@ -135,17 +136,15 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar:
Raises: Raises:
ValueError: If the arguments are invalid. ValueError: If the arguments are invalid.
""" """
if isinstance(condition, Var) and not isinstance(condition, ImmutableVar):
condition._var_is_local = True
# Convert the condition to a Var. # Convert the condition to a Var.
cond_var = LiteralVar.create(condition) cond_var = LiteralVar.create(condition)
assert cond_var is not None, "The condition must be set." if cond_var is None:
raise ValueError("The condition must be set.")
# If the first component is a component, create a Cond component. # If the first component is a component, create a Cond component.
if isinstance(c1, BaseComponent): if isinstance(c1, BaseComponent):
assert c2 is None or isinstance( if c2 is not None and not isinstance(c2, BaseComponent):
c2, BaseComponent raise ValueError("Both arguments must be components.")
), "Both arguments must be components."
return Cond.create(cond_var, c1, c2) return Cond.create(cond_var, c1, c2)
# Otherwise, create a conditional Var. # Otherwise, create a conditional Var.

View File

@ -6,8 +6,9 @@ from typing import Any, Type, Union
from reflex.components.component import Component from reflex.components.component import Component
from reflex.constants import EventTriggers from reflex.constants import EventTriggers
from reflex.event import EventHandler from reflex.event import EventHandler, empty_event
from reflex.vars import Var, VarData from reflex.vars import VarData
from reflex.vars.base import Var
DEFAULT_DEBOUNCE_TIMEOUT = 300 DEFAULT_DEBOUNCE_TIMEOUT = 300
@ -45,7 +46,7 @@ class DebounceInput(Component):
element: Var[Type[Component]] element: Var[Type[Component]]
# Fired when the input value changes # Fired when the input value changes
on_change: EventHandler[lambda e0: [e0.value]] on_change: EventHandler[empty_event]
@classmethod @classmethod
def create(cls, *children: Component, **props: Any) -> Component: def create(cls, *children: Component, **props: Any) -> Component:
@ -106,23 +107,20 @@ class DebounceInput(Component):
props[field] = getattr(child, field) props[field] = getattr(child, field)
child_ref = child.get_ref() child_ref = child.get_ref()
if props.get("input_ref") is None and child_ref: if props.get("input_ref") is None and child_ref:
props["input_ref"] = Var.create_safe( props["input_ref"] = Var(_js_expr=child_ref, _var_type=str)
child_ref, _var_is_local=False, _var_is_string=False
)
props["id"] = child.id props["id"] = child.id
# Set the child element to wrap, including any imports/hooks from the child. # Set the child element to wrap, including any imports/hooks from the child.
props.setdefault( props.setdefault(
"element", "element",
Var.create_safe( Var(
"{%s}" % (child.alias or child.tag), _js_expr=str(child.alias or child.tag),
_var_is_local=False, _var_type=Type[Component],
_var_is_string=False,
_var_data=VarData( _var_data=VarData(
imports=child._get_imports(), imports=child._get_imports(),
hooks=child._get_hooks_internal(), hooks=child._get_hooks_internal(),
), ),
).to(Type[Component]), ),
) )
component = super().create(**props) component = super().create(**props)

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Type, Union, overload from typing import Any, Dict, Optional, Type, Union, overload
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
DEFAULT_DEBOUNCE_TIMEOUT = 300 DEFAULT_DEBOUNCE_TIMEOUT = 300
@ -22,51 +22,31 @@ class DebounceInput(Component):
debounce_timeout: Optional[Union[Var[int], int]] = None, debounce_timeout: Optional[Union[Var[int], int]] = None,
force_notify_by_enter: Optional[Union[Var[bool], bool]] = None, force_notify_by_enter: Optional[Union[Var[bool], bool]] = None,
force_notify_on_blur: Optional[Union[Var[bool], bool]] = None, force_notify_on_blur: Optional[Union[Var[bool], bool]] = None,
value: Optional[Union[Var[Union[float, int, str]], str, int, float]] = None, value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None,
input_ref: Optional[Union[Var[str], str]] = None, input_ref: Optional[Union[Var[str], str]] = None,
element: Optional[Union[Var[Type[Component]], Type[Component]]] = None, element: Optional[Union[Type[Component], Var[Type[Component]]]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_change: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_change: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "DebounceInput": ) -> "DebounceInput":
"""Create a DebounceInput component. """Create a DebounceInput component.

View File

@ -9,9 +9,8 @@ from reflex.components.base.fragment import Fragment
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.tags import IterTag from reflex.components.tags import IterTag
from reflex.constants import MemoizationMode from reflex.constants import MemoizationMode
from reflex.ivars.base import ImmutableVar
from reflex.state import ComponentState from reflex.state import ComponentState
from reflex.vars import Var from reflex.vars.base import LiteralVar, Var
class ForeachVarError(TypeError): class ForeachVarError(TypeError):
@ -52,10 +51,10 @@ class Foreach(Component):
ForeachVarError: If the iterable is of type Any. ForeachVarError: If the iterable is of type Any.
TypeError: If the render function is a ComponentState. TypeError: If the render function is a ComponentState.
""" """
iterable = ImmutableVar.create_safe(iterable) iterable = LiteralVar.create(iterable)
if iterable._var_type == Any: if iterable._var_type == Any:
raise ForeachVarError( raise ForeachVarError(
f"Could not foreach over var `{iterable._var_full_name}` of type Any. " f"Could not foreach over var `{str(iterable)}` of type Any. "
"(If you are trying to foreach over a state var, add a type annotation to the var). " "(If you are trying to foreach over a state var, add a type annotation to the var). "
"See https://reflex.dev/docs/library/dynamic-rendering/foreach/" "See https://reflex.dev/docs/library/dynamic-rendering/foreach/"
) )
@ -127,7 +126,7 @@ class Foreach(Component):
return dict( return dict(
tag, tag,
iterable_state=tag.iterable._var_full_name, iterable_state=str(tag.iterable),
arg_name=tag.arg_var_name, arg_name=tag.arg_var_name,
arg_index=tag.get_index_var_arg(), arg_index=tag.get_index_var_arg(),
iterable_type=tag.iterable._var_type.mro()[0].__name__, iterable_type=tag.iterable._var_type.mro()[0].__name__,

View File

@ -3,7 +3,7 @@
from typing import Dict from typing import Dict
from reflex.components.el.elements.typography import Div from reflex.components.el.elements.typography import Div
from reflex.vars import Var from reflex.vars.base import Var
class Html(Div): class Html(Div):

View File

@ -3,12 +3,12 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Callable, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from reflex.components.el.elements.typography import Div from reflex.components.el.elements.typography import Div
from reflex.event import EventHandler, EventSpec from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars import Var from reflex.vars.base import Var
class Html(Div): class Html(Div):
@overload @overload
@ -17,73 +17,53 @@ class Html(Div):
cls, cls,
*children, *children,
dangerouslySetInnerHTML: Optional[ dangerouslySetInnerHTML: Optional[
Union[Var[Dict[str, str]], Dict[str, str]] Union[Dict[str, str], Var[Dict[str, str]]]
] = None, ] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[ auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
content_editable: Optional[ content_editable: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
context_menu: Optional[ context_menu: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
dir: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[ enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], str, int, bool] Union[Var[Union[bool, int, str]], bool, int, str]
] = None, ] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None, title: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_blur: Optional[EventType[[]]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[ on_context_menu: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_double_click: Optional[EventType[[]]] = None,
] = None, on_focus: Optional[EventType[[]]] = None,
on_double_click: Optional[ on_mount: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_down: Optional[EventType[[]]] = None,
] = None, on_mouse_enter: Optional[EventType[[]]] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_leave: Optional[EventType[[]]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, on_mouse_move: Optional[EventType[[]]] = None,
on_mouse_down: Optional[ on_mouse_out: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_mouse_over: Optional[EventType[[]]] = None,
] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[ on_scroll: Optional[EventType[[]]] = None,
Union[EventHandler, EventSpec, list, Callable, Var] on_unmount: Optional[EventType[[]]] = None,
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props, **props,
) -> "Html": ) -> "Html":
"""Create a html component. """Create a html component.

View File

@ -6,12 +6,12 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from reflex.components.base import Fragment from reflex.components.base import Fragment
from reflex.components.component import BaseComponent, Component, MemoizationLeaf from reflex.components.component import BaseComponent, Component, MemoizationLeaf
from reflex.components.tags import MatchTag, Tag from reflex.components.tags import MatchTag, Tag
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.style import Style from reflex.style import Style
from reflex.utils import format, types from reflex.utils import format, types
from reflex.utils.exceptions import MatchTypeError from reflex.utils.exceptions import MatchTypeError
from reflex.utils.imports import ImportDict from reflex.utils.imports import ImportDict
from reflex.vars import ImmutableVarData, Var, VarData from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
class Match(MemoizationLeaf): class Match(MemoizationLeaf):
@ -187,7 +187,7 @@ class Match(MemoizationLeaf):
if not types._issubclass(type(case[-1]), return_type): if not types._issubclass(type(case[-1]), return_type):
raise MatchTypeError( raise MatchTypeError(
f"Match cases should have the same return types. Case {index} with return " f"Match cases should have the same return types. Case {index} with return "
f"value `{case[-1]._var_name if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`" f"value `{case[-1]._js_expr if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`"
f" of type {type(case[-1])!r} is not {return_type}" f" of type {type(case[-1])!r} is not {return_type}"
) )
@ -232,14 +232,14 @@ class Match(MemoizationLeaf):
) or not types._isinstance(default, Var): ) or not types._isinstance(default, Var):
raise ValueError("Return types of match cases should be Vars.") raise ValueError("Return types of match cases should be Vars.")
return ImmutableVar( return Var(
_var_name=format.format_match( _js_expr=format.format_match(
cond=match_cond_var._var_name_unwrapped, cond=str(match_cond_var),
match_cases=match_cases, # type: ignore match_cases=match_cases,
default=default, # type: ignore default=default, # type: ignore
), ),
_var_type=default._var_type, # type: ignore _var_type=default._var_type, # type: ignore
_var_data=ImmutableVarData.merge( _var_data=VarData.merge(
match_cond_var._get_all_var_data(), match_cond_var._get_all_var_data(),
*[el._get_all_var_data() for case in match_cases for el in case], *[el._get_all_var_data() for case in match_cases for el in case],
default._get_all_var_data(), # type: ignore default._get_all_var_data(), # type: ignore
@ -267,7 +267,8 @@ class Match(MemoizationLeaf):
Returns: Returns:
The import dict. The import dict.
""" """
return getattr(VarData.merge(self.cond._get_all_var_data()), "imports", {}) var_data = VarData.merge(self.cond._get_all_var_data())
return var_data.old_school_imports() if var_data else {}
match = Match.create match = Match.create

View File

@ -2,13 +2,13 @@
from __future__ import annotations from __future__ import annotations
import os
from pathlib import Path from pathlib import Path
from typing import Callable, ClassVar, Dict, List, Optional from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
from reflex.components.el.elements.forms import Input from reflex.components.el.elements.forms import Input
from reflex.components.radix.themes.layout.box import Box from reflex.components.radix.themes.layout.box import Box
from reflex.config import environment
from reflex.constants import Dirs from reflex.constants import Dirs
from reflex.event import ( from reflex.event import (
CallableEventSpec, CallableEventSpec,
@ -19,10 +19,10 @@ from reflex.event import (
call_script, call_script,
parse_args_spec, parse_args_spec,
) )
from reflex.ivars.base import ImmutableCallableVar, ImmutableVar
from reflex.ivars.sequence import LiteralStringVar
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import ImmutableVarData, Var, VarData from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var
from reflex.vars.sequence import LiteralStringVar
DEFAULT_UPLOAD_ID: str = "default" DEFAULT_UPLOAD_ID: str = "default"
@ -37,8 +37,8 @@ upload_files_context_var_data: VarData = VarData(
) )
@ImmutableCallableVar @CallableVar
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar: def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
"""Get the file upload drop trigger. """Get the file upload drop trigger.
This var is passed to the dropzone component to update the file list when a This var is passed to the dropzone component to update the file list when a
@ -58,17 +58,17 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
}}) }})
""" """
return ImmutableVar( return Var(
_var_name=var_name, _js_expr=var_name,
_var_type=EventChain, _var_type=EventChain,
_var_data=ImmutableVarData.merge( _var_data=VarData.merge(
upload_files_context_var_data, id_var._get_all_var_data() upload_files_context_var_data, id_var._get_all_var_data()
), ),
) )
@ImmutableCallableVar @CallableVar
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar: def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var:
"""Get the list of selected files. """Get the list of selected files.
Args: Args:
@ -78,10 +78,10 @@ def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
A var referencing the list of selected file paths. A var referencing the list of selected file paths.
""" """
id_var = LiteralStringVar.create(id_) id_var = LiteralStringVar.create(id_)
return ImmutableVar( return Var(
_var_name=f"(filesById[{str(id_var)}] ? filesById[{str(id_var)}].map((f) => (f.path || f.name)) : [])", _js_expr=f"(filesById[{str(id_var)}] ? filesById[{str(id_var)}].map((f) => (f.path || f.name)) : [])",
_var_type=List[str], _var_type=List[str],
_var_data=ImmutableVarData.merge( _var_data=VarData.merge(
upload_files_context_var_data, id_var._get_all_var_data() upload_files_context_var_data, id_var._get_all_var_data()
), ),
).guess_type() ).guess_type()
@ -113,7 +113,7 @@ def cancel_upload(upload_id: str) -> EventSpec:
An event spec that cancels the upload when triggered. An event spec that cancels the upload when triggered.
""" """
return call_script( return call_script(
f"upload_controllers[{Var.create_safe(upload_id, _var_is_string=True)._var_name_unwrapped}]?.abort()" f"upload_controllers[{str(LiteralVar.create(upload_id))}]?.abort()"
) )
@ -125,23 +125,20 @@ def get_upload_dir() -> Path:
""" """
Upload.is_used = True Upload.is_used = True
uploaded_files_dir = Path( uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR
os.environ.get("REFLEX_UPLOADED_FILES_DIR", "./uploaded_files")
)
uploaded_files_dir.mkdir(parents=True, exist_ok=True) uploaded_files_dir.mkdir(parents=True, exist_ok=True)
return uploaded_files_dir return uploaded_files_dir
uploaded_files_url_prefix: Var = Var.create_safe( uploaded_files_url_prefix = Var(
"${getBackendURL(env.UPLOAD)}", _js_expr="getBackendURL(env.UPLOAD)",
_var_is_string=False,
_var_data=VarData( _var_data=VarData(
imports={ imports={
f"/{Dirs.STATE_PATH}": "getBackendURL", f"/{Dirs.STATE_PATH}": "getBackendURL",
"/env.json": ImportVar(tag="env", is_default=True), "/env.json": ImportVar(tag="env", is_default=True),
} }
), ),
) ).to(str)
def get_upload_url(file_path: str) -> Var[str]: def get_upload_url(file_path: str) -> Var[str]:
@ -155,12 +152,10 @@ def get_upload_url(file_path: str) -> Var[str]:
""" """
Upload.is_used = True Upload.is_used = True
return Var.create_safe( return uploaded_files_url_prefix + "/" + file_path
f"{uploaded_files_url_prefix}/{file_path}", _var_is_string=True
)
def _on_drop_spec(files: Var): def _on_drop_spec(files: Var) -> Tuple[Var[Any]]:
"""Args spec for the on_drop event trigger. """Args spec for the on_drop event trigger.
Args: Args:
@ -169,7 +164,7 @@ def _on_drop_spec(files: Var):
Returns: Returns:
Signature for on_drop handler including the files to upload. Signature for on_drop handler including the files to upload.
""" """
return [files] return (files,)
class UploadFilesProvider(Component): class UploadFilesProvider(Component):
@ -182,7 +177,7 @@ class UploadFilesProvider(Component):
class Upload(MemoizationLeaf): class Upload(MemoizationLeaf):
"""A file upload component.""" """A file upload component."""
library = "react-dropzone@14.2.3" library = "react-dropzone@14.2.10"
tag = "ReactDropzone" tag = "ReactDropzone"
@ -250,9 +245,7 @@ class Upload(MemoizationLeaf):
} }
# The file input to use. # The file input to use.
upload = Input.create(type="file") upload = Input.create(type="file")
upload.special_props = { upload.special_props = [Var(_js_expr="{...getInputProps()}", _var_type=None)]
ImmutableVar(_var_name="...getInputProps()", _var_type=None)
}
# The dropzone to use. # The dropzone to use.
zone = Box.create( zone = Box.create(
@ -260,9 +253,7 @@ class Upload(MemoizationLeaf):
*children, *children,
**{k: v for k, v in props.items() if k not in supported_props}, **{k: v for k, v in props.items() if k not in supported_props},
) )
zone.special_props = { zone.special_props = [Var(_js_expr="{...getRootProps()}", _var_type=None)]
ImmutableVar(_var_name="...getRootProps()", _var_type=None)
}
# Create the component. # Create the component.
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID) upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
@ -299,7 +290,7 @@ class Upload(MemoizationLeaf):
Returns: Returns:
The updated arg_value tuple when arg is "files", otherwise the original arg_value. The updated arg_value tuple when arg is "files", otherwise the original arg_value.
""" """
if arg_value[0]._var_name == "files": if arg_value[0]._js_expr == "files":
placeholder = parse_args_spec(_on_drop_spec)[0] placeholder = parse_args_spec(_on_drop_spec)[0]
return (arg_value[0], placeholder) return (arg_value[0], placeholder)
return arg_value return arg_value

Some files were not shown because too many files have changed in this diff Show More