Merge remote-tracking branch 'upstream/main' into poc_shared_state
This commit is contained in:
commit
9b256876e2
47
.github/workflows/benchmarks.yml
vendored
47
.github/workflows/benchmarks.yml
vendored
@ -18,7 +18,6 @@ env:
|
||||
PYTHONIOENCODING: 'utf8'
|
||||
TELEMETRY_ENABLED: false
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
|
||||
jobs:
|
||||
@ -62,17 +61,16 @@ jobs:
|
||||
run: |
|
||||
# Check that npm is home
|
||||
npm -v
|
||||
poetry run bash scripts/benchmarks/benchmarks.sh ./reflex-web prod
|
||||
poetry run bash benchmarks/lighthouse.sh ./reflex-web prod
|
||||
env:
|
||||
LHCI_GITHUB_APP_TOKEN: $
|
||||
- name: Run Benchmarks
|
||||
# Only run if the database creds are available in this context.
|
||||
if: ${{ env.DATABASE_URL }}
|
||||
run: poetry run python scripts/benchmarks/lighthouse_score_upload.py "$GITHUB_SHA" ./integration/benchmarks/.lighthouseci
|
||||
run: poetry run python benchmarks/benchmark_lighthouse.py "$GITHUB_SHA" ./integration/benchmarks/.lighthouseci
|
||||
env:
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
|
||||
simple-apps-benchmarks:
|
||||
simple-apps-benchmarks: # This app tests the compile times of various compoonents and pages
|
||||
if: github.event.pull_request.merged == true
|
||||
env:
|
||||
OUTPUT_FILE: benchmarks.json
|
||||
@ -116,8 +114,6 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
- name: Install additional dependencies for DB access
|
||||
run: poetry run uv pip install psycopg2-binary
|
||||
- name: Run benchmark tests
|
||||
env:
|
||||
APP_HARNESS_HEADLESS: 1
|
||||
@ -126,16 +122,14 @@ jobs:
|
||||
poetry run pytest -v benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} -s
|
||||
- name: Upload benchmark results
|
||||
# Only run if the database creds are available in this context.
|
||||
if: ${{ env.DATABASE_URL }}
|
||||
run:
|
||||
poetry run python scripts/benchmarks/simple_app_benchmark_upload.py --os "${{ matrix.os }}"
|
||||
poetry run python benchmarks/benchmark_compile_times.py --os "${{ matrix.os }}"
|
||||
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
|
||||
--benchmark-json "${{ env.OUTPUT_FILE }}"
|
||||
--db-url "${{ env.DATABASE_URL }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--event-type "${{ github.event_name }}" --actor "${{ github.actor }}" --pr-id "${{ github.event.pull_request.id }}"
|
||||
--benchmark-json "${{ env.OUTPUT_FILE }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--event-type "${{ github.event_name }}" --pr-id "${{ github.event.pull_request.id }}"
|
||||
|
||||
reflex-build-size:
|
||||
if: github.event.pull_request.merged == true
|
||||
reflex-dist-size: # This job is used to calculate the size of the Reflex distribution (wheel file)
|
||||
if: github.event.pull_request.merged == true
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
# Prioritize getting more information out of the workflow (even if something fails)
|
||||
@ -148,22 +142,19 @@ jobs:
|
||||
python-version: 3.11.5
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
- name: Install additional dependencies for DB access
|
||||
run: poetry run uv pip install psycopg2-binary
|
||||
- name: Build reflex
|
||||
run: |
|
||||
poetry build
|
||||
- name: Upload benchmark results
|
||||
# Only run if the database creds are available in this context.
|
||||
if: ${{ env.DATABASE_URL }}
|
||||
run:
|
||||
poetry run python scripts/benchmarks/benchmark_reflex_size.py --os ubuntu-latest
|
||||
poetry run python benchmarks/benchmark_package_size.py --os ubuntu-latest
|
||||
--python-version 3.11.5 --commit-sha "${{ github.sha }}" --pr-id "${{ github.event.pull_request.id }}"
|
||||
--db-url "${{ env.DATABASE_URL }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--measurement-type "reflex-build" --path ./dist
|
||||
--branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--path ./dist
|
||||
|
||||
reflex-plus-dependency-size:
|
||||
if: github.event.pull_request.merged == true
|
||||
reflex-venv-size: # This job calculates the total size of Reflex and its dependencies
|
||||
if: github.event.pull_request.merged == true
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
# Prioritize getting more information out of the workflow (even if something fails)
|
||||
@ -197,14 +188,10 @@ jobs:
|
||||
run: |
|
||||
poetry run pip install uv
|
||||
|
||||
- name: Install additional dependencies for DB access
|
||||
run: poetry run uv pip install psycopg2-binary
|
||||
|
||||
- if: ${{ env.DATABASE_URL }}
|
||||
name: calculate and upload size
|
||||
- name: calculate and upload size
|
||||
run:
|
||||
poetry run python scripts/benchmarks/benchmark_reflex_size.py --os "${{ matrix.os }}"
|
||||
poetry run python benchmarks/benchmark_package_size.py --os "${{ matrix.os }}"
|
||||
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}" --db-url "${{ env.DATABASE_URL }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}"
|
||||
--branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--measurement-type "reflex-package" --path ./.venv
|
||||
--path ./.venv
|
25
.github/workflows/integration_tests.yml
vendored
25
.github/workflows/integration_tests.yml
vendored
@ -30,7 +30,6 @@ env:
|
||||
PYTHONIOENCODING: 'utf8'
|
||||
TELEMETRY_ENABLED: false
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
|
||||
jobs:
|
||||
@ -100,27 +99,25 @@ jobs:
|
||||
npm -v
|
||||
poetry run bash scripts/integration.sh ./reflex-examples/counter dev
|
||||
- name: Measure and upload .web size
|
||||
if: ${{ env.DATABASE_URL}}
|
||||
run:
|
||||
poetry run python scripts/benchmarks/benchmark_reflex_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 }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}" --db-url "${{ env.DATABASE_URL }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}"
|
||||
--branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--measurement-type "counter-app-dot-web" --path ./reflex-examples/counter/.web
|
||||
--path ./reflex-examples/counter/.web
|
||||
--app-name "counter"
|
||||
- name: Install hyperfine
|
||||
run: cargo install hyperfine
|
||||
- name: Benchmark imports
|
||||
working-directory: ./reflex-examples/counter
|
||||
run: hyperfine --warmup 3 "export POETRY_VIRTUALENVS_PATH=../../.venv; poetry run python counter/counter.py" --show-output --export-json "${{ env.OUTPUT_FILE }}" --shell bash
|
||||
- name: Upload Benchmarks
|
||||
if : ${{ env.DATABASE_URL }}
|
||||
run:
|
||||
poetry run python scripts/benchmarks/benchmark_imports.py --os "${{ matrix.os }}"
|
||||
poetry run python benchmarks/benchmark_imports.py --os "${{ matrix.os }}"
|
||||
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
|
||||
--benchmark-json "./reflex-examples/counter/${{ env.OUTPUT_FILE }}"
|
||||
--db-url "${{ env.DATABASE_URL }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--event-type "${{ github.event_name }}" --actor "${{ github.actor }}" --pr-id "${{ github.event.pull_request.id }}"
|
||||
|
||||
--branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}"
|
||||
--app-name "counter"
|
||||
|
||||
|
||||
|
||||
@ -164,10 +161,8 @@ jobs:
|
||||
npm -v
|
||||
poetry run bash scripts/integration.sh ./reflex-web prod
|
||||
- name: Measure and upload .web size
|
||||
if: ${{ env.DATABASE_URL}}
|
||||
run:
|
||||
poetry run python scripts/benchmarks/benchmark_reflex_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 }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}"
|
||||
--db-url "${{ env.DATABASE_URL }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--measurement-type "reflex-web-dot-web" --path ./reflex-web/.web
|
||||
--pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--app-name "reflex-web" --path ./reflex-web/.web
|
@ -18,7 +18,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
"""Runs the benchmarks and inserts the results into the database."""
|
||||
"""Extracts the compile times from the JSON files in the specified directory and inserts them into the database."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import psycopg2
|
||||
from utils import send_data_to_posthog
|
||||
|
||||
|
||||
def extract_stats_from_json(json_file: str) -> list[dict]:
|
||||
@ -51,7 +50,6 @@ def extract_stats_from_json(json_file: str) -> list[dict]:
|
||||
|
||||
|
||||
def insert_benchmarking_data(
|
||||
db_connection_url: str,
|
||||
os_type_version: str,
|
||||
python_version: str,
|
||||
performance_data: list[dict],
|
||||
@ -59,52 +57,33 @@ def insert_benchmarking_data(
|
||||
pr_title: str,
|
||||
branch_name: str,
|
||||
event_type: str,
|
||||
actor: str,
|
||||
pr_id: str,
|
||||
):
|
||||
"""Insert the benchmarking data into the database.
|
||||
|
||||
Args:
|
||||
db_connection_url: The URL to connect to the database.
|
||||
os_type_version: The OS type and version to insert.
|
||||
python_version: The Python version to insert.
|
||||
performance_data: The performance data of reflex web to insert.
|
||||
commit_sha: The commit SHA to insert.
|
||||
pr_title: The PR title to insert.
|
||||
branch_name: The name of the branch.
|
||||
event_type: Type of github event(push, pull request, etc)
|
||||
actor: Username of the user that triggered the run.
|
||||
event_type: Type of github event(push, pull request, etc).
|
||||
pr_id: Id of the PR.
|
||||
"""
|
||||
# Serialize the JSON data
|
||||
simple_app_performance_json = json.dumps(performance_data)
|
||||
# Prepare the event data
|
||||
properties = {
|
||||
"os": os_type_version,
|
||||
"python_version": python_version,
|
||||
"distinct_id": commit_sha,
|
||||
"pr_title": pr_title,
|
||||
"branch_name": branch_name,
|
||||
"event_type": event_type,
|
||||
"performance": performance_data,
|
||||
"pr_id": pr_id,
|
||||
}
|
||||
|
||||
# Get the current timestamp
|
||||
current_timestamp = datetime.now()
|
||||
|
||||
# Connect to the database and insert the data
|
||||
with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor:
|
||||
insert_query = """
|
||||
INSERT INTO simple_app_benchmarks (os, python_version, commit_sha, time, pr_title, branch_name, event_type, actor, performance, pr_id)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
||||
"""
|
||||
cursor.execute(
|
||||
insert_query,
|
||||
(
|
||||
os_type_version,
|
||||
python_version,
|
||||
commit_sha,
|
||||
current_timestamp,
|
||||
pr_title,
|
||||
branch_name,
|
||||
event_type,
|
||||
actor,
|
||||
simple_app_performance_json,
|
||||
pr_id,
|
||||
),
|
||||
)
|
||||
# Commit the transaction
|
||||
conn.commit()
|
||||
send_data_to_posthog("simple_app_benchmark", properties)
|
||||
|
||||
|
||||
def main():
|
||||
@ -124,11 +103,6 @@ def main():
|
||||
"--benchmark-json",
|
||||
help="The JSON file containing the benchmark results.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--db-url",
|
||||
help="The URL to connect to the database.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pr-title",
|
||||
help="The PR title to insert into the database.",
|
||||
@ -143,11 +117,6 @@ def main():
|
||||
help="The github event type",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--actor",
|
||||
help="Username of the user that triggered the run.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pr-id",
|
||||
help="ID of the PR.",
|
||||
@ -162,7 +131,6 @@ def main():
|
||||
cleaned_benchmark_results = extract_stats_from_json(args.benchmark_json)
|
||||
# Insert the data into the database
|
||||
insert_benchmarking_data(
|
||||
db_connection_url=args.db_url,
|
||||
os_type_version=args.os,
|
||||
python_version=args.python_version,
|
||||
performance_data=cleaned_benchmark_results,
|
||||
@ -170,7 +138,6 @@ def main():
|
||||
pr_title=pr_title,
|
||||
branch_name=args.branch_name,
|
||||
event_type=args.event_type,
|
||||
actor=args.actor,
|
||||
pr_id=args.pr_id,
|
||||
)
|
||||
|
@ -1,13 +1,12 @@
|
||||
"""Runs the benchmarks and inserts the results into the database."""
|
||||
"""Extract and upload benchmarking data to PostHog."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import psycopg2
|
||||
from utils import send_data_to_posthog
|
||||
|
||||
|
||||
def extract_stats_from_json(json_file: str) -> dict:
|
||||
@ -34,59 +33,39 @@ def extract_stats_from_json(json_file: str) -> dict:
|
||||
|
||||
|
||||
def insert_benchmarking_data(
|
||||
db_connection_url: str,
|
||||
os_type_version: str,
|
||||
python_version: str,
|
||||
performance_data: dict,
|
||||
commit_sha: str,
|
||||
pr_title: str,
|
||||
branch_name: str,
|
||||
event_type: str,
|
||||
actor: str,
|
||||
pr_id: str,
|
||||
app_name: str,
|
||||
):
|
||||
"""Insert the benchmarking data into the database.
|
||||
|
||||
Args:
|
||||
db_connection_url: The URL to connect to the database.
|
||||
os_type_version: The OS type and version to insert.
|
||||
python_version: The Python version to insert.
|
||||
performance_data: The imports performance data to insert.
|
||||
commit_sha: The commit SHA to insert.
|
||||
pr_title: The PR title to insert.
|
||||
branch_name: The name of the branch.
|
||||
event_type: Type of github event(push, pull request, etc)
|
||||
actor: Username of the user that triggered the run.
|
||||
pr_id: Id of the PR.
|
||||
app_name: The name of the app being measured.
|
||||
"""
|
||||
# Serialize the JSON data
|
||||
simple_app_performance_json = json.dumps(performance_data)
|
||||
# Get the current timestamp
|
||||
current_timestamp = datetime.now()
|
||||
properties = {
|
||||
"os": os_type_version,
|
||||
"python_version": python_version,
|
||||
"distinct_id": commit_sha,
|
||||
"pr_title": pr_title,
|
||||
"branch_name": branch_name,
|
||||
"pr_id": pr_id,
|
||||
"performance": performance_data,
|
||||
"app_name": app_name,
|
||||
}
|
||||
|
||||
# Connect to the database and insert the data
|
||||
with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor:
|
||||
insert_query = """
|
||||
INSERT INTO import_benchmarks (os, python_version, commit_sha, time, pr_title, branch_name, event_type, actor, performance, pr_id)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
||||
"""
|
||||
cursor.execute(
|
||||
insert_query,
|
||||
(
|
||||
os_type_version,
|
||||
python_version,
|
||||
commit_sha,
|
||||
current_timestamp,
|
||||
pr_title,
|
||||
branch_name,
|
||||
event_type,
|
||||
actor,
|
||||
simple_app_performance_json,
|
||||
pr_id,
|
||||
),
|
||||
)
|
||||
# Commit the transaction
|
||||
conn.commit()
|
||||
send_data_to_posthog("import_benchmark", properties)
|
||||
|
||||
|
||||
def main():
|
||||
@ -106,11 +85,6 @@ def main():
|
||||
"--benchmark-json",
|
||||
help="The JSON file containing the benchmark results.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--db-url",
|
||||
help="The URL to connect to the database.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pr-title",
|
||||
help="The PR title to insert into the database.",
|
||||
@ -121,13 +95,8 @@ def main():
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--event-type",
|
||||
help="The github event type",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--actor",
|
||||
help="Username of the user that triggered the run.",
|
||||
"--app-name",
|
||||
help="The name of the app measured.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
@ -143,15 +112,13 @@ def main():
|
||||
cleaned_benchmark_results = extract_stats_from_json(args.benchmark_json)
|
||||
# Insert the data into the database
|
||||
insert_benchmarking_data(
|
||||
db_connection_url=args.db_url,
|
||||
os_type_version=args.os,
|
||||
python_version=args.python_version,
|
||||
performance_data=cleaned_benchmark_results,
|
||||
commit_sha=args.commit_sha,
|
||||
pr_title=pr_title,
|
||||
branch_name=args.branch_name,
|
||||
event_type=args.event_type,
|
||||
actor=args.actor,
|
||||
app_name=args.app_name,
|
||||
pr_id=args.pr_id,
|
||||
)
|
||||
|
@ -1,52 +1,31 @@
|
||||
"""Runs the benchmarks and inserts the results into the database."""
|
||||
"""Extracts the Lighthouse scores from the JSON files in the specified directory and inserts them into the database."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import psycopg2
|
||||
from utils import send_data_to_posthog
|
||||
|
||||
|
||||
def insert_benchmarking_data(
|
||||
db_connection_url: str,
|
||||
lighthouse_data: dict,
|
||||
commit_sha: str,
|
||||
pr_title: str,
|
||||
):
|
||||
"""Insert the benchmarking data into the database.
|
||||
|
||||
Args:
|
||||
db_connection_url: The URL to connect to the database.
|
||||
lighthouse_data: The Lighthouse data to insert.
|
||||
commit_sha: The commit SHA to insert.
|
||||
pr_title: The PR title to insert.
|
||||
"""
|
||||
# Serialize the JSON data
|
||||
lighthouse_json = json.dumps(lighthouse_data)
|
||||
properties = {
|
||||
"distinct_id": commit_sha,
|
||||
"lighthouse_data": lighthouse_data,
|
||||
}
|
||||
|
||||
# Get the current timestamp
|
||||
current_timestamp = datetime.now()
|
||||
|
||||
# Connect to the database and insert the data
|
||||
with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor:
|
||||
insert_query = """
|
||||
INSERT INTO benchmarks (lighthouse, commit_sha, pr_title, time)
|
||||
VALUES (%s, %s, %s, %s);
|
||||
"""
|
||||
cursor.execute(
|
||||
insert_query,
|
||||
(
|
||||
lighthouse_json,
|
||||
commit_sha,
|
||||
pr_title,
|
||||
current_timestamp,
|
||||
),
|
||||
)
|
||||
# Commit the transaction
|
||||
conn.commit()
|
||||
# Send the data to PostHog
|
||||
send_data_to_posthog("lighthouse_benchmark", properties)
|
||||
|
||||
|
||||
def get_lighthouse_scores(directory_path: str) -> dict:
|
||||
@ -67,7 +46,7 @@ def get_lighthouse_scores(directory_path: str) -> dict:
|
||||
with open(file_path, "r") as file:
|
||||
data = json.load(file)
|
||||
# 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"],
|
||||
"accessibility_score": data["categories"]["accessibility"][
|
||||
"score"
|
||||
@ -76,11 +55,9 @@ def get_lighthouse_scores(directory_path: str) -> dict:
|
||||
"score"
|
||||
],
|
||||
"seo_score": data["categories"]["seo"]["score"],
|
||||
"pwa_score": data["categories"]["pwa"]["score"],
|
||||
}
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"error": "Error parsing JSON files"}
|
||||
return {"error": e}
|
||||
|
||||
return scores
|
||||
|
||||
@ -91,18 +68,11 @@ def main():
|
||||
commit_sha = sys.argv[1]
|
||||
json_dir = sys.argv[2]
|
||||
|
||||
# Get the PR title and database URL from the environment variables
|
||||
pr_title = os.environ.get("PR_TITLE")
|
||||
db_url = os.environ.get("DATABASE_URL")
|
||||
|
||||
if db_url is None or pr_title is None:
|
||||
sys.exit("Missing environment variables")
|
||||
|
||||
# Get the Lighthouse scores
|
||||
lighthouse_scores = get_lighthouse_scores(json_dir)
|
||||
|
||||
# Insert the data into the database
|
||||
insert_benchmarking_data(db_url, lighthouse_scores, commit_sha, pr_title)
|
||||
insert_benchmarking_data(lighthouse_scores, commit_sha)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
@ -1,53 +1,9 @@
|
||||
"""Checks the size of a specific directory and uploads result."""
|
||||
"""Checks the size of a specific directory and uploads result to Posthog."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
import psycopg2
|
||||
|
||||
|
||||
def get_directory_size(directory):
|
||||
"""Get the size of a directory in bytes.
|
||||
|
||||
Args:
|
||||
directory: The directory to check.
|
||||
|
||||
Returns:
|
||||
The size of the dir in bytes.
|
||||
"""
|
||||
total_size = 0
|
||||
for dirpath, _, filenames in os.walk(directory):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
total_size += os.path.getsize(fp)
|
||||
return total_size
|
||||
|
||||
|
||||
def get_python_version(venv_path, os_name):
|
||||
"""Get the python version of python in a virtual env.
|
||||
|
||||
Args:
|
||||
venv_path: Path to virtual environment.
|
||||
os_name: Name of os.
|
||||
|
||||
Returns:
|
||||
The python version.
|
||||
"""
|
||||
python_executable = (
|
||||
os.path.join(venv_path, "bin", "python")
|
||||
if "windows" not in os_name
|
||||
else os.path.join(venv_path, "Scripts", "python.exe")
|
||||
)
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[python_executable, "--version"], stderr=subprocess.STDOUT
|
||||
)
|
||||
python_version = output.decode("utf-8").strip().split()[1]
|
||||
return ".".join(python_version.split(".")[:-1])
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
from utils import get_directory_size, get_python_version, send_data_to_posthog
|
||||
|
||||
|
||||
def get_package_size(venv_path, os_name):
|
||||
@ -64,6 +20,7 @@ def get_package_size(venv_path, os_name):
|
||||
ValueError: when venv does not exist or python version is None.
|
||||
"""
|
||||
python_version = get_python_version(venv_path, os_name)
|
||||
print("Python version:", python_version)
|
||||
if python_version is None:
|
||||
raise ValueError("Error: Failed to determine Python version.")
|
||||
|
||||
@ -86,61 +43,45 @@ def get_package_size(venv_path, os_name):
|
||||
|
||||
|
||||
def insert_benchmarking_data(
|
||||
db_connection_url: str,
|
||||
os_type_version: str,
|
||||
python_version: str,
|
||||
measurement_type: str,
|
||||
commit_sha: str,
|
||||
pr_title: str,
|
||||
branch_name: str,
|
||||
pr_id: str,
|
||||
path: str,
|
||||
):
|
||||
"""Insert the benchmarking data into the database.
|
||||
"""Insert the benchmarking data into PostHog.
|
||||
|
||||
Args:
|
||||
db_connection_url: The URL to connect to the database.
|
||||
os_type_version: The OS type and version to insert.
|
||||
python_version: The Python version to insert.
|
||||
measurement_type: The type of metric to measure.
|
||||
commit_sha: The commit SHA to insert.
|
||||
pr_title: The PR title to insert.
|
||||
branch_name: The name of the branch.
|
||||
pr_id: The id of the PR.
|
||||
path: The path to the dir or file to check size.
|
||||
"""
|
||||
if measurement_type == "reflex-package":
|
||||
size = get_package_size(path, os_type_version)
|
||||
else:
|
||||
if "./dist" in path:
|
||||
size = get_directory_size(path)
|
||||
else:
|
||||
size = get_package_size(path, os_type_version)
|
||||
|
||||
# Get the current timestamp
|
||||
current_timestamp = datetime.now()
|
||||
# Prepare the event data
|
||||
properties = {
|
||||
"path": path,
|
||||
"os": os_type_version,
|
||||
"python_version": python_version,
|
||||
"distinct_id": commit_sha,
|
||||
"pr_title": pr_title,
|
||||
"branch_name": branch_name,
|
||||
"pr_id": pr_id,
|
||||
"size_mb": round(
|
||||
size / (1024 * 1024), 3
|
||||
), # save size in MB and round to 3 places
|
||||
}
|
||||
|
||||
# Connect to the database and insert the data
|
||||
with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor:
|
||||
insert_query = """
|
||||
INSERT INTO size_benchmarks (os, python_version, commit_sha, created_at, pr_title, branch_name, pr_id, measurement_type, size)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);
|
||||
"""
|
||||
cursor.execute(
|
||||
insert_query,
|
||||
(
|
||||
os_type_version,
|
||||
python_version,
|
||||
commit_sha,
|
||||
current_timestamp,
|
||||
pr_title,
|
||||
branch_name,
|
||||
pr_id,
|
||||
measurement_type,
|
||||
round(
|
||||
size / (1024 * 1024), 3
|
||||
), # save size in mb and round to 3 places.
|
||||
),
|
||||
)
|
||||
# Commit the transaction
|
||||
conn.commit()
|
||||
send_data_to_posthog("package_size", properties)
|
||||
|
||||
|
||||
def main():
|
||||
@ -155,11 +96,6 @@ def main():
|
||||
parser.add_argument(
|
||||
"--commit-sha", help="The commit SHA to insert into the database."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--db-url",
|
||||
help="The URL to connect to the database.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pr-title",
|
||||
help="The PR title to insert into the database.",
|
||||
@ -174,14 +110,9 @@ def main():
|
||||
help="The pr id",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--measurement-type",
|
||||
help="The type of metric to be checked.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--path",
|
||||
help="the current path to check size.",
|
||||
help="The path to the vnenv.",
|
||||
required=True,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
@ -191,10 +122,8 @@ def main():
|
||||
|
||||
# Insert the data into the database
|
||||
insert_benchmarking_data(
|
||||
db_connection_url=args.db_url,
|
||||
os_type_version=args.os,
|
||||
python_version=args.python_version,
|
||||
measurement_type=args.measurement_type,
|
||||
commit_sha=args.commit_sha,
|
||||
pr_title=pr_title,
|
||||
branch_name=args.branch_name,
|
105
benchmarks/benchmark_web_size.py
Normal file
105
benchmarks/benchmark_web_size.py
Normal file
@ -0,0 +1,105 @@
|
||||
"""Checks the size of a specific directory and uploads result to Posthog."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from utils import get_directory_size, send_data_to_posthog
|
||||
|
||||
|
||||
def insert_benchmarking_data(
|
||||
os_type_version: str,
|
||||
python_version: str,
|
||||
app_name: str,
|
||||
commit_sha: str,
|
||||
pr_title: str,
|
||||
branch_name: str,
|
||||
pr_id: str,
|
||||
path: str,
|
||||
):
|
||||
"""Insert the benchmarking data into PostHog.
|
||||
|
||||
Args:
|
||||
app_name: The name of the app being measured.
|
||||
os_type_version: The OS type and version to insert.
|
||||
python_version: The Python version to insert.
|
||||
commit_sha: The commit SHA to insert.
|
||||
pr_title: The PR title to insert.
|
||||
branch_name: The name of the branch.
|
||||
pr_id: The id of the PR.
|
||||
path: The path to the dir or file to check size.
|
||||
"""
|
||||
size = get_directory_size(path)
|
||||
|
||||
# Prepare the event data
|
||||
properties = {
|
||||
"app_name": app_name,
|
||||
"os": os_type_version,
|
||||
"python_version": python_version,
|
||||
"distinct_id": commit_sha,
|
||||
"pr_title": pr_title,
|
||||
"branch_name": branch_name,
|
||||
"pr_id": pr_id,
|
||||
"size_mb": round(
|
||||
size / (1024 * 1024), 3
|
||||
), # save size in MB and round to 3 places
|
||||
}
|
||||
|
||||
send_data_to_posthog("web-size", properties)
|
||||
|
||||
|
||||
def main():
|
||||
"""Runs the benchmarks and inserts the results."""
|
||||
parser = argparse.ArgumentParser(description="Run benchmarks and process results.")
|
||||
parser.add_argument(
|
||||
"--os", help="The OS type and version to insert into the database."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--python-version", help="The Python version to insert into the database."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--commit-sha", help="The commit SHA to insert into the database."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pr-title",
|
||||
help="The PR title to insert into the database.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--branch-name",
|
||||
help="The current branch",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--app-name",
|
||||
help="The name of the app measured.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pr-id",
|
||||
help="The pr id",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--path",
|
||||
help="The current path to app to check.",
|
||||
required=True,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get the PR title from env or the args. For the PR merge or push event, there is no PR title, leaving it empty.
|
||||
pr_title = args.pr_title or os.getenv("PR_TITLE", "")
|
||||
|
||||
# Insert the data into the database
|
||||
insert_benchmarking_data(
|
||||
app_name=args.app_name,
|
||||
os_type_version=args.os,
|
||||
python_version=args.python_version,
|
||||
commit_sha=args.commit_sha,
|
||||
pr_title=pr_title,
|
||||
branch_name=args.branch_name,
|
||||
pr_id=args.pr_id,
|
||||
path=args.path,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
73
benchmarks/utils.py
Normal file
73
benchmarks/utils.py
Normal file
@ -0,0 +1,73 @@
|
||||
"""Utility functions for the benchmarks."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import httpx
|
||||
from httpx import HTTPError
|
||||
|
||||
|
||||
def get_python_version(venv_path, os_name):
|
||||
"""Get the python version of python in a virtual env.
|
||||
|
||||
Args:
|
||||
venv_path: Path to virtual environment.
|
||||
os_name: Name of os.
|
||||
|
||||
Returns:
|
||||
The python version.
|
||||
"""
|
||||
python_executable = (
|
||||
os.path.join(venv_path, "bin", "python")
|
||||
if "windows" not in os_name
|
||||
else os.path.join(venv_path, "Scripts", "python.exe")
|
||||
)
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[python_executable, "--version"], stderr=subprocess.STDOUT
|
||||
)
|
||||
python_version = output.decode("utf-8").strip().split()[1]
|
||||
return ".".join(python_version.split(".")[:-1])
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
|
||||
def get_directory_size(directory):
|
||||
"""Get the size of a directory in bytes.
|
||||
|
||||
Args:
|
||||
directory: The directory to check.
|
||||
|
||||
Returns:
|
||||
The size of the dir in bytes.
|
||||
"""
|
||||
total_size = 0
|
||||
for dirpath, _, filenames in os.walk(directory):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
total_size += os.path.getsize(fp)
|
||||
return total_size
|
||||
|
||||
|
||||
def send_data_to_posthog(event, properties):
|
||||
"""Send data to PostHog.
|
||||
|
||||
Args:
|
||||
event: The event to send.
|
||||
properties: The properties to send.
|
||||
|
||||
Raises:
|
||||
HTTPError: When there is an error sending data to PostHog.
|
||||
"""
|
||||
event_data = {
|
||||
"api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb",
|
||||
"event": event,
|
||||
"properties": properties,
|
||||
}
|
||||
|
||||
with httpx.Client() as client:
|
||||
response = client.post("https://app.posthog.com/capture/", json=event_data)
|
||||
if response.status_code != 200:
|
||||
raise HTTPError(
|
||||
f"Error sending data to PostHog: {response.status_code} - {response.text}"
|
||||
)
|
@ -18,7 +18,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -18,7 +18,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -20,7 +20,7 @@ Pynecone की तलाश हैं? आप सही रेपो में
|
||||
|
||||
---
|
||||
|
||||
## [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) | [한국어](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)
|
||||
## [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) | [한국어](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)
|
||||
|
||||
# Reflex
|
||||
|
||||
|
@ -18,7 +18,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) | [한국어](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)
|
||||
[Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/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)
|
||||
---
|
||||
|
||||
## ⚙️ Installazione
|
||||
|
@ -20,7 +20,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
</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) | [한국어](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)
|
||||
[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) | [한국어](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)
|
||||
---
|
||||
## ⚙️ 설치
|
||||
|
||||
|
263
docs/pe/README.md
Normal file
263
docs/pe/README.md
Normal file
@ -0,0 +1,263 @@
|
||||
```diff
|
||||
+ دنبال Pynecone میگردی؟ شما در مخزن درستی قرار داری. Pynecone به 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>
|
||||
|
||||
### **✨ برنامه های تحت وب قابل تنظیم، کارآمد تماما پایتونی که در چند ثانیه مستقر(دپلوی) میشود. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](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)
|
||||
|
||||
---
|
||||
|
||||
# Reflex - رفلکس
|
||||
|
||||
رفلکس(Reflex) یک کتابخانه برای ساخت برنامه های وب فول استک تماما پایتونی است.
|
||||
|
||||
ویژگی های کلیدی:
|
||||
* **تماما پایتونی** - فرانت اند و بک اند برنامه خود را همه در پایتون بنویسید، بدون نیاز به یادگیری جاوا اسکریپت.
|
||||
* **انعطاف پذیری کامل** - شروع به کار با Reflex آسان است، اما می تواند به برنامه های پیچیده نیز تبدیل شود.
|
||||
* **دپلوی فوری** - پس از ساخت، برنامه خود را با [یک دستور](https://reflex.dev/docs/hosting/deploy-quick-start/) دپلوی کنید یا آن را روی سرور خود میزبانی کنید.
|
||||
|
||||
برای آشنایی با نحوه عملکرد Reflex [صفحه معماری](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) را ببینید.
|
||||
|
||||
## ⚙️ Installation - نصب و راه اندازی
|
||||
|
||||
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.8+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
```
|
||||
|
||||
## 🥳 اولین برنامه خود را ایجاد کنید
|
||||
|
||||
نصب `reflex` همچنین `reflex` در خط فرمان را نصب میکند.
|
||||
|
||||
با ایجاد یک پروژه جدید موفقیت آمیز بودن نصب را تست کنید. (`my_app_name` را با اسم پروژه خودتان جایگزین کنید):
|
||||
|
||||
```bash
|
||||
mkdir my_app_name
|
||||
cd my_app_name
|
||||
reflex init
|
||||
```
|
||||
|
||||
این دستور یک برنامه الگو(تمپلیت) را در فهرست(دایرکتوری) جدید شما مقداردهی اولیه می کند
|
||||
|
||||
می توانید این برنامه را در حالت توسعه(development) اجرا کنید:
|
||||
|
||||
```bash
|
||||
reflex run
|
||||
```
|
||||
|
||||
باید برنامه خود را در حال اجرا ببینید در http://localhost:3000.
|
||||
|
||||
اکنون میتوانید کد منبع را در «my_app_name/my_app_name.py» تغییر دهید. Reflex دارای تازهسازیهای سریعی است، بنابراین میتوانید تغییرات خود را بلافاصله پس از ذخیره کد خود مشاهده کنید.
|
||||
|
||||
|
||||
## 🫧 Example App - برنامه نمونه
|
||||
|
||||
بیایید یک مثال بزنیم: ایجاد یک رابط کاربری برای تولید تصویر [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). برای سادگی، ما فراخوانی میکنیم [OpenAI API](https://platform.openai.com/docs/api-reference/authentication), اما می توانید آن را با یک مدل ML که به صورت محلی اجرا می شود جایگزین کنید.
|
||||
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
در اینجا کد کامل برای ایجاد این پروژه آمده است. همه اینها در یک فایل پایتون انجام می شود!
|
||||
|
||||
|
||||
|
||||
```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")
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## بیاید سادش کنیم
|
||||
|
||||
<div align="center">
|
||||
<img src="docs/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 - رابط کاربری رفلکس**
|
||||
|
||||
بیایید با رابط کاربری شروع کنیم.
|
||||
|
||||
```python
|
||||
def index():
|
||||
return rx.center(
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
تابع `index` قسمت فرانت اند برنامه را تعریف می کند.
|
||||
|
||||
ما از اجزای مختلفی مثل `center`, `vstack`, `input` و `button` استفاده میکنیم تا فرانت اند را بسازیم. اجزاء را می توان درون یکدیگر قرار داد
|
||||
برای ایجاد طرح بندی های پیچیده می توانید از args کلمات کلیدی برای استایل دادن به آنها از CSS استفاده کنید.
|
||||
|
||||
رفلکس دارای [بیش از 60 جزء](https://reflex.dev/docs/library) برای کمک به شما برای شروع. ما به طور فعال اجزای بیشتری را اضافه می کنیم, و این خیلی آسان است که [اجزا خود را بسازید](https://reflex.dev/docs/wrapping-react/overview/).
|
||||
|
||||
### **State - حالت**
|
||||
|
||||
رفلکس رابط کاربری شما را به عنوان تابعی از وضعیت شما نشان می دهد.
|
||||
|
||||
```python
|
||||
class State(rx.State):
|
||||
"""The app state."""
|
||||
prompt = ""
|
||||
image_url = ""
|
||||
processing = False
|
||||
complete = False
|
||||
|
||||
```
|
||||
|
||||
حالت تمام متغیرها(variables) (به نام vars) را در یک برنامه که می توانند تغییر دهند و توابعی که آنها را تغییر می دهند تعریف می کند..
|
||||
|
||||
در اینجا حالت از یک `prompt` و `image_url` تشکیل شده است. همچنین دو بولی `processing` و `complete` برای نشان دادن زمان غیرفعال کردن دکمه (در طول تولید تصویر) و زمان نمایش تصویر نتیجه وجود دارد.
|
||||
|
||||
### **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
|
||||
```
|
||||
|
||||
در داخل حالت، توابعی به نام کنترل کننده رویداد تعریف می کنیم که متغیرهای حالت را تغییر می دهند. کنترل کننده های رویداد راهی هستند که می توانیم وضعیت را در Reflex تغییر دهیم. آنها را می توان در پاسخ به اقدامات کاربر، مانند کلیک کردن روی یک دکمه یا تایپ کردن در یک متن، فراخوانی کرد. به این اعمال وقایع می گویند.
|
||||
|
||||
برنامه DALL·E ما دارای یک کنترل کننده رویداد، `get_image` است که این تصویر را از OpenAI API دریافت می کند. استفاده از `yield` در وسط کنترلکننده رویداد باعث بهروزرسانی رابط کاربری میشود. در غیر این صورت رابط کاربری در پایان کنترل کننده رویداد به روز می شود.
|
||||
|
||||
### **Routing - مسیریابی**
|
||||
|
||||
بالاخره اپلیکیشن خود را تعریف می کنیم.
|
||||
|
||||
```python
|
||||
app = rx.App()
|
||||
```
|
||||
|
||||
ما یک صفحه از root برنامه را به جزء index اضافه می کنیم. ما همچنین عنوانی را اضافه می کنیم که در برگه پیش نمایش/مرورگر صفحه نمایش داده می شود.
|
||||
|
||||
```python
|
||||
app.add_page(index, title="DALL-E")
|
||||
```
|
||||
|
||||
با افزودن صفحات بیشتر می توانید یک برنامه چند صفحه ای ایجاد کنید.
|
||||
|
||||
## 📑 Resources - منابع
|
||||
|
||||
<div align="center">
|
||||
|
||||
📑 [اسناد](https://reflex.dev/docs/getting-started/introduction) | 🗞️ [وبلاگ](https://reflex.dev/blog) | 📱 [کتابخانه جزء](https://reflex.dev/docs/library) | 🖼️ [گالری](https://reflex.dev/docs/gallery) | 🛸 [استقرار](https://reflex.dev/docs/hosting/deploy-quick-start)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## ✅ Status - وضعیت
|
||||
|
||||
رفلکس(reflex) در دسامبر 2022 با نام Pynecone راه اندازی شد.
|
||||
|
||||
از فوریه 2024، سرویس میزبانی ما در آلفا است! در این مدت هر کسی میتواند برنامههای خود را به صورت رایگان اجرا کند. [نقشه راه](https://github.com/reflex-dev/reflex/issues/2727) را ببینید تا متوجه شوید چه برنامهریزی شده است.
|
||||
|
||||
رفلکس(reflex) هر هفته نسخه ها و ویژگی های جدیدی دارد! مطمئن شوید که :star: ستاره و :eyes: این مخزن را تماشا کنید تا به روز بمانید.
|
||||
|
||||
## Contributing - مشارکت کردن
|
||||
|
||||
ما از مشارکت در هر اندازه استقبال می کنیم! در زیر چند راه خوب برای شروع در انجمن رفلکس آورده شده است.
|
||||
|
||||
- **به Discord ما بپیوندید**: [Discord](https://discord.gg/T5WSbC2YtQ) ما بهترین مکان برای دریافت کمک در مورد پروژه Reflex و بحث در مورد اینکه چگونه می توانید کمک کنید است.
|
||||
- **بحث های GitHub**: راهی عالی برای صحبت در مورد ویژگی هایی که می خواهید اضافه کنید یا چیزهایی که گیج کننده هستند/نیاز به توضیح دارند.
|
||||
- **قسمت مشکلات GitHub**: [قسمت مشکلات](https://github.com/reflex-dev/reflex/issues) یک راه عالی برای گزارش اشکال هستند. علاوه بر این، می توانید یک مشکل موجود را حل کنید و یک PR(pull request) ارسال کنید.
|
||||
|
||||
ما فعالانه به دنبال مشارکت کنندگان هستیم، فارغ از سطح مهارت یا تجربه شما. برای مشارکت [CONTIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) را بررسی کنید.
|
||||
|
||||
|
||||
## All Thanks To Our Contributors - با تشکر از همکاران ما:
|
||||
<a href="https://github.com/reflex-dev/reflex/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=reflex-dev/reflex" />
|
||||
</a>
|
||||
|
||||
## License - مجوز
|
||||
|
||||
رفلکس متن باز و تحت مجوز [Apache License 2.0](LICENSE) است.
|
@ -17,7 +17,7 @@
|
||||
</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) | [한국어](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)
|
||||
[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) | [한국어](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)
|
||||
---
|
||||
## ⚙️ Instalação
|
||||
|
||||
|
@ -19,7 +19,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -18,7 +18,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -20,7 +20,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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "reflex"
|
||||
version = "0.5.6"
|
||||
version = "0.5.7"
|
||||
description = "Web apps in pure Python."
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
from .base import ArrayVar as ArrayVar
|
||||
from .base import BooleanVar as BooleanVar
|
||||
from .base import ConcatVarOperation as ConcatVarOperation
|
||||
from .base import FunctionVar as FunctionVar
|
||||
from .base import ImmutableVar as ImmutableVar
|
||||
from .base import LiteralStringVar as LiteralStringVar
|
||||
from .base import LiteralVar as LiteralVar
|
||||
from .base import NumberVar as NumberVar
|
||||
from .base import ObjectVar as ObjectVar
|
||||
from .base import StringVar as StringVar
|
||||
|
@ -3,13 +3,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from functools import cached_property
|
||||
from typing import Any, Optional, Type
|
||||
|
||||
from reflex import constants
|
||||
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
||||
from reflex.utils import serializers, types
|
||||
from reflex.utils.exceptions import VarTypeError
|
||||
from reflex.vars import Var, VarData, _decode_var, _extract_var_data, _global_vars
|
||||
from reflex.vars import (
|
||||
ImmutableVarData,
|
||||
Var,
|
||||
VarData,
|
||||
_decode_var_immutable,
|
||||
_extract_var_data,
|
||||
_global_vars,
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
@ -27,7 +38,15 @@ class ImmutableVar(Var):
|
||||
_var_type: Type = dataclasses.field(default=Any)
|
||||
|
||||
# Extra metadata associated with the Var
|
||||
_var_data: Optional[VarData] = dataclasses.field(default=None)
|
||||
_var_data: Optional[ImmutableVarData] = dataclasses.field(default=None)
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""String representation of the var. Guaranteed to be a valid Javascript expression.
|
||||
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
return self._var_name
|
||||
|
||||
@property
|
||||
def _var_is_local(self) -> bool:
|
||||
@ -59,12 +78,25 @@ class ImmutableVar(Var):
|
||||
def __post_init__(self):
|
||||
"""Post-initialize the var."""
|
||||
# Decode any inline Var markup and apply it to the instance
|
||||
_var_data, _var_name = _decode_var(self._var_name)
|
||||
_var_data, _var_name = _decode_var_immutable(self._var_name)
|
||||
if _var_data:
|
||||
self.__init__(
|
||||
_var_name, self._var_type, VarData.merge(self._var_data, _var_data)
|
||||
_var_name,
|
||||
self._var_type,
|
||||
ImmutableVarData.merge(self._var_data, _var_data),
|
||||
)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
"""Define a hash function for the var.
|
||||
|
||||
Returns:
|
||||
The hash of the var.
|
||||
"""
|
||||
return hash((self._var_name, self._var_type, self._var_data))
|
||||
|
||||
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||
return self._var_data
|
||||
|
||||
def _replace(self, merge_var_data=None, **kwargs: Any):
|
||||
"""Make a copy of this Var with updated fields.
|
||||
|
||||
@ -96,7 +128,7 @@ class ImmutableVar(Var):
|
||||
field_values = dict(
|
||||
_var_name=kwargs.pop("_var_name", self._var_name),
|
||||
_var_type=kwargs.pop("_var_type", self._var_type),
|
||||
_var_data=VarData.merge(
|
||||
_var_data=ImmutableVarData.merge(
|
||||
kwargs.get("_var_data", self._var_data), merge_var_data
|
||||
),
|
||||
)
|
||||
@ -109,7 +141,7 @@ class ImmutableVar(Var):
|
||||
_var_is_local: bool | None = None,
|
||||
_var_is_string: bool | None = None,
|
||||
_var_data: VarData | None = None,
|
||||
) -> Var | None:
|
||||
) -> ImmutableVar | Var | None:
|
||||
"""Create a var from a value.
|
||||
|
||||
Args:
|
||||
@ -164,7 +196,15 @@ class ImmutableVar(Var):
|
||||
return cls(
|
||||
_var_name=name,
|
||||
_var_type=type_,
|
||||
_var_data=_var_data,
|
||||
_var_data=(
|
||||
ImmutableVarData(
|
||||
state=_var_data.state,
|
||||
imports=_var_data.imports,
|
||||
hooks=_var_data.hooks,
|
||||
)
|
||||
if _var_data
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ -174,7 +214,7 @@ class ImmutableVar(Var):
|
||||
_var_is_local: bool | None = None,
|
||||
_var_is_string: bool | None = None,
|
||||
_var_data: VarData | None = None,
|
||||
) -> Var:
|
||||
) -> Var | ImmutableVar:
|
||||
"""Create a var from a value, asserting that it is not None.
|
||||
|
||||
Args:
|
||||
@ -234,3 +274,181 @@ class ArrayVar(ImmutableVar):
|
||||
|
||||
class FunctionVar(ImmutableVar):
|
||||
"""Base class for immutable function vars."""
|
||||
|
||||
|
||||
class LiteralVar(ImmutableVar):
|
||||
"""Base class for immutable literal vars."""
|
||||
|
||||
def __post_init__(self):
|
||||
"""Post-initialize the var."""
|
||||
|
||||
|
||||
# Compile regex for finding reflex var tags.
|
||||
_decode_var_pattern_re = (
|
||||
rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
|
||||
)
|
||||
_decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
frozen=True,
|
||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||
)
|
||||
class LiteralStringVar(LiteralVar):
|
||||
"""Base class for immutable literal string vars."""
|
||||
|
||||
_var_value: Optional[str] = dataclasses.field(default=None)
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
value: str,
|
||||
_var_data: VarData | None = None,
|
||||
) -> LiteralStringVar | ConcatVarOperation:
|
||||
"""Create a var from a string value.
|
||||
|
||||
Args:
|
||||
value: The value to create the var from.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
|
||||
Returns:
|
||||
The var.
|
||||
"""
|
||||
if REFLEX_VAR_OPENING_TAG in value:
|
||||
strings_and_vals: list[Var] = []
|
||||
offset = 0
|
||||
|
||||
# Initialize some methods for reading json.
|
||||
var_data_config = VarData().__config__
|
||||
|
||||
def json_loads(s):
|
||||
try:
|
||||
return var_data_config.json_loads(s)
|
||||
except json.decoder.JSONDecodeError:
|
||||
return var_data_config.json_loads(
|
||||
var_data_config.json_loads(f'"{s}"')
|
||||
)
|
||||
|
||||
# Find all tags.
|
||||
while m := _decode_var_pattern.search(value):
|
||||
start, end = m.span()
|
||||
if start > 0:
|
||||
strings_and_vals.append(LiteralStringVar.create(value[:start]))
|
||||
|
||||
serialized_data = m.group(1)
|
||||
|
||||
if serialized_data[1:].isnumeric():
|
||||
# This is a global immutable var.
|
||||
var = _global_vars[int(serialized_data)]
|
||||
strings_and_vals.append(var)
|
||||
value = value[(end + len(var._var_name)) :]
|
||||
else:
|
||||
data = json_loads(serialized_data)
|
||||
string_length = data.pop("string_length", None)
|
||||
var_data = VarData.parse_obj(data)
|
||||
|
||||
# Use string length to compute positions of interpolations.
|
||||
if string_length is not None:
|
||||
realstart = start + offset
|
||||
var_data.interpolations = [
|
||||
(realstart, realstart + string_length)
|
||||
]
|
||||
strings_and_vals.append(
|
||||
ImmutableVar.create_safe(
|
||||
value[end : (end + string_length)], _var_data=var_data
|
||||
)
|
||||
)
|
||||
value = value[(end + string_length) :]
|
||||
|
||||
offset += end - start
|
||||
|
||||
if value:
|
||||
strings_and_vals.append(LiteralStringVar.create(value))
|
||||
|
||||
return ConcatVarOperation.create(
|
||||
tuple(strings_and_vals), _var_data=_var_data
|
||||
)
|
||||
|
||||
return cls(
|
||||
_var_value=value,
|
||||
_var_name=f'"{value}"',
|
||||
_var_type=str,
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
frozen=True,
|
||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||
)
|
||||
class ConcatVarOperation(StringVar):
|
||||
"""Representing a concatenation of literal string vars."""
|
||||
|
||||
_var_value: tuple[Var, ...] = dataclasses.field(default_factory=tuple)
|
||||
|
||||
def __init__(self, _var_value: tuple[Var, ...], _var_data: VarData | None = None):
|
||||
"""Initialize the operation of concatenating literal string vars.
|
||||
|
||||
Args:
|
||||
_var_value: The list of vars to concatenate.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
super(ConcatVarOperation, self).__init__(
|
||||
_var_name="", _var_data=ImmutableVarData.merge(_var_data), _var_type=str
|
||||
)
|
||||
object.__setattr__(self, "_var_value", _var_value)
|
||||
object.__setattr__(self, "_var_name", self._cached_var_name)
|
||||
|
||||
@cached_property
|
||||
def _cached_var_name(self) -> str:
|
||||
"""The name of the var.
|
||||
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
return "+".join([str(element) for element in self._var_value])
|
||||
|
||||
@cached_property
|
||||
def _cached_get_all_var_data(self) -> ImmutableVarData | None:
|
||||
"""Get all VarData associated with the Var.
|
||||
|
||||
Returns:
|
||||
The VarData of the components and all of its children.
|
||||
"""
|
||||
return ImmutableVarData.merge(
|
||||
*[var._get_all_var_data() for var in self._var_value], self._var_data
|
||||
)
|
||||
|
||||
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||
"""Wrapper method for cached property.
|
||||
|
||||
Returns:
|
||||
The VarData of the components and all of its children.
|
||||
"""
|
||||
return self._cached_get_all_var_data
|
||||
|
||||
def __post_init__(self):
|
||||
"""Post-initialize the var."""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
value: tuple[Var, ...],
|
||||
_var_data: VarData | None = None,
|
||||
) -> ConcatVarOperation:
|
||||
"""Create a var from a tuple of values.
|
||||
|
||||
Args:
|
||||
value: The value to create the var from.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
|
||||
Returns:
|
||||
The var.
|
||||
"""
|
||||
return ConcatVarOperation(
|
||||
_var_value=value,
|
||||
_var_data=_var_data,
|
||||
)
|
||||
|
@ -3,12 +3,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
from typing import Dict, List, Optional, Union
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
from reflex.base import Base
|
||||
|
||||
|
||||
def merge_imports(*imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
|
||||
def merge_imports(
|
||||
*imports: ImportDict | ParsedImportDict | ImmutableParsedImportDict,
|
||||
) -> ParsedImportDict:
|
||||
"""Merge multiple import dicts together.
|
||||
|
||||
Args:
|
||||
@ -19,7 +21,9 @@ def merge_imports(*imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
|
||||
"""
|
||||
all_imports = defaultdict(list)
|
||||
for import_dict in imports:
|
||||
for lib, fields in import_dict.items():
|
||||
for lib, fields in (
|
||||
import_dict if isinstance(import_dict, tuple) else import_dict.items()
|
||||
):
|
||||
all_imports[lib].extend(fields)
|
||||
return all_imports
|
||||
|
||||
@ -48,7 +52,9 @@ def parse_imports(imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
|
||||
}
|
||||
|
||||
|
||||
def collapse_imports(imports: ParsedImportDict) -> ParsedImportDict:
|
||||
def collapse_imports(
|
||||
imports: ParsedImportDict | ImmutableParsedImportDict,
|
||||
) -> ParsedImportDict:
|
||||
"""Remove all duplicate ImportVar within an ImportDict.
|
||||
|
||||
Args:
|
||||
@ -58,8 +64,14 @@ def collapse_imports(imports: ParsedImportDict) -> ParsedImportDict:
|
||||
The collapsed import dict.
|
||||
"""
|
||||
return {
|
||||
lib: list(set(import_vars)) if isinstance(import_vars, list) else import_vars
|
||||
for lib, import_vars in imports.items()
|
||||
lib: (
|
||||
list(set(import_vars))
|
||||
if isinstance(import_vars, list)
|
||||
else list(import_vars)
|
||||
)
|
||||
for lib, import_vars in (
|
||||
imports if isinstance(imports, tuple) else imports.items()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -99,11 +111,61 @@ class ImportVar(Base):
|
||||
else:
|
||||
return self.tag or ""
|
||||
|
||||
def __hash__(self) -> int:
|
||||
"""Define a hash function for the import var.
|
||||
def __lt__(self, other: ImportVar) -> bool:
|
||||
"""Compare two ImportVar objects.
|
||||
|
||||
Args:
|
||||
other: The other ImportVar object to compare.
|
||||
|
||||
Returns:
|
||||
The hash of the var.
|
||||
Whether this ImportVar object is less than the other.
|
||||
"""
|
||||
return (
|
||||
self.tag,
|
||||
self.is_default,
|
||||
self.alias,
|
||||
self.install,
|
||||
self.render,
|
||||
self.transpile,
|
||||
) < (
|
||||
other.tag,
|
||||
other.is_default,
|
||||
other.alias,
|
||||
other.install,
|
||||
other.render,
|
||||
other.transpile,
|
||||
)
|
||||
|
||||
def __eq__(self, other: ImportVar) -> bool:
|
||||
"""Check if two ImportVar objects are equal.
|
||||
|
||||
Args:
|
||||
other: The other ImportVar object to compare.
|
||||
|
||||
Returns:
|
||||
Whether the two ImportVar objects are equal.
|
||||
"""
|
||||
return (
|
||||
self.tag,
|
||||
self.is_default,
|
||||
self.alias,
|
||||
self.install,
|
||||
self.render,
|
||||
self.transpile,
|
||||
) == (
|
||||
other.tag,
|
||||
other.is_default,
|
||||
other.alias,
|
||||
other.install,
|
||||
other.render,
|
||||
other.transpile,
|
||||
)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
"""Hash the ImportVar object.
|
||||
|
||||
Returns:
|
||||
The hash of the ImportVar object.
|
||||
"""
|
||||
return hash(
|
||||
(
|
||||
@ -120,3 +182,4 @@ class ImportVar(Base):
|
||||
ImportTypes = Union[str, ImportVar, List[Union[str, ImportVar]], List[ImportVar]]
|
||||
ImportDict = Dict[str, ImportTypes]
|
||||
ParsedImportDict = Dict[str, List[ImportVar]]
|
||||
ImmutableParsedImportDict = Tuple[Tuple[str, Tuple[ImportVar, ...]], ...]
|
||||
|
191
reflex/vars.py
191
reflex/vars.py
@ -45,6 +45,7 @@ from reflex.utils.exceptions import (
|
||||
|
||||
# This module used to export ImportVar itself, so we still import it for export here
|
||||
from reflex.utils.imports import (
|
||||
ImmutableParsedImportDict,
|
||||
ImportDict,
|
||||
ImportVar,
|
||||
ParsedImportDict,
|
||||
@ -154,7 +155,7 @@ class VarData(Base):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@classmethod
|
||||
def merge(cls, *others: VarData | None) -> VarData | None:
|
||||
def merge(cls, *others: ImmutableVarData | VarData | None) -> VarData | None:
|
||||
"""Merge multiple var data objects.
|
||||
|
||||
Args:
|
||||
@ -172,8 +173,14 @@ class VarData(Base):
|
||||
continue
|
||||
state = state or var_data.state
|
||||
_imports = imports.merge_imports(_imports, var_data.imports)
|
||||
hooks.update(var_data.hooks)
|
||||
interpolations += var_data.interpolations
|
||||
hooks.update(
|
||||
var_data.hooks
|
||||
if isinstance(var_data.hooks, dict)
|
||||
else {k: None for k in var_data.hooks}
|
||||
)
|
||||
interpolations += (
|
||||
var_data.interpolations if isinstance(var_data, VarData) else []
|
||||
)
|
||||
|
||||
return (
|
||||
cls(
|
||||
@ -231,6 +238,173 @@ class VarData(Base):
|
||||
}
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=True,
|
||||
frozen=True,
|
||||
)
|
||||
class ImmutableVarData:
|
||||
"""Metadata associated with a Var."""
|
||||
|
||||
# The name of the enclosing state.
|
||||
state: str = dataclasses.field(default="")
|
||||
|
||||
# Imports needed to render this var
|
||||
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
||||
|
||||
# Hooks that need to be present in the component to render this var
|
||||
hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
state: str = "",
|
||||
imports: ImportDict | ParsedImportDict | None = None,
|
||||
hooks: dict[str, None] | None = None,
|
||||
):
|
||||
"""Initialize the var data.
|
||||
|
||||
Args:
|
||||
state: The name of the enclosing state.
|
||||
imports: Imports needed to render this var.
|
||||
hooks: Hooks that need to be present in the component to render this var.
|
||||
"""
|
||||
immutable_imports: ImmutableParsedImportDict = tuple(
|
||||
sorted(
|
||||
((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
|
||||
)
|
||||
)
|
||||
object.__setattr__(self, "state", state)
|
||||
object.__setattr__(self, "imports", immutable_imports)
|
||||
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
||||
|
||||
@classmethod
|
||||
def merge(
|
||||
cls, *others: ImmutableVarData | VarData | None
|
||||
) -> ImmutableVarData | None:
|
||||
"""Merge multiple var data objects.
|
||||
|
||||
Args:
|
||||
*others: The var data objects to merge.
|
||||
|
||||
Returns:
|
||||
The merged var data object.
|
||||
"""
|
||||
state = ""
|
||||
_imports = {}
|
||||
hooks = {}
|
||||
for var_data in others:
|
||||
if var_data is None:
|
||||
continue
|
||||
state = state or var_data.state
|
||||
_imports = imports.merge_imports(_imports, var_data.imports)
|
||||
hooks.update(
|
||||
var_data.hooks
|
||||
if isinstance(var_data.hooks, dict)
|
||||
else {k: None for k in var_data.hooks}
|
||||
)
|
||||
|
||||
return (
|
||||
ImmutableVarData(
|
||||
state=state,
|
||||
imports=_imports,
|
||||
hooks=hooks,
|
||||
)
|
||||
or None
|
||||
)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""Check if the var data is non-empty.
|
||||
|
||||
Returns:
|
||||
True if any field is set to a non-default value.
|
||||
"""
|
||||
return bool(self.state or self.imports or self.hooks)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
"""Check if two var data objects are equal.
|
||||
|
||||
Args:
|
||||
other: The other var data object to compare.
|
||||
|
||||
Returns:
|
||||
True if all fields are equal and collapsed imports are equal.
|
||||
"""
|
||||
if not isinstance(other, (ImmutableVarData, VarData)):
|
||||
return False
|
||||
|
||||
# Don't compare interpolations - that's added in by the decoder, and
|
||||
# not part of the vardata itself.
|
||||
return (
|
||||
self.state == other.state
|
||||
and self.hooks
|
||||
== (
|
||||
other.hooks
|
||||
if isinstance(other, ImmutableVarData)
|
||||
else tuple(other.hooks.keys())
|
||||
)
|
||||
and imports.collapse_imports(self.imports)
|
||||
== imports.collapse_imports(other.imports)
|
||||
)
|
||||
|
||||
|
||||
def _decode_var_immutable(value: str) -> tuple[ImmutableVarData | None, str]:
|
||||
"""Decode the state name from a formatted var.
|
||||
|
||||
Args:
|
||||
value: The value to extract the state name from.
|
||||
|
||||
Returns:
|
||||
The extracted state name and the value without the state name.
|
||||
"""
|
||||
var_datas = []
|
||||
if isinstance(value, str):
|
||||
# fast path if there is no encoded VarData
|
||||
if constants.REFLEX_VAR_OPENING_TAG not in value:
|
||||
return None, value
|
||||
|
||||
offset = 0
|
||||
|
||||
# Initialize some methods for reading json.
|
||||
var_data_config = VarData().__config__
|
||||
|
||||
def json_loads(s):
|
||||
try:
|
||||
return var_data_config.json_loads(s)
|
||||
except json.decoder.JSONDecodeError:
|
||||
return var_data_config.json_loads(var_data_config.json_loads(f'"{s}"'))
|
||||
|
||||
# Find all tags.
|
||||
while m := _decode_var_pattern.search(value):
|
||||
start, end = m.span()
|
||||
value = value[:start] + value[end:]
|
||||
|
||||
serialized_data = m.group(1)
|
||||
|
||||
if serialized_data[1:].isnumeric():
|
||||
# This is a global immutable var.
|
||||
var = _global_vars[int(serialized_data)]
|
||||
var_data = var._var_data
|
||||
|
||||
if var_data is not None:
|
||||
realstart = start + offset
|
||||
|
||||
var_datas.append(var_data)
|
||||
else:
|
||||
# Read the JSON, pull out the string length, parse the rest as VarData.
|
||||
data = json_loads(serialized_data)
|
||||
string_length = data.pop("string_length", None)
|
||||
var_data = VarData.parse_obj(data)
|
||||
|
||||
# Use string length to compute positions of interpolations.
|
||||
if string_length is not None:
|
||||
realstart = start + offset
|
||||
var_data.interpolations = [(realstart, realstart + string_length)]
|
||||
|
||||
var_datas.append(var_data)
|
||||
offset += end - start
|
||||
|
||||
return ImmutableVarData.merge(*var_datas) if var_datas else None, value
|
||||
|
||||
|
||||
def _encode_var(value: Var) -> str:
|
||||
"""Encode the state name into a formatted var.
|
||||
|
||||
@ -306,9 +480,6 @@ def _decode_var(value: str) -> tuple[VarData | None, str]:
|
||||
|
||||
if var_data is not None:
|
||||
realstart = start + offset
|
||||
var_data.interpolations = [
|
||||
(realstart, realstart + len(var._var_name))
|
||||
]
|
||||
|
||||
var_datas.append(var_data)
|
||||
else:
|
||||
@ -1818,6 +1989,14 @@ class Var:
|
||||
"""
|
||||
return self._var_data.state if self._var_data else ""
|
||||
|
||||
def _get_all_var_data(self) -> VarData | None:
|
||||
"""Get all the var data.
|
||||
|
||||
Returns:
|
||||
The var data.
|
||||
"""
|
||||
return self._var_data
|
||||
|
||||
@property
|
||||
def _var_name_unwrapped(self) -> str:
|
||||
"""Get the var str without wrapping in curly braces.
|
||||
|
@ -29,7 +29,7 @@ from reflex.state import State as State
|
||||
from reflex.utils import console as console
|
||||
from reflex.utils import format as format
|
||||
from reflex.utils import types as types
|
||||
from reflex.utils.imports import ImportDict, ParsedImportDict
|
||||
from reflex.utils.imports import ImmutableParsedImportDict, ImportDict, ParsedImportDict
|
||||
|
||||
USED_VARIABLES: Incomplete
|
||||
|
||||
@ -47,7 +47,24 @@ class VarData(Base):
|
||||
hooks: Dict[str, None] = {}
|
||||
interpolations: List[Tuple[int, int]] = []
|
||||
@classmethod
|
||||
def merge(cls, *others: VarData | None) -> VarData | None: ...
|
||||
def merge(cls, *others: ImmutableVarData | VarData | None) -> VarData | None: ...
|
||||
|
||||
class ImmutableVarData:
|
||||
state: str = ""
|
||||
imports: ImmutableParsedImportDict = tuple()
|
||||
hooks: Tuple[str, ...] = tuple()
|
||||
def __init__(
|
||||
self,
|
||||
state: str = "",
|
||||
imports: ImportDict | ParsedImportDict | None = None,
|
||||
hooks: dict[str, None] | None = None,
|
||||
) -> None: ...
|
||||
@classmethod
|
||||
def merge(
|
||||
cls, *others: ImmutableVarData | VarData | None
|
||||
) -> ImmutableVarData | None: ...
|
||||
|
||||
def _decode_var_immutable(value: str) -> tuple[ImmutableVarData, str]: ...
|
||||
|
||||
class Var:
|
||||
_var_name: str
|
||||
@ -133,6 +150,7 @@ class Var:
|
||||
@property
|
||||
def _var_full_name(self) -> str: ...
|
||||
def _var_set_state(self, state: Type[BaseState] | str) -> Any: ...
|
||||
def _get_all_var_data(self) -> VarData: ...
|
||||
|
||||
@dataclass(eq=False)
|
||||
class BaseVar(Var):
|
||||
|
@ -7,12 +7,17 @@ from pandas import DataFrame
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
||||
from reflex.experimental.vars.base import ImmutableVar
|
||||
from reflex.experimental.vars.base import (
|
||||
ConcatVarOperation,
|
||||
ImmutableVar,
|
||||
LiteralStringVar,
|
||||
)
|
||||
from reflex.state import BaseState
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import (
|
||||
BaseVar,
|
||||
ComputedVar,
|
||||
ImmutableVarData,
|
||||
Var,
|
||||
VarData,
|
||||
computed_var,
|
||||
@ -880,13 +885,61 @@ def test_retrival():
|
||||
)
|
||||
assert (
|
||||
result_var_data.imports
|
||||
== result_immutable_var_data.imports
|
||||
== (
|
||||
result_immutable_var_data.imports
|
||||
if isinstance(result_immutable_var_data.imports, dict)
|
||||
else {
|
||||
k: list(v)
|
||||
for k, v in result_immutable_var_data.imports
|
||||
if k in original_var_data.imports
|
||||
}
|
||||
)
|
||||
== original_var_data.imports
|
||||
)
|
||||
assert (
|
||||
result_var_data.hooks
|
||||
== result_immutable_var_data.hooks
|
||||
== original_var_data.hooks
|
||||
list(result_var_data.hooks.keys())
|
||||
== (
|
||||
list(result_immutable_var_data.hooks.keys())
|
||||
if isinstance(result_immutable_var_data.hooks, dict)
|
||||
else list(result_immutable_var_data.hooks)
|
||||
)
|
||||
== list(original_var_data.hooks.keys())
|
||||
)
|
||||
|
||||
|
||||
def test_fstring_concat():
|
||||
original_var_with_data = Var.create_safe(
|
||||
"imagination", _var_data=VarData(state="fear")
|
||||
)
|
||||
|
||||
immutable_var_with_data = ImmutableVar.create_safe(
|
||||
"consequences",
|
||||
_var_data=VarData(
|
||||
imports={
|
||||
"react": [ImportVar(tag="useRef")],
|
||||
"utils": [ImportVar(tag="useEffect")],
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
f_string = f"foo{original_var_with_data}bar{immutable_var_with_data}baz"
|
||||
|
||||
string_concat = LiteralStringVar.create(
|
||||
f_string,
|
||||
_var_data=VarData(
|
||||
hooks={"const state = useContext(StateContexts.state)": None}
|
||||
),
|
||||
)
|
||||
|
||||
assert str(string_concat) == '"foo"+imagination+"bar"+consequences+"baz"'
|
||||
assert isinstance(string_concat, ConcatVarOperation)
|
||||
assert string_concat._get_all_var_data() == ImmutableVarData(
|
||||
state="fear",
|
||||
imports={
|
||||
"react": [ImportVar(tag="useRef")],
|
||||
"utils": [ImportVar(tag="useEffect")],
|
||||
},
|
||||
hooks={"const state = useContext(StateContexts.state)": None},
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user