reflex 0.7.5__py3-none-any.whl → 0.7.6a0__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/jinja/web/pages/_app.js.jinja2 +1 -1
- reflex/__init__.py +1 -1
- reflex/admin.py +6 -2
- reflex/app.py +9 -3
- reflex/compiler/compiler.py +83 -44
- reflex/components/core/cond.py +1 -1
- reflex/components/core/foreach.py +4 -1
- reflex/config.py +0 -6
- reflex/constants/base.py +13 -1
- reflex/constants/compiler.py +1 -1
- reflex/constants/installer.py +2 -4
- reflex/custom_components/custom_components.py +17 -387
- reflex/event.py +17 -9
- reflex/reflex.py +9 -2
- reflex/state.py +13 -3
- reflex/utils/codespaces.py +12 -3
- reflex/utils/exec.py +2 -2
- reflex/utils/lazy_loader.py +63 -12
- reflex/utils/prerequisites.py +39 -33
- reflex/utils/processes.py +13 -4
- reflex/utils/pyi_generator.py +96 -13
- reflex/utils/types.py +11 -1
- {reflex-0.7.5.dist-info → reflex-0.7.6a0.dist-info}/METADATA +2 -11
- {reflex-0.7.5.dist-info → reflex-0.7.6a0.dist-info}/RECORD +27 -27
- {reflex-0.7.5.dist-info → reflex-0.7.6a0.dist-info}/WHEEL +0 -0
- {reflex-0.7.5.dist-info → reflex-0.7.6a0.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.5.dist-info → reflex-0.7.6a0.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,12 +11,10 @@ from contextlib import contextmanager
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
13
|
import httpx
|
|
14
|
-
import tomlkit
|
|
15
14
|
import typer
|
|
16
|
-
from tomlkit.exceptions import NonExistentKey, TOMLKitError
|
|
17
15
|
|
|
18
16
|
from reflex import constants
|
|
19
|
-
from reflex.config import
|
|
17
|
+
from reflex.config import get_config
|
|
20
18
|
from reflex.constants import CustomComponents
|
|
21
19
|
from reflex.utils import console
|
|
22
20
|
|
|
@@ -64,28 +62,6 @@ def _create_package_config(module_name: str, package_name: str):
|
|
|
64
62
|
)
|
|
65
63
|
|
|
66
64
|
|
|
67
|
-
def _get_package_config(exit_on_fail: bool = True) -> dict:
|
|
68
|
-
"""Get the package configuration from the pyproject.toml file.
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
exit_on_fail: Whether to exit if the pyproject.toml file is not found.
|
|
72
|
-
|
|
73
|
-
Returns:
|
|
74
|
-
The package configuration.
|
|
75
|
-
|
|
76
|
-
Raises:
|
|
77
|
-
Exit: If the pyproject.toml file is not found and exit_on_fail is True.
|
|
78
|
-
"""
|
|
79
|
-
pyproject = Path(CustomComponents.PYPROJECT_TOML)
|
|
80
|
-
try:
|
|
81
|
-
return dict(tomlkit.loads(pyproject.read_bytes()))
|
|
82
|
-
except (OSError, TOMLKitError) as ex:
|
|
83
|
-
console.error(f"Unable to read from {pyproject} due to {ex}")
|
|
84
|
-
if exit_on_fail:
|
|
85
|
-
raise typer.Exit(code=1) from ex
|
|
86
|
-
raise
|
|
87
|
-
|
|
88
|
-
|
|
89
65
|
def _create_readme(module_name: str, package_name: str):
|
|
90
66
|
"""Create a package README file.
|
|
91
67
|
|
|
@@ -425,12 +401,13 @@ def _make_pyi_files():
|
|
|
425
401
|
"""Create pyi files for the custom component."""
|
|
426
402
|
from reflex.utils.pyi_generator import PyiGenerator
|
|
427
403
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
for dir, _, _ in os.walk(f"./{package_name}"):
|
|
431
|
-
if "__pycache__" in dir:
|
|
404
|
+
for top_level_dir in Path.cwd().iterdir():
|
|
405
|
+
if not top_level_dir.is_dir() or top_level_dir.name.startswith("."):
|
|
432
406
|
continue
|
|
433
|
-
|
|
407
|
+
for dir, _, _ in top_level_dir.walk():
|
|
408
|
+
if "__pycache__" in dir.name:
|
|
409
|
+
continue
|
|
410
|
+
PyiGenerator().scan_all([dir])
|
|
434
411
|
|
|
435
412
|
|
|
436
413
|
def _run_build():
|
|
@@ -465,359 +442,18 @@ def build(
|
|
|
465
442
|
_run_build()
|
|
466
443
|
|
|
467
444
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
Args:
|
|
472
|
-
repository: The name of the repository.
|
|
473
|
-
|
|
474
|
-
Returns:
|
|
475
|
-
The name of the repository.
|
|
476
|
-
|
|
477
|
-
Raises:
|
|
478
|
-
Exit: If the repository name is not supported.
|
|
479
|
-
"""
|
|
480
|
-
if repository is None:
|
|
481
|
-
return "pypi"
|
|
482
|
-
elif repository not in CustomComponents.REPO_URLS:
|
|
483
|
-
console.error(
|
|
484
|
-
f"Unsupported repository name. Allow {CustomComponents.REPO_URLS.keys()}, got {repository}"
|
|
485
|
-
)
|
|
486
|
-
raise typer.Exit(code=1)
|
|
487
|
-
return repository
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
def _validate_credentials(
|
|
491
|
-
username: str | None, password: str | None, token: str | None
|
|
492
|
-
) -> tuple[str, str]:
|
|
493
|
-
"""Validate the credentials.
|
|
494
|
-
|
|
495
|
-
Args:
|
|
496
|
-
username: The username to use for authentication on python package repository.
|
|
497
|
-
password: The password to use for authentication on python package repository.
|
|
498
|
-
token: The token to use for authentication on python package repository.
|
|
499
|
-
|
|
500
|
-
Raises:
|
|
501
|
-
Exit: If the appropriate combination of credentials is not provided.
|
|
502
|
-
|
|
503
|
-
Returns:
|
|
504
|
-
The username and password.
|
|
505
|
-
"""
|
|
506
|
-
if token is not None:
|
|
507
|
-
if username is not None or password is not None:
|
|
508
|
-
console.error("Cannot use token and username/password at the same time.")
|
|
509
|
-
raise typer.Exit(code=1)
|
|
510
|
-
username = "__token__"
|
|
511
|
-
password = token
|
|
512
|
-
elif username is None or password is None:
|
|
513
|
-
console.error(
|
|
514
|
-
"Must provide both username and password for authentication if not using a token."
|
|
515
|
-
)
|
|
516
|
-
raise typer.Exit(code=1)
|
|
517
|
-
|
|
518
|
-
return username, password
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
def _get_version_to_publish() -> str:
|
|
522
|
-
"""Get the version to publish from the pyproject.toml.
|
|
523
|
-
|
|
524
|
-
Returns:
|
|
525
|
-
The version to publish.
|
|
526
|
-
"""
|
|
527
|
-
try:
|
|
528
|
-
return _get_package_config()["project"]["version"]
|
|
529
|
-
except NonExistentKey:
|
|
530
|
-
# Try to get the version from dynamic sources
|
|
531
|
-
import build.util
|
|
532
|
-
|
|
533
|
-
return build.util.project_wheel_metadata(".", isolated=True)["version"]
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
def _ensure_dist_dir(version_to_publish: str, build: bool):
|
|
537
|
-
"""Ensure the distribution directory and the expected files exist.
|
|
538
|
-
|
|
539
|
-
Args:
|
|
540
|
-
version_to_publish: The version to be published.
|
|
541
|
-
build: Whether to build the package first.
|
|
445
|
+
@custom_components_cli.command(name="publish", deprecated=True)
|
|
446
|
+
def publish():
|
|
447
|
+
"""Publish a custom component. This command is deprecated and will be removed in future releases.
|
|
542
448
|
|
|
543
449
|
Raises:
|
|
544
|
-
Exit: If the
|
|
450
|
+
Exit: If the publish command fails.
|
|
545
451
|
"""
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
# Need to check if the files here are for the version to be published.
|
|
550
|
-
if dist_dir.exists():
|
|
551
|
-
# Check if the distribution files are for the version to be published.
|
|
552
|
-
needs_rebuild = False
|
|
553
|
-
for suffix in CustomComponents.DISTRIBUTION_FILE_SUFFIXES:
|
|
554
|
-
if not list(dist_dir.glob(f"*{version_to_publish}*{suffix}")):
|
|
555
|
-
console.debug(
|
|
556
|
-
f"Expected distribution file with suffix {suffix} for version {version_to_publish} not found in directory {dist_dir.name}"
|
|
557
|
-
)
|
|
558
|
-
needs_rebuild = True
|
|
559
|
-
break
|
|
560
|
-
else:
|
|
561
|
-
needs_rebuild = True
|
|
562
|
-
|
|
563
|
-
if not needs_rebuild:
|
|
564
|
-
needs_rebuild = (
|
|
565
|
-
console.ask(
|
|
566
|
-
"Distribution files for the version to be published already exist. Do you want to rebuild?",
|
|
567
|
-
choices=["y", "n"],
|
|
568
|
-
default="n",
|
|
569
|
-
)
|
|
570
|
-
== "y"
|
|
571
|
-
)
|
|
572
|
-
if needs_rebuild:
|
|
573
|
-
_run_build()
|
|
574
|
-
|
|
575
|
-
# Check if the distribution directory exists.
|
|
576
|
-
if not dist_dir.exists():
|
|
577
|
-
console.error(f"Directory {dist_dir.name} does not exist. Please build first.")
|
|
578
|
-
raise typer.Exit(code=1)
|
|
579
|
-
|
|
580
|
-
# Check if the distribution directory is indeed a directory.
|
|
581
|
-
if not dist_dir.is_dir():
|
|
582
|
-
console.error(
|
|
583
|
-
f"{dist_dir.name} is not a directory. If this is a file you added, move it and rebuild."
|
|
584
|
-
)
|
|
585
|
-
raise typer.Exit(code=1)
|
|
586
|
-
|
|
587
|
-
# Check if the distribution files exist.
|
|
588
|
-
for suffix in CustomComponents.DISTRIBUTION_FILE_SUFFIXES:
|
|
589
|
-
if not list(dist_dir.glob(f"*{suffix}")):
|
|
590
|
-
console.error(
|
|
591
|
-
f"Expected distribution file with suffix {suffix} in directory {dist_dir.name}"
|
|
592
|
-
)
|
|
593
|
-
raise typer.Exit(code=1)
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
@custom_components_cli.command(name="publish")
|
|
597
|
-
def publish(
|
|
598
|
-
repository: str | None = typer.Option(
|
|
599
|
-
None,
|
|
600
|
-
"-r",
|
|
601
|
-
"--repository",
|
|
602
|
-
help="The name of the repository. Defaults to pypi. Only supports pypi and testpypi (Test PyPI) for now.",
|
|
603
|
-
),
|
|
604
|
-
token: str | None = typer.Option(
|
|
605
|
-
None,
|
|
606
|
-
"-t",
|
|
607
|
-
"--token",
|
|
608
|
-
help="The API token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time",
|
|
609
|
-
),
|
|
610
|
-
username: str | None = typer.Option(
|
|
611
|
-
environment.TWINE_USERNAME.get(),
|
|
612
|
-
"-u",
|
|
613
|
-
"--username",
|
|
614
|
-
show_default="TWINE_USERNAME environment variable value if set",
|
|
615
|
-
help="The username to use for authentication on python package repository. Username and password must both be provided.",
|
|
616
|
-
),
|
|
617
|
-
password: str | None = typer.Option(
|
|
618
|
-
environment.TWINE_PASSWORD.get(),
|
|
619
|
-
"-p",
|
|
620
|
-
"--password",
|
|
621
|
-
show_default="TWINE_PASSWORD environment variable value if set",
|
|
622
|
-
help="The password to use for authentication on python package repository. Username and password must both be provided.",
|
|
623
|
-
),
|
|
624
|
-
build: bool = typer.Option(
|
|
625
|
-
True,
|
|
626
|
-
help="Whether to build the package before publishing. If the package is already built, set this to False.",
|
|
627
|
-
),
|
|
628
|
-
share: bool = typer.Option(
|
|
629
|
-
True,
|
|
630
|
-
help="Whether to prompt to share more details on the published package. Only applicable when published to PyPI. Defaults to True.",
|
|
631
|
-
),
|
|
632
|
-
validate_project_info: bool = typer.Option(
|
|
633
|
-
True,
|
|
634
|
-
help="Whether to interactively validate the project information in the pyproject.toml file.",
|
|
635
|
-
),
|
|
636
|
-
loglevel: constants.LogLevel | None = typer.Option(
|
|
637
|
-
None, help="The log level to use."
|
|
638
|
-
),
|
|
639
|
-
):
|
|
640
|
-
"""Publish a custom component. Must be run from the project root directory where the pyproject.toml is.
|
|
641
|
-
|
|
642
|
-
Args:
|
|
643
|
-
repository: The name of the Python package repository, such pypi, testpypi.
|
|
644
|
-
token: The token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time.
|
|
645
|
-
username: The username to use for authentication on python package repository.
|
|
646
|
-
password: The password to use for authentication on python package repository.
|
|
647
|
-
build: Whether to build the distribution files. Defaults to True.
|
|
648
|
-
share: Whether to prompt to share more details on the published package. Defaults to True.
|
|
649
|
-
validate_project_info: whether to interactively validate the project information in the pyproject.toml file. Defaults to True.
|
|
650
|
-
loglevel: The log level to use.
|
|
651
|
-
|
|
652
|
-
Raises:
|
|
653
|
-
Exit: If arguments provided are not correct or the publish fails.
|
|
654
|
-
"""
|
|
655
|
-
console.set_log_level(loglevel or get_config().loglevel)
|
|
656
|
-
|
|
657
|
-
# Validate the repository name.
|
|
658
|
-
repository = _validate_repository_name(repository)
|
|
659
|
-
console.print(f"Publishing custom component to {repository}...")
|
|
660
|
-
|
|
661
|
-
# Validate the credentials.
|
|
662
|
-
username, password = _validate_credentials(username, password, token)
|
|
663
|
-
|
|
664
|
-
# Minimal Validation of the pyproject.toml.
|
|
665
|
-
_min_validate_project_info()
|
|
666
|
-
|
|
667
|
-
# Get the version to publish from the pyproject.toml.
|
|
668
|
-
version_to_publish = _get_version_to_publish()
|
|
669
|
-
|
|
670
|
-
# Validate the distribution directory.
|
|
671
|
-
_ensure_dist_dir(version_to_publish=version_to_publish, build=build)
|
|
672
|
-
|
|
673
|
-
if validate_project_info and (
|
|
674
|
-
console.ask(
|
|
675
|
-
"Would you like to interactively review the package information?",
|
|
676
|
-
choices=["y", "n"],
|
|
677
|
-
default="y",
|
|
678
|
-
)
|
|
679
|
-
== "y"
|
|
680
|
-
):
|
|
681
|
-
_validate_project_info()
|
|
682
|
-
|
|
683
|
-
publish_cmds = [
|
|
684
|
-
sys.executable,
|
|
685
|
-
"-m",
|
|
686
|
-
"twine",
|
|
687
|
-
"upload",
|
|
688
|
-
"--repository-url",
|
|
689
|
-
CustomComponents.REPO_URLS[repository],
|
|
690
|
-
"--username",
|
|
691
|
-
username,
|
|
692
|
-
"--password",
|
|
693
|
-
password,
|
|
694
|
-
"--non-interactive",
|
|
695
|
-
f"{CustomComponents.DIST_DIR}/*{version_to_publish}*",
|
|
696
|
-
]
|
|
697
|
-
if _run_commands_in_subprocess(publish_cmds):
|
|
698
|
-
console.info("Custom component published successfully!")
|
|
699
|
-
else:
|
|
700
|
-
raise typer.Exit(1)
|
|
701
|
-
|
|
702
|
-
# Only prompt to share more details on the published package if it is published to PyPI.
|
|
703
|
-
if repository != "pypi" or not share:
|
|
704
|
-
return
|
|
705
|
-
|
|
706
|
-
# Ask user to share more details on the published package.
|
|
707
|
-
if (
|
|
708
|
-
console.ask(
|
|
709
|
-
"Would you like to include your published component on our gallery?",
|
|
710
|
-
choices=["y", "n"],
|
|
711
|
-
default="y",
|
|
712
|
-
)
|
|
713
|
-
== "n"
|
|
714
|
-
):
|
|
715
|
-
console.print(
|
|
716
|
-
"If you decide to do this later, you can run `reflex component share` command. Thank you!"
|
|
717
|
-
)
|
|
718
|
-
return
|
|
719
|
-
|
|
720
|
-
_collect_details_for_gallery()
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
def _process_entered_list(input: str | None) -> list | None:
|
|
724
|
-
"""Process the user entered comma separated list into a list if applicable.
|
|
725
|
-
|
|
726
|
-
Args:
|
|
727
|
-
input: the user entered comma separated list
|
|
728
|
-
|
|
729
|
-
Returns:
|
|
730
|
-
The list of items or None.
|
|
731
|
-
"""
|
|
732
|
-
return [t.strip() for t in (input or "").split(",") if t if input] or None
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
def _min_validate_project_info():
|
|
736
|
-
"""Ensures minimal project information in the pyproject.toml file.
|
|
737
|
-
|
|
738
|
-
Raises:
|
|
739
|
-
Exit: If the pyproject.toml file is ill-formed.
|
|
740
|
-
"""
|
|
741
|
-
pyproject_toml = _get_package_config()
|
|
742
|
-
|
|
743
|
-
project = pyproject_toml.get("project")
|
|
744
|
-
if project is None:
|
|
745
|
-
console.error(
|
|
746
|
-
f"The project section is not found in {CustomComponents.PYPROJECT_TOML}"
|
|
747
|
-
)
|
|
748
|
-
raise typer.Exit(code=1)
|
|
749
|
-
|
|
750
|
-
if not project.get("name"):
|
|
751
|
-
console.error(
|
|
752
|
-
f"The project name is not found in {CustomComponents.PYPROJECT_TOML}"
|
|
753
|
-
)
|
|
754
|
-
raise typer.Exit(code=1)
|
|
755
|
-
|
|
756
|
-
if not project.get("version") and "version" not in project.get("dynamic", []):
|
|
757
|
-
console.error(
|
|
758
|
-
f"The project version is not found in {CustomComponents.PYPROJECT_TOML}"
|
|
759
|
-
)
|
|
760
|
-
raise typer.Exit(code=1)
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
def _validate_project_info():
|
|
764
|
-
"""Validate the project information in the pyproject.toml file.
|
|
765
|
-
|
|
766
|
-
Raises:
|
|
767
|
-
Exit: If the pyproject.toml file is ill-formed.
|
|
768
|
-
"""
|
|
769
|
-
pyproject_toml = _get_package_config()
|
|
770
|
-
project = pyproject_toml["project"]
|
|
771
|
-
console.print(
|
|
772
|
-
f"Double check the information before publishing: {project['name']} version {_get_version_to_publish()}"
|
|
773
|
-
)
|
|
774
|
-
|
|
775
|
-
console.print("Update or enter to keep the current information.")
|
|
776
|
-
project["description"] = console.ask(
|
|
777
|
-
"short description", default=project.get("description", "")
|
|
778
|
-
)
|
|
779
|
-
# PyPI only shows the first author.
|
|
780
|
-
author = project.get("authors", [{}])[0]
|
|
781
|
-
author["name"] = console.ask("Author Name", default=author.get("name", ""))
|
|
782
|
-
author["email"] = console.ask("Author Email", default=author.get("email", ""))
|
|
783
|
-
|
|
784
|
-
console.print(f"Current keywords are: {project.get('keywords') or []}")
|
|
785
|
-
keyword_action = console.ask(
|
|
786
|
-
"Keep, replace or append?", choices=["k", "r", "a"], default="k"
|
|
787
|
-
)
|
|
788
|
-
new_keywords = []
|
|
789
|
-
if keyword_action == "r":
|
|
790
|
-
new_keywords = (
|
|
791
|
-
_process_entered_list(
|
|
792
|
-
console.ask("Enter new set of keywords separated by commas")
|
|
793
|
-
)
|
|
794
|
-
or []
|
|
795
|
-
)
|
|
796
|
-
project["keywords"] = new_keywords
|
|
797
|
-
elif keyword_action == "a":
|
|
798
|
-
new_keywords = (
|
|
799
|
-
_process_entered_list(
|
|
800
|
-
console.ask("Enter new set of keywords separated by commas")
|
|
801
|
-
)
|
|
802
|
-
or []
|
|
803
|
-
)
|
|
804
|
-
project["keywords"] = project.get("keywords", []) + new_keywords
|
|
805
|
-
|
|
806
|
-
if not project.get("urls"):
|
|
807
|
-
project["urls"] = {}
|
|
808
|
-
project["urls"]["homepage"] = console.ask(
|
|
809
|
-
"homepage URL", default=project["urls"].get("homepage", "")
|
|
452
|
+
console.error(
|
|
453
|
+
"The publish command is deprecated. You can use `reflex component build` followed by `twine upload` or a similar publishing command to publish your custom component."
|
|
454
|
+
"\nIf you want to share your custom component with the Reflex community, please use `reflex component share`."
|
|
810
455
|
)
|
|
811
|
-
|
|
812
|
-
"source code URL", default=project["urls"].get("source", "")
|
|
813
|
-
)
|
|
814
|
-
pyproject_toml["project"] = project
|
|
815
|
-
try:
|
|
816
|
-
with CustomComponents.PYPROJECT_TOML.open("w") as f:
|
|
817
|
-
tomlkit.dump(pyproject_toml, f)
|
|
818
|
-
except (OSError, TOMLKitError) as ex:
|
|
819
|
-
console.error(f"Unable to write to pyproject.toml due to {ex}")
|
|
820
|
-
raise typer.Exit(code=1) from ex
|
|
456
|
+
raise typer.Exit(code=1)
|
|
821
457
|
|
|
822
458
|
|
|
823
459
|
def _collect_details_for_gallery():
|
|
@@ -840,14 +476,8 @@ def _collect_details_for_gallery():
|
|
|
840
476
|
|
|
841
477
|
console.rule("[bold]Custom Component Information")
|
|
842
478
|
params = {}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
package_name = _get_package_config(exit_on_fail=False)["project"]["name"]
|
|
846
|
-
except (TOMLKitError, KeyError) as ex:
|
|
847
|
-
console.debug(
|
|
848
|
-
f"Unable to read from pyproject.toml in current directory due to {ex}"
|
|
849
|
-
)
|
|
850
|
-
package_name = console.ask("[ Published python package name ]")
|
|
479
|
+
|
|
480
|
+
package_name = console.ask("[ Published python package name ]")
|
|
851
481
|
console.print(f"[ Custom component package name ] : {package_name}")
|
|
852
482
|
params["package_name"] = package_name
|
|
853
483
|
|
reflex/event.py
CHANGED
|
@@ -219,7 +219,7 @@ class EventHandler(EventActionsMixin):
|
|
|
219
219
|
from reflex.utils.exceptions import EventHandlerTypeError
|
|
220
220
|
|
|
221
221
|
# Get the function args.
|
|
222
|
-
fn_args = inspect.
|
|
222
|
+
fn_args = list(inspect.signature(self.fn).parameters)[1:]
|
|
223
223
|
fn_args = (Var(_js_expr=arg) for arg in fn_args)
|
|
224
224
|
|
|
225
225
|
# Construct the payload.
|
|
@@ -317,7 +317,9 @@ class EventSpec(EventActionsMixin):
|
|
|
317
317
|
from reflex.utils.exceptions import EventHandlerTypeError
|
|
318
318
|
|
|
319
319
|
# Get the remaining unfilled function args.
|
|
320
|
-
fn_args = inspect.
|
|
320
|
+
fn_args = list(inspect.signature(self.handler.fn).parameters)[
|
|
321
|
+
1 + len(self.args) :
|
|
322
|
+
]
|
|
321
323
|
fn_args = (Var(_js_expr=arg) for arg in fn_args)
|
|
322
324
|
|
|
323
325
|
# Construct the payload.
|
|
@@ -1264,7 +1266,9 @@ def call_event_handler(
|
|
|
1264
1266
|
type_hints_of_provided_callback = {}
|
|
1265
1267
|
|
|
1266
1268
|
if event_spec_return_types:
|
|
1267
|
-
|
|
1269
|
+
event_callback_spec_args = list(
|
|
1270
|
+
inspect.signature(event_callback.fn).parameters.keys()
|
|
1271
|
+
)
|
|
1268
1272
|
|
|
1269
1273
|
for event_spec_index, event_spec_return_type in enumerate(
|
|
1270
1274
|
event_spec_return_types
|
|
@@ -1277,7 +1281,7 @@ def call_event_handler(
|
|
|
1277
1281
|
|
|
1278
1282
|
# check that args of event handler are matching the spec if type hints are provided
|
|
1279
1283
|
for i, arg in enumerate(
|
|
1280
|
-
|
|
1284
|
+
event_callback_spec_args[1 : len(args_types_without_vars) + 1]
|
|
1281
1285
|
):
|
|
1282
1286
|
if arg not in type_hints_of_provided_callback:
|
|
1283
1287
|
continue
|
|
@@ -1320,7 +1324,7 @@ def call_event_handler(
|
|
|
1320
1324
|
|
|
1321
1325
|
given_string = ", ".join(
|
|
1322
1326
|
repr(type_hints_of_provided_callback.get(arg, Any))
|
|
1323
|
-
for arg in
|
|
1327
|
+
for arg in event_callback_spec_args[1:]
|
|
1324
1328
|
).replace("[", "\\[")
|
|
1325
1329
|
|
|
1326
1330
|
console.warn(
|
|
@@ -1424,12 +1428,16 @@ def check_fn_match_arg_spec(
|
|
|
1424
1428
|
Raises:
|
|
1425
1429
|
EventFnArgMismatchError: Raised if the number of mandatory arguments do not match
|
|
1426
1430
|
"""
|
|
1427
|
-
user_args = inspect.
|
|
1431
|
+
user_args = list(inspect.signature(user_func).parameters)
|
|
1428
1432
|
# Drop the first argument if it's a bound method
|
|
1429
1433
|
if inspect.ismethod(user_func) and user_func.__self__ is not None:
|
|
1430
1434
|
user_args = user_args[1:]
|
|
1431
1435
|
|
|
1432
|
-
user_default_args =
|
|
1436
|
+
user_default_args = [
|
|
1437
|
+
p.default
|
|
1438
|
+
for p in inspect.signature(user_func).parameters.values()
|
|
1439
|
+
if p.default is not inspect.Parameter.empty
|
|
1440
|
+
]
|
|
1433
1441
|
number_of_user_args = len(user_args) - number_of_bound_args
|
|
1434
1442
|
number_of_user_default_args = len(user_default_args) if user_default_args else 0
|
|
1435
1443
|
|
|
@@ -1476,7 +1484,7 @@ def call_event_fn(
|
|
|
1476
1484
|
|
|
1477
1485
|
parsed_args = parse_args_spec(arg_spec)
|
|
1478
1486
|
|
|
1479
|
-
number_of_fn_args = len(inspect.
|
|
1487
|
+
number_of_fn_args = len(inspect.signature(fn).parameters)
|
|
1480
1488
|
|
|
1481
1489
|
# Call the function with the parsed args.
|
|
1482
1490
|
out = fn(*[*parsed_args][:number_of_fn_args])
|
|
@@ -1520,7 +1528,7 @@ def get_handler_args(
|
|
|
1520
1528
|
Returns:
|
|
1521
1529
|
The handler args.
|
|
1522
1530
|
"""
|
|
1523
|
-
args = inspect.
|
|
1531
|
+
args = inspect.signature(event_spec.handler.fn).parameters
|
|
1524
1532
|
|
|
1525
1533
|
return event_spec.args if len(args) > 1 else ()
|
|
1526
1534
|
|
reflex/reflex.py
CHANGED
|
@@ -97,11 +97,18 @@ def _init(
|
|
|
97
97
|
prerequisites.initialize_gitignore()
|
|
98
98
|
|
|
99
99
|
# Initialize the requirements.txt.
|
|
100
|
-
prerequisites.initialize_requirements_txt()
|
|
100
|
+
wrote_to_requirements = prerequisites.initialize_requirements_txt()
|
|
101
101
|
|
|
102
102
|
template_msg = f" using the {template} template" if template else ""
|
|
103
103
|
# Finish initializing the app.
|
|
104
|
-
console.success(
|
|
104
|
+
console.success(
|
|
105
|
+
f"Initialized {app_name}{template_msg}."
|
|
106
|
+
+ (
|
|
107
|
+
f" Make sure to add {constants.RequirementsTxt.DEFAULTS_STUB + constants.Reflex.VERSION} to your requirements.txt or pyproject.toml file."
|
|
108
|
+
if not wrote_to_requirements
|
|
109
|
+
else ""
|
|
110
|
+
)
|
|
111
|
+
)
|
|
105
112
|
|
|
106
113
|
|
|
107
114
|
@cli.command()
|
reflex/state.py
CHANGED
|
@@ -1702,7 +1702,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1702
1702
|
|
|
1703
1703
|
try:
|
|
1704
1704
|
# Get the delta after processing the event.
|
|
1705
|
-
delta = await
|
|
1705
|
+
delta = await state._get_resolved_delta()
|
|
1706
1706
|
state._clean()
|
|
1707
1707
|
|
|
1708
1708
|
return StateUpdate(
|
|
@@ -1873,11 +1873,13 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1873
1873
|
tuple(state_name.split("."))
|
|
1874
1874
|
)
|
|
1875
1875
|
defining_state.dirty_vars.add(cvar)
|
|
1876
|
-
dirty_vars.add(cvar)
|
|
1877
1876
|
actual_var = defining_state.computed_vars.get(cvar)
|
|
1878
1877
|
if actual_var is not None:
|
|
1879
1878
|
actual_var.mark_dirty(instance=defining_state)
|
|
1880
|
-
if defining_state is
|
|
1879
|
+
if defining_state is self:
|
|
1880
|
+
dirty_vars.add(cvar)
|
|
1881
|
+
else:
|
|
1882
|
+
# mark dirty where this var is defined
|
|
1881
1883
|
defining_state._mark_dirty()
|
|
1882
1884
|
|
|
1883
1885
|
def _expired_computed_vars(self) -> set[str]:
|
|
@@ -1947,6 +1949,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1947
1949
|
# Return the delta.
|
|
1948
1950
|
return delta
|
|
1949
1951
|
|
|
1952
|
+
async def _get_resolved_delta(self) -> Delta:
|
|
1953
|
+
"""Get the delta for the state after resolving all coroutines.
|
|
1954
|
+
|
|
1955
|
+
Returns:
|
|
1956
|
+
The resolved delta for the state.
|
|
1957
|
+
"""
|
|
1958
|
+
return await _resolve_delta(self.get_delta())
|
|
1959
|
+
|
|
1950
1960
|
def _mark_dirty(self):
|
|
1951
1961
|
"""Mark the substate and all parent states as dirty."""
|
|
1952
1962
|
state_name = self.get_name()
|
reflex/utils/codespaces.py
CHANGED
|
@@ -11,8 +11,17 @@ from reflex.components.component import Component
|
|
|
11
11
|
from reflex.components.core.banner import has_connection_errors
|
|
12
12
|
from reflex.components.core.cond import cond
|
|
13
13
|
from reflex.constants import Endpoint
|
|
14
|
+
from reflex.utils.decorator import once
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
@once
|
|
18
|
+
def redirect_script() -> str:
|
|
19
|
+
"""Get the redirect script for Github Codespaces.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
The redirect script as a string.
|
|
23
|
+
"""
|
|
24
|
+
return """
|
|
16
25
|
const thisUrl = new URL(window.location.href);
|
|
17
26
|
const params = new URLSearchParams(thisUrl.search)
|
|
18
27
|
|
|
@@ -61,7 +70,7 @@ def codespaces_auto_redirect() -> list[Component]:
|
|
|
61
70
|
A list containing the conditional redirect component, or empty list.
|
|
62
71
|
"""
|
|
63
72
|
if is_running_in_codespaces():
|
|
64
|
-
return [cond(has_connection_errors, Script.create(redirect_script))]
|
|
73
|
+
return [cond(has_connection_errors, Script.create(redirect_script()))]
|
|
65
74
|
return []
|
|
66
75
|
|
|
67
76
|
|
|
@@ -87,5 +96,5 @@ async def auth_codespace() -> HTMLResponse:
|
|
|
87
96
|
</body>
|
|
88
97
|
</html>
|
|
89
98
|
"""
|
|
90
|
-
% redirect_script
|
|
99
|
+
% redirect_script()
|
|
91
100
|
)
|
reflex/utils/exec.py
CHANGED
|
@@ -603,13 +603,13 @@ def output_system_info():
|
|
|
603
603
|
|
|
604
604
|
dependencies = [
|
|
605
605
|
f"[Reflex {constants.Reflex.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
|
|
606
|
-
f"[Node {prerequisites.get_node_version()} (
|
|
606
|
+
f"[Node {prerequisites.get_node_version()} (Minimum: {constants.Node.MIN_VERSION}) (PATH:{path_ops.get_node_path()})]",
|
|
607
607
|
]
|
|
608
608
|
|
|
609
609
|
system = platform.system()
|
|
610
610
|
|
|
611
611
|
dependencies.append(
|
|
612
|
-
f"[Bun {prerequisites.get_bun_version()} (
|
|
612
|
+
f"[Bun {prerequisites.get_bun_version()} (Minimum: {constants.Bun.MIN_VERSION}) (PATH: {path_ops.get_bun_path()})]"
|
|
613
613
|
)
|
|
614
614
|
|
|
615
615
|
if system == "Linux":
|