reflex 0.4.6a4__py3-none-any.whl → 0.4.7a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/apps/blank/code/blank.py +1 -0
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
- reflex/.templates/jinja/custom_components/src.py.jinja2 +8 -8
- reflex/.templates/jinja/web/pages/index.js.jinja2 +0 -4
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +2 -6
- reflex/.templates/web/utils/state.js +6 -1
- reflex/__init__.py +2 -0
- reflex/__init__.pyi +2 -0
- reflex/app.py +12 -16
- reflex/app.pyi +2 -0
- reflex/compiler/compiler.py +10 -11
- reflex/compiler/utils.py +3 -3
- reflex/components/chakra/forms/pininput.py +2 -1
- reflex/components/component.py +71 -104
- reflex/components/core/banner.py +1 -1
- reflex/components/core/upload.py +2 -1
- reflex/components/datadisplay/__init__.py +1 -0
- reflex/components/datadisplay/logo.py +49 -0
- reflex/components/el/elements/forms.py +7 -4
- reflex/components/el/elements/forms.pyi +0 -1
- reflex/components/lucide/icon.py +3 -2
- reflex/components/lucide/icon.pyi +2 -2
- reflex/components/markdown/markdown.py +10 -6
- reflex/components/markdown/markdown.pyi +0 -3
- reflex/components/radix/themes/components/select.py +10 -3
- reflex/config.py +1 -1
- reflex/config.pyi +1 -1
- reflex/constants/base.py +4 -5
- reflex/constants/base.pyi +94 -0
- reflex/constants/compiler.py +8 -0
- reflex/custom_components/custom_components.py +1 -1
- reflex/experimental/__init__.py +14 -0
- reflex/experimental/hooks.py +75 -0
- reflex/page.py +1 -1
- reflex/reflex.py +18 -32
- reflex/style.py +4 -4
- reflex/testing.py +1 -1
- reflex/utils/console.py +6 -4
- reflex/utils/exec.py +17 -1
- reflex/utils/export.py +0 -3
- reflex/utils/prerequisites.py +239 -43
- reflex/utils/processes.py +6 -1
- reflex/utils/telemetry.py +14 -2
- reflex/vars.py +6 -6
- reflex/vars.pyi +2 -1
- {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/METADATA +15 -10
- {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/RECORD +50 -64
- {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/WHEEL +1 -1
- reflex/.templates/apps/sidebar/README.md +0 -69
- reflex/.templates/apps/sidebar/assets/favicon.ico +0 -0
- reflex/.templates/apps/sidebar/assets/github.svg +0 -10
- reflex/.templates/apps/sidebar/assets/logo.svg +0 -68
- reflex/.templates/apps/sidebar/assets/paneleft.svg +0 -13
- reflex/.templates/apps/sidebar/assets/reflex_black.svg +0 -37
- reflex/.templates/apps/sidebar/assets/reflex_white.svg +0 -8
- reflex/.templates/apps/sidebar/code/__init__.py +0 -1
- reflex/.templates/apps/sidebar/code/components/__init__.py +0 -0
- reflex/.templates/apps/sidebar/code/components/sidebar.py +0 -152
- reflex/.templates/apps/sidebar/code/pages/__init__.py +0 -3
- reflex/.templates/apps/sidebar/code/pages/dashboard.py +0 -22
- reflex/.templates/apps/sidebar/code/pages/index.py +0 -18
- reflex/.templates/apps/sidebar/code/pages/settings.py +0 -61
- reflex/.templates/apps/sidebar/code/sidebar.py +0 -16
- reflex/.templates/apps/sidebar/code/styles.py +0 -60
- reflex/.templates/apps/sidebar/code/templates/__init__.py +0 -1
- reflex/.templates/apps/sidebar/code/templates/template.py +0 -145
- {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/LICENSE +0 -0
- {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/entry_points.txt +0 -0
reflex/utils/prerequisites.py
CHANGED
|
@@ -10,6 +10,7 @@ import os
|
|
|
10
10
|
import platform
|
|
11
11
|
import random
|
|
12
12
|
import re
|
|
13
|
+
import shutil
|
|
13
14
|
import stat
|
|
14
15
|
import sys
|
|
15
16
|
import tempfile
|
|
@@ -30,6 +31,7 @@ from redis.asyncio import Redis
|
|
|
30
31
|
|
|
31
32
|
import reflex
|
|
32
33
|
from reflex import constants, model
|
|
34
|
+
from reflex.base import Base
|
|
33
35
|
from reflex.compiler import templates
|
|
34
36
|
from reflex.config import Config, get_config
|
|
35
37
|
from reflex.utils import console, path_ops, processes
|
|
@@ -37,6 +39,15 @@ from reflex.utils import console, path_ops, processes
|
|
|
37
39
|
CURRENTLY_INSTALLING_NODE = False
|
|
38
40
|
|
|
39
41
|
|
|
42
|
+
class Template(Base):
|
|
43
|
+
"""A template for a Reflex app."""
|
|
44
|
+
|
|
45
|
+
name: str
|
|
46
|
+
description: str
|
|
47
|
+
code_url: str
|
|
48
|
+
demo_url: str
|
|
49
|
+
|
|
50
|
+
|
|
40
51
|
def check_latest_package_version(package_name: str):
|
|
41
52
|
"""Check if the latest version of the package is installed.
|
|
42
53
|
|
|
@@ -407,17 +418,42 @@ def initialize_requirements_txt():
|
|
|
407
418
|
console.info(f"Unable to check {fp} for reflex dependency.")
|
|
408
419
|
|
|
409
420
|
|
|
410
|
-
def initialize_app_directory(
|
|
421
|
+
def initialize_app_directory(
|
|
422
|
+
app_name: str,
|
|
423
|
+
template_name: str = constants.Templates.DEFAULT,
|
|
424
|
+
template_code_dir_name: str | None = None,
|
|
425
|
+
template_dir: Path | None = None,
|
|
426
|
+
):
|
|
411
427
|
"""Initialize the app directory on reflex init.
|
|
412
428
|
|
|
413
429
|
Args:
|
|
414
430
|
app_name: The name of the app.
|
|
415
|
-
|
|
431
|
+
template_name: The name of the template to use.
|
|
432
|
+
template_code_dir_name: The name of the code directory in the template.
|
|
433
|
+
template_dir: The directory of the template source files.
|
|
434
|
+
|
|
435
|
+
Raises:
|
|
436
|
+
Exit: If template_name, template_code_dir_name, template_dir combination is not supported.
|
|
416
437
|
"""
|
|
417
438
|
console.log("Initializing the app directory.")
|
|
418
439
|
|
|
419
|
-
#
|
|
420
|
-
|
|
440
|
+
# By default, use the blank template from local assets.
|
|
441
|
+
if template_name == constants.Templates.DEFAULT:
|
|
442
|
+
if template_code_dir_name is not None or template_dir is not None:
|
|
443
|
+
console.error(
|
|
444
|
+
f"Only {template_name=} should be provided, got {template_code_dir_name=}, {template_dir=}."
|
|
445
|
+
)
|
|
446
|
+
raise typer.Exit(1)
|
|
447
|
+
template_code_dir_name = constants.Templates.Dirs.CODE
|
|
448
|
+
template_dir = Path(constants.Templates.Dirs.BASE, "apps", template_name)
|
|
449
|
+
else:
|
|
450
|
+
if template_code_dir_name is None or template_dir is None:
|
|
451
|
+
console.error(
|
|
452
|
+
f"For `{template_name}` template, `template_code_dir_name` and `template_dir` should both be provided."
|
|
453
|
+
)
|
|
454
|
+
raise typer.Exit(1)
|
|
455
|
+
|
|
456
|
+
console.debug(f"Using {template_name=} {template_dir=} {template_code_dir_name=}.")
|
|
421
457
|
|
|
422
458
|
# Remove all pyc and __pycache__ dirs in template directory.
|
|
423
459
|
for pyc_file in template_dir.glob("**/*.pyc"):
|
|
@@ -430,16 +466,16 @@ def initialize_app_directory(app_name: str, template: constants.Templates.Kind):
|
|
|
430
466
|
path_ops.cp(str(file), file.name)
|
|
431
467
|
|
|
432
468
|
# Rename the template app to the app name.
|
|
433
|
-
path_ops.mv(
|
|
469
|
+
path_ops.mv(template_code_dir_name, app_name)
|
|
434
470
|
path_ops.mv(
|
|
435
|
-
os.path.join(app_name,
|
|
471
|
+
os.path.join(app_name, template_name + constants.Ext.PY),
|
|
436
472
|
os.path.join(app_name, app_name + constants.Ext.PY),
|
|
437
473
|
)
|
|
438
474
|
|
|
439
475
|
# Fix up the imports.
|
|
440
476
|
path_ops.find_replace(
|
|
441
477
|
app_name,
|
|
442
|
-
f"from {
|
|
478
|
+
f"from {template_name}",
|
|
443
479
|
f"from {app_name}",
|
|
444
480
|
)
|
|
445
481
|
|
|
@@ -802,43 +838,38 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|
|
802
838
|
)
|
|
803
839
|
|
|
804
840
|
|
|
805
|
-
def
|
|
806
|
-
"""Check
|
|
841
|
+
def needs_reinit(frontend: bool = True) -> bool:
|
|
842
|
+
"""Check if an app needs to be reinitialized.
|
|
807
843
|
|
|
808
844
|
Args:
|
|
809
845
|
frontend: Whether to check if the frontend is initialized.
|
|
810
846
|
|
|
847
|
+
Returns:
|
|
848
|
+
Whether the app needs to be reinitialized.
|
|
849
|
+
|
|
811
850
|
Raises:
|
|
812
851
|
Exit: If the app is not initialized.
|
|
813
852
|
"""
|
|
814
|
-
|
|
815
|
-
has_reflex_dir = not frontend or os.path.exists(constants.Reflex.DIR)
|
|
816
|
-
has_web_dir = not frontend or os.path.exists(constants.Dirs.WEB)
|
|
817
|
-
|
|
818
|
-
# Check if the app is initialized.
|
|
819
|
-
if not (has_config and has_reflex_dir and has_web_dir):
|
|
853
|
+
if not os.path.exists(constants.Config.FILE):
|
|
820
854
|
console.error(
|
|
821
|
-
f"
|
|
855
|
+
f"{constants.Config.FILE} not found. Run [bold]{constants.Reflex.MODULE_NAME} init[/bold] first."
|
|
822
856
|
)
|
|
823
857
|
raise typer.Exit(1)
|
|
824
858
|
|
|
825
|
-
#
|
|
826
|
-
if
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
859
|
+
# Make sure the .reflex directory exists.
|
|
860
|
+
if not os.path.exists(constants.Reflex.DIR):
|
|
861
|
+
return True
|
|
862
|
+
|
|
863
|
+
# Make sure the .web directory exists in frontend mode.
|
|
864
|
+
if frontend and not os.path.exists(constants.Dirs.WEB):
|
|
865
|
+
return True
|
|
831
866
|
|
|
832
|
-
# Print a warning for Windows users.
|
|
833
867
|
if constants.IS_WINDOWS:
|
|
834
868
|
console.warn(
|
|
835
869
|
"""Windows Subsystem for Linux (WSL) is recommended for improving initial install times."""
|
|
836
870
|
)
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
"Python 3.12 on Windows has known issues with hot reload (reflex-dev/reflex#2335). "
|
|
840
|
-
"Python 3.11 is recommended with this release of Reflex."
|
|
841
|
-
)
|
|
871
|
+
# No need to reinitialize if the app is already initialized.
|
|
872
|
+
return False
|
|
842
873
|
|
|
843
874
|
|
|
844
875
|
def is_latest_template() -> bool:
|
|
@@ -1004,33 +1035,35 @@ def check_schema_up_to_date():
|
|
|
1004
1035
|
)
|
|
1005
1036
|
|
|
1006
1037
|
|
|
1007
|
-
def prompt_for_template() ->
|
|
1038
|
+
def prompt_for_template(templates: list[Template]) -> str:
|
|
1008
1039
|
"""Prompt the user to specify a template.
|
|
1009
1040
|
|
|
1041
|
+
Args:
|
|
1042
|
+
templates: The templates to choose from.
|
|
1043
|
+
|
|
1010
1044
|
Returns:
|
|
1011
|
-
The template the user
|
|
1045
|
+
The template name the user selects.
|
|
1012
1046
|
"""
|
|
1013
|
-
# Show the user the URLs of each
|
|
1047
|
+
# Show the user the URLs of each template to preview.
|
|
1014
1048
|
console.print("\nGet started with a template:")
|
|
1015
|
-
console.print("blank (https://blank-template.reflex.run) - A minimal template.")
|
|
1016
|
-
console.print(
|
|
1017
|
-
"sidebar (https://sidebar-template.reflex.run) - A template with a sidebar to navigate pages."
|
|
1018
|
-
)
|
|
1019
|
-
console.print("")
|
|
1020
1049
|
|
|
1021
1050
|
# Prompt the user to select a template.
|
|
1051
|
+
id_to_name = {
|
|
1052
|
+
str(idx): f"{template.name} ({template.demo_url}) - {template.description}"
|
|
1053
|
+
for idx, template in enumerate(templates)
|
|
1054
|
+
}
|
|
1055
|
+
for id in range(len(id_to_name)):
|
|
1056
|
+
console.print(f"({id}) {id_to_name[str(id)]}")
|
|
1057
|
+
|
|
1022
1058
|
template = console.ask(
|
|
1023
1059
|
"Which template would you like to use?",
|
|
1024
|
-
choices=[
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
if template.value != "demo"
|
|
1028
|
-
],
|
|
1029
|
-
default=constants.Templates.Kind.BLANK.value,
|
|
1060
|
+
choices=[str(i) for i in range(len(id_to_name))],
|
|
1061
|
+
show_choices=False,
|
|
1062
|
+
default="0",
|
|
1030
1063
|
)
|
|
1031
1064
|
|
|
1032
1065
|
# Return the template.
|
|
1033
|
-
return
|
|
1066
|
+
return templates[int(template)].name
|
|
1034
1067
|
|
|
1035
1068
|
|
|
1036
1069
|
def should_show_rx_chakra_migration_instructions() -> bool:
|
|
@@ -1183,3 +1216,166 @@ def migrate_to_reflex():
|
|
|
1183
1216
|
for old, new in updates.items():
|
|
1184
1217
|
line = line.replace(old, new)
|
|
1185
1218
|
print(line, end="")
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def fetch_app_templates() -> dict[str, Template]:
|
|
1222
|
+
"""Fetch the list of app templates from the Reflex backend server.
|
|
1223
|
+
|
|
1224
|
+
Returns:
|
|
1225
|
+
The name and download URL as a dictionary.
|
|
1226
|
+
"""
|
|
1227
|
+
config = get_config()
|
|
1228
|
+
if not config.cp_backend_url:
|
|
1229
|
+
console.info(
|
|
1230
|
+
"Skip fetching App templates. No backend URL is specified in the config."
|
|
1231
|
+
)
|
|
1232
|
+
return {}
|
|
1233
|
+
try:
|
|
1234
|
+
response = httpx.get(
|
|
1235
|
+
f"{config.cp_backend_url}{constants.Templates.APP_TEMPLATES_ROUTE}"
|
|
1236
|
+
)
|
|
1237
|
+
response.raise_for_status()
|
|
1238
|
+
return {
|
|
1239
|
+
template["name"]: Template.parse_obj(template)
|
|
1240
|
+
for template in response.json()
|
|
1241
|
+
}
|
|
1242
|
+
except httpx.HTTPError as ex:
|
|
1243
|
+
console.info(f"Failed to fetch app templates: {ex}")
|
|
1244
|
+
return {}
|
|
1245
|
+
except (TypeError, KeyError, json.JSONDecodeError) as tkje:
|
|
1246
|
+
console.info(f"Unable to process server response for app templates: {tkje}")
|
|
1247
|
+
return {}
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
def create_config_init_app_from_remote_template(
|
|
1251
|
+
app_name: str,
|
|
1252
|
+
template_url: str,
|
|
1253
|
+
):
|
|
1254
|
+
"""Create new rxconfig and initialize app using a remote template.
|
|
1255
|
+
|
|
1256
|
+
Args:
|
|
1257
|
+
app_name: The name of the app.
|
|
1258
|
+
template_url: The path to the template source code as a zip file.
|
|
1259
|
+
|
|
1260
|
+
Raises:
|
|
1261
|
+
Exit: If any download, file operations fail or unexpected zip file format.
|
|
1262
|
+
|
|
1263
|
+
"""
|
|
1264
|
+
# Create a temp directory for the zip download.
|
|
1265
|
+
try:
|
|
1266
|
+
temp_dir = tempfile.mkdtemp()
|
|
1267
|
+
except OSError as ose:
|
|
1268
|
+
console.error(f"Failed to create temp directory for download: {ose}")
|
|
1269
|
+
raise typer.Exit(1) from ose
|
|
1270
|
+
|
|
1271
|
+
# Use httpx GET with redirects to download the zip file.
|
|
1272
|
+
zip_file_path = Path(temp_dir) / "template.zip"
|
|
1273
|
+
try:
|
|
1274
|
+
# Note: following redirects can be risky. We only allow this for reflex built templates at the moment.
|
|
1275
|
+
response = httpx.get(template_url, follow_redirects=True)
|
|
1276
|
+
console.debug(f"Server responded download request: {response}")
|
|
1277
|
+
response.raise_for_status()
|
|
1278
|
+
except httpx.HTTPError as he:
|
|
1279
|
+
console.error(f"Failed to download the template: {he}")
|
|
1280
|
+
raise typer.Exit(1) from he
|
|
1281
|
+
try:
|
|
1282
|
+
with open(zip_file_path, "wb") as f:
|
|
1283
|
+
f.write(response.content)
|
|
1284
|
+
console.debug(f"Downloaded the zip to {zip_file_path}")
|
|
1285
|
+
except OSError as ose:
|
|
1286
|
+
console.error(f"Unable to write the downloaded zip to disk {ose}")
|
|
1287
|
+
raise typer.Exit(1) from ose
|
|
1288
|
+
|
|
1289
|
+
# Create a temp directory for the zip extraction.
|
|
1290
|
+
try:
|
|
1291
|
+
unzip_dir = Path(tempfile.mkdtemp())
|
|
1292
|
+
except OSError as ose:
|
|
1293
|
+
console.error(f"Failed to create temp directory for extracting zip: {ose}")
|
|
1294
|
+
raise typer.Exit(1) from ose
|
|
1295
|
+
try:
|
|
1296
|
+
zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir)
|
|
1297
|
+
# The zip file downloaded from github looks like:
|
|
1298
|
+
# repo-name-branch/**/*, so we need to remove the top level directory.
|
|
1299
|
+
if len(subdirs := os.listdir(unzip_dir)) != 1:
|
|
1300
|
+
console.error(f"Expected one directory in the zip, found {subdirs}")
|
|
1301
|
+
raise typer.Exit(1)
|
|
1302
|
+
template_dir = unzip_dir / subdirs[0]
|
|
1303
|
+
console.debug(f"Template folder is located at {template_dir}")
|
|
1304
|
+
except Exception as uze:
|
|
1305
|
+
console.error(f"Failed to unzip the template: {uze}")
|
|
1306
|
+
raise typer.Exit(1) from uze
|
|
1307
|
+
|
|
1308
|
+
# Move the rxconfig file here first.
|
|
1309
|
+
path_ops.mv(str(template_dir / constants.Config.FILE), constants.Config.FILE)
|
|
1310
|
+
new_config = get_config(reload=True)
|
|
1311
|
+
|
|
1312
|
+
# Get the template app's name from rxconfig in case it is different than
|
|
1313
|
+
# the source code repo name on github.
|
|
1314
|
+
template_name = new_config.app_name
|
|
1315
|
+
|
|
1316
|
+
create_config(app_name)
|
|
1317
|
+
initialize_app_directory(
|
|
1318
|
+
app_name,
|
|
1319
|
+
template_name=template_name,
|
|
1320
|
+
template_code_dir_name=template_name,
|
|
1321
|
+
template_dir=template_dir,
|
|
1322
|
+
)
|
|
1323
|
+
|
|
1324
|
+
# Clean up the temp directories.
|
|
1325
|
+
shutil.rmtree(temp_dir)
|
|
1326
|
+
shutil.rmtree(unzip_dir)
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
def initialize_app(app_name: str, template: str | None = None):
|
|
1330
|
+
"""Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit.
|
|
1331
|
+
|
|
1332
|
+
Args:
|
|
1333
|
+
app_name: The name of the app.
|
|
1334
|
+
template: The name of the template to use.
|
|
1335
|
+
|
|
1336
|
+
Raises:
|
|
1337
|
+
Exit: If template is directly provided in the command flag and is invalid.
|
|
1338
|
+
"""
|
|
1339
|
+
# Local imports to avoid circular imports.
|
|
1340
|
+
from reflex.utils import telemetry
|
|
1341
|
+
|
|
1342
|
+
# Check if the app is already initialized.
|
|
1343
|
+
if os.path.exists(constants.Config.FILE):
|
|
1344
|
+
telemetry.send("reinit")
|
|
1345
|
+
return
|
|
1346
|
+
|
|
1347
|
+
# Get the available templates
|
|
1348
|
+
templates: dict[str, Template] = fetch_app_templates()
|
|
1349
|
+
|
|
1350
|
+
# Prompt for a template if not provided.
|
|
1351
|
+
if template is None and len(templates) > 0:
|
|
1352
|
+
template = prompt_for_template(list(templates.values()))
|
|
1353
|
+
elif template is None:
|
|
1354
|
+
template = constants.Templates.DEFAULT
|
|
1355
|
+
assert template is not None
|
|
1356
|
+
|
|
1357
|
+
# If the blank template is selected, create a blank app.
|
|
1358
|
+
if template == constants.Templates.DEFAULT:
|
|
1359
|
+
# Default app creation behavior: a blank app.
|
|
1360
|
+
create_config(app_name)
|
|
1361
|
+
initialize_app_directory(app_name)
|
|
1362
|
+
else:
|
|
1363
|
+
# Fetch App templates from the backend server.
|
|
1364
|
+
console.debug(f"Available templates: {templates}")
|
|
1365
|
+
|
|
1366
|
+
# If user selects a template, it needs to exist.
|
|
1367
|
+
if template in templates:
|
|
1368
|
+
template_url = templates[template].code_url
|
|
1369
|
+
else:
|
|
1370
|
+
# Check if the template is a github repo.
|
|
1371
|
+
if template.startswith("https://github.com"):
|
|
1372
|
+
template_url = f"{template.strip('/')}/archive/main.zip"
|
|
1373
|
+
else:
|
|
1374
|
+
console.error(f"Template `{template}` not found.")
|
|
1375
|
+
raise typer.Exit(1)
|
|
1376
|
+
create_config_init_app_from_remote_template(
|
|
1377
|
+
app_name=app_name,
|
|
1378
|
+
template_url=template_url,
|
|
1379
|
+
)
|
|
1380
|
+
|
|
1381
|
+
telemetry.send("init")
|
reflex/utils/processes.py
CHANGED
|
@@ -14,6 +14,7 @@ import psutil
|
|
|
14
14
|
import typer
|
|
15
15
|
from redis.exceptions import RedisError
|
|
16
16
|
|
|
17
|
+
from reflex import constants
|
|
17
18
|
from reflex.utils import console, path_ops, prerequisites
|
|
18
19
|
|
|
19
20
|
|
|
@@ -227,7 +228,11 @@ def stream_logs(message: str, process: subprocess.Popen, progress=None):
|
|
|
227
228
|
yield line
|
|
228
229
|
|
|
229
230
|
# Check if the process failed (not printing the logs for SIGINT).
|
|
230
|
-
|
|
231
|
+
|
|
232
|
+
# Windows uvicorn bug
|
|
233
|
+
# https://github.com/reflex-dev/reflex/issues/2335
|
|
234
|
+
accepted_return_codes = [0, -2, 15] if constants.IS_WINDOWS else [0, -2]
|
|
235
|
+
if process.returncode not in accepted_return_codes:
|
|
231
236
|
console.error(f"{message} failed with exit code {process.returncode}")
|
|
232
237
|
for line in logs:
|
|
233
238
|
console.error(line, end="")
|
reflex/utils/telemetry.py
CHANGED
|
@@ -4,7 +4,13 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import multiprocessing
|
|
6
6
|
import platform
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from datetime import UTC, datetime
|
|
10
|
+
except ImportError:
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
UTC = None
|
|
8
14
|
|
|
9
15
|
import httpx
|
|
10
16
|
import psutil
|
|
@@ -99,6 +105,12 @@ def _prepare_event(event: str) -> dict:
|
|
|
99
105
|
)
|
|
100
106
|
return {}
|
|
101
107
|
|
|
108
|
+
if UTC is None:
|
|
109
|
+
# for python 3.8, 3.9 & 3.10
|
|
110
|
+
stamp = datetime.utcnow().isoformat()
|
|
111
|
+
else:
|
|
112
|
+
# for python 3.11 & 3.12
|
|
113
|
+
stamp = datetime.now(UTC).isoformat()
|
|
102
114
|
return {
|
|
103
115
|
"api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb",
|
|
104
116
|
"event": event,
|
|
@@ -111,7 +123,7 @@ def _prepare_event(event: str) -> dict:
|
|
|
111
123
|
"cpu_count": get_cpu_count(),
|
|
112
124
|
"memory": get_memory(),
|
|
113
125
|
},
|
|
114
|
-
"timestamp":
|
|
126
|
+
"timestamp": stamp,
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
|
reflex/vars.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Define a state var."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import contextlib
|
|
@@ -21,7 +22,6 @@ from typing import (
|
|
|
21
22
|
List,
|
|
22
23
|
Literal,
|
|
23
24
|
Optional,
|
|
24
|
-
Set,
|
|
25
25
|
Tuple,
|
|
26
26
|
Type,
|
|
27
27
|
Union,
|
|
@@ -119,7 +119,7 @@ class VarData(Base):
|
|
|
119
119
|
imports: ImportDict = {}
|
|
120
120
|
|
|
121
121
|
# Hooks that need to be present in the component to render this var
|
|
122
|
-
hooks:
|
|
122
|
+
hooks: Dict[str, None] = {}
|
|
123
123
|
|
|
124
124
|
# Positions of interpolated strings. This is used by the decoder to figure
|
|
125
125
|
# out where the interpolations are and only escape the non-interpolated
|
|
@@ -138,7 +138,7 @@ class VarData(Base):
|
|
|
138
138
|
"""
|
|
139
139
|
state = ""
|
|
140
140
|
_imports = {}
|
|
141
|
-
hooks =
|
|
141
|
+
hooks = {}
|
|
142
142
|
interpolations = []
|
|
143
143
|
for var_data in others:
|
|
144
144
|
if var_data is None:
|
|
@@ -182,7 +182,7 @@ class VarData(Base):
|
|
|
182
182
|
# not part of the vardata itself.
|
|
183
183
|
return (
|
|
184
184
|
self.state == other.state
|
|
185
|
-
and self.hooks == other.hooks
|
|
185
|
+
and self.hooks.keys() == other.hooks.keys()
|
|
186
186
|
and imports.collapse_imports(self.imports)
|
|
187
187
|
== imports.collapse_imports(other.imports)
|
|
188
188
|
)
|
|
@@ -200,7 +200,7 @@ class VarData(Base):
|
|
|
200
200
|
lib: [import_var.dict() for import_var in import_vars]
|
|
201
201
|
for lib, import_vars in self.imports.items()
|
|
202
202
|
},
|
|
203
|
-
"hooks":
|
|
203
|
+
"hooks": self.hooks,
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
|
|
@@ -1659,7 +1659,7 @@ class Var:
|
|
|
1659
1659
|
hooks={
|
|
1660
1660
|
"const {0} = useContext(StateContexts.{0})".format(
|
|
1661
1661
|
format.format_state_name(state_name)
|
|
1662
|
-
)
|
|
1662
|
+
): None
|
|
1663
1663
|
},
|
|
1664
1664
|
imports={
|
|
1665
1665
|
f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
|
reflex/vars.pyi
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
""" Generated with stubgen from mypy, then manually edited, do not regen."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from dataclasses import dataclass
|
|
@@ -35,7 +36,7 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]: ...
|
|
|
35
36
|
class VarData(Base):
|
|
36
37
|
state: str
|
|
37
38
|
imports: dict[str, set[ImportVar]]
|
|
38
|
-
hooks:
|
|
39
|
+
hooks: Dict[str, None]
|
|
39
40
|
interpolations: List[Tuple[int, int]]
|
|
40
41
|
@classmethod
|
|
41
42
|
def merge(cls, *others: VarData | None) -> VarData | None: ...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: reflex
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7a1
|
|
4
4
|
Summary: Web apps in pure Python.
|
|
5
5
|
Home-page: https://reflex.dev
|
|
6
6
|
License: Apache-2.0
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
19
|
Requires-Dist: alembic (>=1.11.1,<2.0)
|
|
19
20
|
Requires-Dist: build (>=1.0.3,<2.0)
|
|
20
21
|
Requires-Dist: charset-normalizer (>=3.3.2,<4.0)
|
|
@@ -74,6 +75,18 @@ Description-Content-Type: text/markdown
|
|
|
74
75
|
[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)
|
|
75
76
|
|
|
76
77
|
---
|
|
78
|
+
|
|
79
|
+
# Reflex
|
|
80
|
+
|
|
81
|
+
Reflex is a library to build full-stack web apps in pure Python.
|
|
82
|
+
|
|
83
|
+
Key features:
|
|
84
|
+
* **Pure Python** - Write your app's frontend and backend all in Python, no need to learn Javascript.
|
|
85
|
+
* **Full Flexibility** - Reflex is easy to get started with, but can also scale to complex apps.
|
|
86
|
+
* **Deploy Instantly** - After building, deploy your app with a [single command](https://reflex.dev/docs/hosting/deploy-quick-start/) or host it on your own server.
|
|
87
|
+
|
|
88
|
+
See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) to learn how Reflex works under the hood.
|
|
89
|
+
|
|
77
90
|
## ⚙️ Installation
|
|
78
91
|
|
|
79
92
|
Open a terminal and run (Requires Python 3.8+):
|
|
@@ -263,19 +276,11 @@ You can create a multi-page app by adding more pages.
|
|
|
263
276
|
</div>
|
|
264
277
|
|
|
265
278
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
279
|
## ✅ Status
|
|
270
280
|
|
|
271
281
|
Reflex launched in December 2022 with the name Pynecone.
|
|
272
282
|
|
|
273
|
-
As of
|
|
274
|
-
|
|
275
|
-
- :white_check_mark: **Public Alpha**: Anyone can install and use Reflex. There may be issues, but we are working to resolve them actively.
|
|
276
|
-
- :large_orange_diamond: **Public Beta**: Stable enough for non-enterprise use-cases.
|
|
277
|
-
- **Public Hosting Beta**: _Optionally_, deploy and host your apps on Reflex!
|
|
278
|
-
- **Public**: Reflex is production ready.
|
|
283
|
+
As of February 2024, our hosting service is in alpha! During this time anyone can deploy their apps for free. See our [roadmap](https://github.com/reflex-dev/reflex/issues/2727) to see what's planned.
|
|
279
284
|
|
|
280
285
|
Reflex has new releases and features coming every week! Make sure to :star: star and :eyes: watch this repository to stay up to date.
|
|
281
286
|
|