merging
This commit is contained in:
commit
a0a5dcb831
@ -44,7 +44,8 @@ class Bun(SimpleNamespace):
|
|||||||
DEFAULT_PATH = ROOT_PATH / "bin" / ("bun" if not IS_WINDOWS else "bun.exe")
|
DEFAULT_PATH = ROOT_PATH / "bin" / ("bun" if not IS_WINDOWS else "bun.exe")
|
||||||
|
|
||||||
# URL to bun install script.
|
# URL to bun install script.
|
||||||
INSTALL_URL = "https://bun.sh/install"
|
INSTALL_URL = "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/bun_install.sh"
|
||||||
|
|
||||||
# URL to windows install script.
|
# URL to windows install script.
|
||||||
WINDOWS_INSTALL_URL = (
|
WINDOWS_INSTALL_URL = (
|
||||||
"https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/install.ps1"
|
"https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/install.ps1"
|
||||||
|
@ -392,6 +392,8 @@ class EventChain(EventActionsMixin):
|
|||||||
|
|
||||||
args_spec: Optional[Callable] = dataclasses.field(default=None)
|
args_spec: Optional[Callable] = dataclasses.field(default=None)
|
||||||
|
|
||||||
|
invocation: Optional[Var] = dataclasses.field(default=None)
|
||||||
|
|
||||||
|
|
||||||
# These chains can be used for their side effects when no other events are desired.
|
# These chains can be used for their side effects when no other events are desired.
|
||||||
stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation
|
stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation
|
||||||
@ -1360,10 +1362,15 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar):
|
|||||||
arg_def = ("...args",)
|
arg_def = ("...args",)
|
||||||
arg_def_expr = Var(_js_expr="args")
|
arg_def_expr = Var(_js_expr="args")
|
||||||
|
|
||||||
|
if self._var_value.invocation is None:
|
||||||
|
invocation = FunctionStringVar.create("addEvents")
|
||||||
|
else:
|
||||||
|
invocation = self._var_value.invocation
|
||||||
|
|
||||||
return str(
|
return str(
|
||||||
ArgsFunctionOperation.create(
|
ArgsFunctionOperation.create(
|
||||||
arg_def,
|
arg_def,
|
||||||
FunctionStringVar.create("addEvents").call(
|
invocation.call(
|
||||||
LiteralVar.create(
|
LiteralVar.create(
|
||||||
[LiteralVar.create(event) for event in self._var_value.events]
|
[LiteralVar.create(event) for event in self._var_value.events]
|
||||||
),
|
),
|
||||||
|
3
reflex/istate/dynamic.py
Normal file
3
reflex/istate/dynamic.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"""A container for dynamically generated states."""
|
||||||
|
|
||||||
|
# This page intentionally left blank.
|
@ -10,6 +10,7 @@ import functools
|
|||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@ -60,6 +61,7 @@ import wrapt
|
|||||||
from redis.asyncio import Redis
|
from redis.asyncio import Redis
|
||||||
from redis.exceptions import ResponseError
|
from redis.exceptions import ResponseError
|
||||||
|
|
||||||
|
import reflex.istate.dynamic
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.event import (
|
from reflex.event import (
|
||||||
@ -425,6 +427,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
if mixin:
|
if mixin:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Handle locally-defined states for pickling.
|
||||||
|
if "<locals>" in cls.__qualname__:
|
||||||
|
cls._handle_local_def()
|
||||||
|
|
||||||
# Validate the module name.
|
# Validate the module name.
|
||||||
cls._validate_module_name()
|
cls._validate_module_name()
|
||||||
|
|
||||||
@ -471,7 +477,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
new_backend_vars.update(
|
new_backend_vars.update(
|
||||||
{
|
{
|
||||||
name: cls._get_var_default(name, annotation_value)
|
name: cls._get_var_default(name, annotation_value)
|
||||||
for name, annotation_value in get_type_hints(cls).items()
|
for name, annotation_value in cls._get_type_hints().items()
|
||||||
if name not in new_backend_vars
|
if name not in new_backend_vars
|
||||||
and types.is_backend_base_variable(name, cls)
|
and types.is_backend_base_variable(name, cls)
|
||||||
}
|
}
|
||||||
@ -647,6 +653,39 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _handle_local_def(cls):
|
||||||
|
"""Handle locally-defined states for pickling."""
|
||||||
|
known_names = dir(reflex.istate.dynamic)
|
||||||
|
proposed_name = cls.__name__
|
||||||
|
for ix in range(len(known_names)):
|
||||||
|
if proposed_name not in known_names:
|
||||||
|
break
|
||||||
|
proposed_name = f"{cls.__name__}_{ix}"
|
||||||
|
setattr(reflex.istate.dynamic, proposed_name, cls)
|
||||||
|
cls.__original_name__ = cls.__name__
|
||||||
|
cls.__original_module__ = cls.__module__
|
||||||
|
cls.__name__ = cls.__qualname__ = proposed_name
|
||||||
|
cls.__module__ = reflex.istate.dynamic.__name__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_type_hints(cls) -> dict[str, Any]:
|
||||||
|
"""Get the type hints for this class.
|
||||||
|
|
||||||
|
If the class is dynamic, evaluate the type hints with the original
|
||||||
|
module in the local namespace.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The type hints dict.
|
||||||
|
"""
|
||||||
|
original_module = getattr(cls, "__original_module__", None)
|
||||||
|
if original_module is not None:
|
||||||
|
localns = sys.modules[original_module].__dict__
|
||||||
|
else:
|
||||||
|
localns = None
|
||||||
|
|
||||||
|
return get_type_hints(cls, localns=localns)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _init_var_dependency_dicts(cls):
|
def _init_var_dependency_dicts(cls):
|
||||||
"""Initialize the var dependency tracking dicts.
|
"""Initialize the var dependency tracking dicts.
|
||||||
@ -1210,7 +1249,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
name not in self.vars
|
name not in self.vars
|
||||||
and name not in self.get_skip_vars()
|
and name not in self.get_skip_vars()
|
||||||
and not name.startswith("__")
|
and not name.startswith("__")
|
||||||
and not name.startswith(f"_{type(self).__name__}__")
|
and not name.startswith(
|
||||||
|
f"_{getattr(type(self), '__original_name__', type(self).__name__)}__"
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise SetUndefinedStateVarError(
|
raise SetUndefinedStateVarError(
|
||||||
f"The state variable '{name}' has not been defined in '{type(self).__name__}'. "
|
f"The state variable '{name}' has not been defined in '{type(self).__name__}'. "
|
||||||
@ -2199,10 +2240,13 @@ class ComponentState(State, mixin=True):
|
|||||||
cls._per_component_state_instance_count += 1
|
cls._per_component_state_instance_count += 1
|
||||||
state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}"
|
state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}"
|
||||||
component_state = type(
|
component_state = type(
|
||||||
state_cls_name, (cls, State), {"__module__": __name__}, mixin=False
|
state_cls_name,
|
||||||
|
(cls, State),
|
||||||
|
{"__module__": reflex.istate.dynamic.__name__},
|
||||||
|
mixin=False,
|
||||||
)
|
)
|
||||||
# Save a reference to the dynamic state for pickle/unpickle.
|
# Save a reference to the dynamic state for pickle/unpickle.
|
||||||
globals()[state_cls_name] = component_state
|
setattr(reflex.istate.dynamic, state_cls_name, component_state)
|
||||||
component = component_state.get_component(*children, **props)
|
component = component_state.get_component(*children, **props)
|
||||||
component.State = component_state
|
component.State = component_state
|
||||||
return component
|
return component
|
||||||
|
@ -218,7 +218,7 @@ def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
|
|||||||
|
|
||||||
# Read the existing json object from the file.
|
# Read the existing json object from the file.
|
||||||
json_object = {}
|
json_object = {}
|
||||||
if fp.stat().st_size == 0:
|
if fp.stat().st_size:
|
||||||
with open(fp) as f:
|
with open(fp) as f:
|
||||||
json_object = json.load(f)
|
json_object = json.load(f)
|
||||||
|
|
||||||
|
@ -221,6 +221,27 @@ def is_literal(cls: GenericType) -> bool:
|
|||||||
return get_origin(cls) is Literal
|
return get_origin(cls) is Literal
|
||||||
|
|
||||||
|
|
||||||
|
def has_args(cls) -> bool:
|
||||||
|
"""Check if the class has generic parameters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cls: The class to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Whether the class has generic
|
||||||
|
"""
|
||||||
|
if get_args(cls):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check if the class inherits from a generic class (using __orig_bases__)
|
||||||
|
if hasattr(cls, "__orig_bases__"):
|
||||||
|
for base in cls.__orig_bases__:
|
||||||
|
if get_args(base):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_optional(cls: GenericType) -> bool:
|
def is_optional(cls: GenericType) -> bool:
|
||||||
"""Check if a class is an Optional.
|
"""Check if a class is an Optional.
|
||||||
|
|
||||||
@ -526,6 +547,10 @@ def is_backend_base_variable(name: str, cls: Type) -> bool:
|
|||||||
if name.startswith(f"_{cls.__name__}__"):
|
if name.startswith(f"_{cls.__name__}__"):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Extract the namespace of the original module if defined (dynamic substates).
|
||||||
|
if callable(getattr(cls, "_get_type_hints", None)):
|
||||||
|
hints = cls._get_type_hints()
|
||||||
|
else:
|
||||||
hints = get_type_hints(cls)
|
hints = get_type_hints(cls)
|
||||||
if name in hints:
|
if name in hints:
|
||||||
hint = get_origin(hints[name])
|
hint = get_origin(hints[name])
|
||||||
|
@ -56,7 +56,7 @@ from reflex.utils.imports import (
|
|||||||
ParsedImportDict,
|
ParsedImportDict,
|
||||||
parse_imports,
|
parse_imports,
|
||||||
)
|
)
|
||||||
from reflex.utils.types import GenericType, Self, get_origin
|
from reflex.utils.types import GenericType, Self, get_origin, has_args
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from reflex.state import BaseState
|
from reflex.state import BaseState
|
||||||
@ -1273,6 +1273,11 @@ def figure_out_type(value: Any) -> types.GenericType:
|
|||||||
Returns:
|
Returns:
|
||||||
The type of the value.
|
The type of the value.
|
||||||
"""
|
"""
|
||||||
|
if isinstance(value, Var):
|
||||||
|
return value._var_type
|
||||||
|
type_ = type(value)
|
||||||
|
if has_args(type_):
|
||||||
|
return type_
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
return List[unionize(*(figure_out_type(v) for v in value))]
|
return List[unionize(*(figure_out_type(v) for v in value))]
|
||||||
if isinstance(value, set):
|
if isinstance(value, set):
|
||||||
@ -1284,8 +1289,6 @@ def figure_out_type(value: Any) -> types.GenericType:
|
|||||||
unionize(*(figure_out_type(k) for k in value)),
|
unionize(*(figure_out_type(k) for k in value)),
|
||||||
unionize(*(figure_out_type(v) for v in value.values())),
|
unionize(*(figure_out_type(v) for v in value.values())),
|
||||||
]
|
]
|
||||||
if isinstance(value, Var):
|
|
||||||
return value._var_type
|
|
||||||
return type(value)
|
return type(value)
|
||||||
|
|
||||||
|
|
||||||
|
311
scripts/bun_install.sh
Normal file
311
scripts/bun_install.sh
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
platform=$(uname -ms)
|
||||||
|
|
||||||
|
if [[ ${OS:-} = Windows_NT ]]; then
|
||||||
|
if [[ $platform != MINGW64* ]]; then
|
||||||
|
powershell -c "irm bun.sh/install.ps1|iex"
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reset
|
||||||
|
Color_Off=''
|
||||||
|
|
||||||
|
# Regular Colors
|
||||||
|
Red=''
|
||||||
|
Green=''
|
||||||
|
Dim='' # White
|
||||||
|
|
||||||
|
# Bold
|
||||||
|
Bold_White=''
|
||||||
|
Bold_Green=''
|
||||||
|
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
# Reset
|
||||||
|
Color_Off='\033[0m' # Text Reset
|
||||||
|
|
||||||
|
# Regular Colors
|
||||||
|
Red='\033[0;31m' # Red
|
||||||
|
Green='\033[0;32m' # Green
|
||||||
|
Dim='\033[0;2m' # White
|
||||||
|
|
||||||
|
# Bold
|
||||||
|
Bold_Green='\033[1;32m' # Bold Green
|
||||||
|
Bold_White='\033[1m' # Bold White
|
||||||
|
fi
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${Red}error${Color_Off}:" "$@" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo -e "${Dim}$@ ${Color_Off}"
|
||||||
|
}
|
||||||
|
|
||||||
|
info_bold() {
|
||||||
|
echo -e "${Bold_White}$@ ${Color_Off}"
|
||||||
|
}
|
||||||
|
|
||||||
|
success() {
|
||||||
|
echo -e "${Green}$@ ${Color_Off}"
|
||||||
|
}
|
||||||
|
|
||||||
|
command -v unzip >/dev/null ||
|
||||||
|
error 'unzip is required to install bun'
|
||||||
|
|
||||||
|
if [[ $# -gt 2 ]]; then
|
||||||
|
error 'Too many arguments, only 2 are allowed. The first can be a specific tag of bun to install. (e.g. "bun-v0.1.4") The second can be a build variant of bun to install. (e.g. "debug-info")'
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $platform in
|
||||||
|
'Darwin x86_64')
|
||||||
|
target=darwin-x64
|
||||||
|
;;
|
||||||
|
'Darwin arm64')
|
||||||
|
target=darwin-aarch64
|
||||||
|
;;
|
||||||
|
'Linux aarch64' | 'Linux arm64')
|
||||||
|
target=linux-aarch64
|
||||||
|
;;
|
||||||
|
'MINGW64'*)
|
||||||
|
target=windows-x64
|
||||||
|
;;
|
||||||
|
'Linux x86_64' | *)
|
||||||
|
target=linux-x64
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ $target = darwin-x64 ]]; then
|
||||||
|
# Is this process running in Rosetta?
|
||||||
|
# redirect stderr to devnull to avoid error message when not running in Rosetta
|
||||||
|
if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then
|
||||||
|
target=darwin-aarch64
|
||||||
|
info "Your shell is running in Rosetta 2. Downloading bun for $target instead"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
GITHUB=${GITHUB-"https://github.com"}
|
||||||
|
|
||||||
|
github_repo="$GITHUB/oven-sh/bun"
|
||||||
|
|
||||||
|
if [[ $target = darwin-x64 ]]; then
|
||||||
|
# If AVX2 isn't supported, use the -baseline build
|
||||||
|
if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
|
||||||
|
target=darwin-x64-baseline
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $target = linux-x64 ]]; then
|
||||||
|
# If AVX2 isn't supported, use the -baseline build
|
||||||
|
if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
|
||||||
|
target=linux-x64-baseline
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exe_name=bun
|
||||||
|
|
||||||
|
if [[ $# = 2 && $2 = debug-info ]]; then
|
||||||
|
target=$target-profile
|
||||||
|
exe_name=bun-profile
|
||||||
|
info "You requested a debug build of bun. More information will be shown if a crash occurs."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# = 0 ]]; then
|
||||||
|
bun_uri=$github_repo/releases/latest/download/bun-$target.zip
|
||||||
|
else
|
||||||
|
bun_uri=$github_repo/releases/download/$1/bun-$target.zip
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_env=BUN_INSTALL
|
||||||
|
bin_env=\$$install_env/bin
|
||||||
|
|
||||||
|
install_dir=${!install_env:-$HOME/.bun}
|
||||||
|
bin_dir=$install_dir/bin
|
||||||
|
exe=$bin_dir/bun
|
||||||
|
|
||||||
|
if [[ ! -d $bin_dir ]]; then
|
||||||
|
mkdir -p "$bin_dir" ||
|
||||||
|
error "Failed to create install directory \"$bin_dir\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --fail --location --progress-bar --output "$exe.zip" "$bun_uri" ||
|
||||||
|
error "Failed to download bun from \"$bun_uri\""
|
||||||
|
|
||||||
|
unzip -oqd "$bin_dir" "$exe.zip" ||
|
||||||
|
error 'Failed to extract bun'
|
||||||
|
|
||||||
|
mv "$bin_dir/bun-$target/$exe_name" "$exe" ||
|
||||||
|
error 'Failed to move extracted bun to destination'
|
||||||
|
|
||||||
|
chmod +x "$exe" ||
|
||||||
|
error 'Failed to set permissions on bun executable'
|
||||||
|
|
||||||
|
rm -r "$bin_dir/bun-$target" "$exe.zip"
|
||||||
|
|
||||||
|
tildify() {
|
||||||
|
if [[ $1 = $HOME/* ]]; then
|
||||||
|
local replacement=\~/
|
||||||
|
|
||||||
|
echo "${1/$HOME\//$replacement}"
|
||||||
|
else
|
||||||
|
echo "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
success "bun was installed successfully to $Bold_Green$(tildify "$exe")"
|
||||||
|
|
||||||
|
if command -v bun >/dev/null; then
|
||||||
|
# Install completions, but we don't care if it fails
|
||||||
|
IS_BUN_AUTO_UPDATE=true $exe completions &>/dev/null || :
|
||||||
|
|
||||||
|
echo "Run 'bun --help' to get started"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
refresh_command=''
|
||||||
|
|
||||||
|
tilde_bin_dir=$(tildify "$bin_dir")
|
||||||
|
quoted_install_dir=\"${install_dir//\"/\\\"}\"
|
||||||
|
|
||||||
|
if [[ $quoted_install_dir = \"$HOME/* ]]; then
|
||||||
|
quoted_install_dir=${quoted_install_dir/$HOME\//\$HOME/}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
case $(basename "$SHELL") in
|
||||||
|
fish)
|
||||||
|
# Install completions, but we don't care if it fails
|
||||||
|
IS_BUN_AUTO_UPDATE=true SHELL=fish $exe completions &>/dev/null || :
|
||||||
|
|
||||||
|
commands=(
|
||||||
|
"set --export $install_env $quoted_install_dir"
|
||||||
|
"set --export PATH $bin_env \$PATH"
|
||||||
|
)
|
||||||
|
|
||||||
|
fish_config=$HOME/.config/fish/config.fish
|
||||||
|
tilde_fish_config=$(tildify "$fish_config")
|
||||||
|
|
||||||
|
if [[ -w $fish_config ]]; then
|
||||||
|
{
|
||||||
|
echo -e '\n# bun'
|
||||||
|
|
||||||
|
for command in "${commands[@]}"; do
|
||||||
|
echo "$command"
|
||||||
|
done
|
||||||
|
} >>"$fish_config"
|
||||||
|
|
||||||
|
info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_fish_config\""
|
||||||
|
|
||||||
|
refresh_command="source $tilde_fish_config"
|
||||||
|
else
|
||||||
|
echo "Manually add the directory to $tilde_fish_config (or similar):"
|
||||||
|
|
||||||
|
for command in "${commands[@]}"; do
|
||||||
|
info_bold " $command"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
# Install completions, but we don't care if it fails
|
||||||
|
IS_BUN_AUTO_UPDATE=true SHELL=zsh $exe completions &>/dev/null || :
|
||||||
|
|
||||||
|
commands=(
|
||||||
|
"export $install_env=$quoted_install_dir"
|
||||||
|
"export PATH=\"$bin_env:\$PATH\""
|
||||||
|
)
|
||||||
|
|
||||||
|
zsh_config=$HOME/.zshrc
|
||||||
|
tilde_zsh_config=$(tildify "$zsh_config")
|
||||||
|
|
||||||
|
if [[ -w $zsh_config ]]; then
|
||||||
|
{
|
||||||
|
echo -e '\n# bun'
|
||||||
|
|
||||||
|
for command in "${commands[@]}"; do
|
||||||
|
echo "$command"
|
||||||
|
done
|
||||||
|
} >>"$zsh_config"
|
||||||
|
|
||||||
|
info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_zsh_config\""
|
||||||
|
|
||||||
|
refresh_command="exec $SHELL"
|
||||||
|
else
|
||||||
|
echo "Manually add the directory to $tilde_zsh_config (or similar):"
|
||||||
|
|
||||||
|
for command in "${commands[@]}"; do
|
||||||
|
info_bold " $command"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
# Install completions, but we don't care if it fails
|
||||||
|
IS_BUN_AUTO_UPDATE=true SHELL=bash $exe completions &>/dev/null || :
|
||||||
|
|
||||||
|
commands=(
|
||||||
|
"export $install_env=$quoted_install_dir"
|
||||||
|
"export PATH=\"$bin_env:\$PATH\""
|
||||||
|
)
|
||||||
|
|
||||||
|
bash_configs=(
|
||||||
|
"$HOME/.bashrc"
|
||||||
|
"$HOME/.bash_profile"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ ${XDG_CONFIG_HOME:-} ]]; then
|
||||||
|
bash_configs+=(
|
||||||
|
"$XDG_CONFIG_HOME/.bash_profile"
|
||||||
|
"$XDG_CONFIG_HOME/.bashrc"
|
||||||
|
"$XDG_CONFIG_HOME/bash_profile"
|
||||||
|
"$XDG_CONFIG_HOME/bashrc"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_manually=true
|
||||||
|
for bash_config in "${bash_configs[@]}"; do
|
||||||
|
tilde_bash_config=$(tildify "$bash_config")
|
||||||
|
|
||||||
|
if [[ -w $bash_config ]]; then
|
||||||
|
{
|
||||||
|
echo -e '\n# bun'
|
||||||
|
|
||||||
|
for command in "${commands[@]}"; do
|
||||||
|
echo "$command"
|
||||||
|
done
|
||||||
|
} >>"$bash_config"
|
||||||
|
|
||||||
|
info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_bash_config\""
|
||||||
|
|
||||||
|
refresh_command="source $bash_config"
|
||||||
|
set_manually=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $set_manually = true ]]; then
|
||||||
|
echo "Manually add the directory to $tilde_bash_config (or similar):"
|
||||||
|
|
||||||
|
for command in "${commands[@]}"; do
|
||||||
|
info_bold " $command"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo 'Manually add the directory to ~/.bashrc (or similar):'
|
||||||
|
info_bold " export $install_env=$quoted_install_dir"
|
||||||
|
info_bold " export PATH=\"$bin_env:\$PATH\""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo
|
||||||
|
info "To get started, run:"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ $refresh_command ]]; then
|
||||||
|
info_bold " $refresh_command"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info_bold " bun --help"
|
@ -20,6 +20,8 @@ def ComponentStateApp():
|
|||||||
E = TypeVar("E")
|
E = TypeVar("E")
|
||||||
|
|
||||||
class MultiCounter(rx.ComponentState, Generic[E]):
|
class MultiCounter(rx.ComponentState, Generic[E]):
|
||||||
|
"""ComponentState style."""
|
||||||
|
|
||||||
count: int = 0
|
count: int = 0
|
||||||
_be: E
|
_be: E
|
||||||
_be_int: int
|
_be_int: int
|
||||||
@ -43,16 +45,47 @@ def ComponentStateApp():
|
|||||||
**props,
|
**props,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def multi_counter_func(id: str = "default") -> rx.Component:
|
||||||
|
"""Local-substate style.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: identifier for this instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A new instance of the component with its own state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class _Counter(rx.State):
|
||||||
|
count: int = 0
|
||||||
|
|
||||||
|
def increment(self):
|
||||||
|
self.count += 1
|
||||||
|
|
||||||
|
return rx.vstack(
|
||||||
|
rx.heading(_Counter.count, id=f"count-{id}"),
|
||||||
|
rx.button(
|
||||||
|
"Increment",
|
||||||
|
on_click=_Counter.increment,
|
||||||
|
id=f"button-{id}",
|
||||||
|
),
|
||||||
|
State=_Counter,
|
||||||
|
)
|
||||||
|
|
||||||
app = rx.App(state=rx.State) # noqa
|
app = rx.App(state=rx.State) # noqa
|
||||||
|
|
||||||
@rx.page()
|
@rx.page()
|
||||||
def index():
|
def index():
|
||||||
mc_a = MultiCounter.create(id="a")
|
mc_a = MultiCounter.create(id="a")
|
||||||
mc_b = MultiCounter.create(id="b")
|
mc_b = MultiCounter.create(id="b")
|
||||||
|
mc_c = multi_counter_func(id="c")
|
||||||
|
mc_d = multi_counter_func(id="d")
|
||||||
assert mc_a.State != mc_b.State
|
assert mc_a.State != mc_b.State
|
||||||
|
assert mc_c.State != mc_d.State
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
mc_a,
|
mc_a,
|
||||||
mc_b,
|
mc_b,
|
||||||
|
mc_c,
|
||||||
|
mc_d,
|
||||||
rx.button(
|
rx.button(
|
||||||
"Inc A",
|
"Inc A",
|
||||||
on_click=mc_a.State.increment, # type: ignore
|
on_click=mc_a.State.increment, # type: ignore
|
||||||
@ -150,3 +183,21 @@ async def test_component_state_app(component_state_app: AppHarness):
|
|||||||
b_state = root_state.substates[b_state_name]
|
b_state = root_state.substates[b_state_name]
|
||||||
assert b_state._backend_vars != b_state.backend_vars
|
assert b_state._backend_vars != b_state.backend_vars
|
||||||
assert b_state._be == b_state._backend_vars["_be"] == 2
|
assert b_state._be == b_state._backend_vars["_be"] == 2
|
||||||
|
|
||||||
|
# Check locally-defined substate style
|
||||||
|
count_c = driver.find_element(By.ID, "count-c")
|
||||||
|
count_d = driver.find_element(By.ID, "count-d")
|
||||||
|
button_c = driver.find_element(By.ID, "button-c")
|
||||||
|
button_d = driver.find_element(By.ID, "button-d")
|
||||||
|
|
||||||
|
assert component_state_app.poll_for_content(count_c, exp_not_equal="") == "0"
|
||||||
|
assert component_state_app.poll_for_content(count_d, exp_not_equal="") == "0"
|
||||||
|
button_c.click()
|
||||||
|
assert component_state_app.poll_for_content(count_c, exp_not_equal="0") == "1"
|
||||||
|
assert component_state_app.poll_for_content(count_d, exp_not_equal="") == "0"
|
||||||
|
button_c.click()
|
||||||
|
assert component_state_app.poll_for_content(count_c, exp_not_equal="1") == "2"
|
||||||
|
assert component_state_app.poll_for_content(count_d, exp_not_equal="") == "0"
|
||||||
|
button_d.click()
|
||||||
|
assert component_state_app.poll_for_content(count_c, exp_not_equal="1") == "2"
|
||||||
|
assert component_state_app.poll_for_content(count_d, exp_not_equal="0") == "1"
|
||||||
|
@ -2502,7 +2502,10 @@ def test_mutable_copy_vars(mutable_state: MutableTestState, copy_func: Callable)
|
|||||||
|
|
||||||
|
|
||||||
def test_duplicate_substate_class(mocker):
|
def test_duplicate_substate_class(mocker):
|
||||||
|
# Neuter pytest escape hatch, because we want to test duplicate detection.
|
||||||
mocker.patch("reflex.state.is_testing_env", lambda: False)
|
mocker.patch("reflex.state.is_testing_env", lambda: False)
|
||||||
|
# Neuter <locals> state handling since these _are_ defined inside a function.
|
||||||
|
mocker.patch("reflex.state.BaseState._handle_local_def", lambda: None)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
|
||||||
class TestState(BaseState):
|
class TestState(BaseState):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Literal, Tuple, Union
|
from typing import Any, Dict, List, Literal, Tuple, Union
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -45,3 +45,48 @@ def test_issubclass(
|
|||||||
cls: types.GenericType, cls_check: types.GenericType, expected: bool
|
cls: types.GenericType, cls_check: types.GenericType, expected: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
assert types._issubclass(cls, cls_check) == expected
|
assert types._issubclass(cls, cls_check) == expected
|
||||||
|
|
||||||
|
|
||||||
|
class CustomDict(dict[str, str]):
|
||||||
|
"""A custom dict with generic arguments."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ChildCustomDict(CustomDict):
|
||||||
|
"""A child of CustomDict."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GenericDict(dict):
|
||||||
|
"""A generic dict with no generic arguments."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ChildGenericDict(GenericDict):
|
||||||
|
"""A child of GenericDict."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cls,expected",
|
||||||
|
[
|
||||||
|
(int, False),
|
||||||
|
(str, False),
|
||||||
|
(float, False),
|
||||||
|
(Tuple[int], True),
|
||||||
|
(List[int], True),
|
||||||
|
(Union[int, str], True),
|
||||||
|
(Union[str, int], True),
|
||||||
|
(Dict[str, int], True),
|
||||||
|
(CustomDict, True),
|
||||||
|
(ChildCustomDict, True),
|
||||||
|
(GenericDict, False),
|
||||||
|
(ChildGenericDict, False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_has_args(cls, expected: bool) -> None:
|
||||||
|
assert types.has_args(cls) == expected
|
||||||
|
@ -5,6 +5,30 @@ import pytest
|
|||||||
from reflex.vars.base import figure_out_type
|
from reflex.vars.base import figure_out_type
|
||||||
|
|
||||||
|
|
||||||
|
class CustomDict(dict[str, str]):
|
||||||
|
"""A custom dict with generic arguments."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ChildCustomDict(CustomDict):
|
||||||
|
"""A child of CustomDict."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GenericDict(dict):
|
||||||
|
"""A generic dict with no generic arguments."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ChildGenericDict(GenericDict):
|
||||||
|
"""A child of GenericDict."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("value", "expected"),
|
("value", "expected"),
|
||||||
[
|
[
|
||||||
@ -15,6 +39,10 @@ from reflex.vars.base import figure_out_type
|
|||||||
([1, 2.0, "a"], List[Union[int, float, str]]),
|
([1, 2.0, "a"], List[Union[int, float, str]]),
|
||||||
({"a": 1, "b": 2}, Dict[str, int]),
|
({"a": 1, "b": 2}, Dict[str, int]),
|
||||||
({"a": 1, 2: "b"}, Dict[Union[int, str], Union[str, int]]),
|
({"a": 1, 2: "b"}, Dict[Union[int, str], Union[str, int]]),
|
||||||
|
(CustomDict(), CustomDict),
|
||||||
|
(ChildCustomDict(), ChildCustomDict),
|
||||||
|
(GenericDict({1: 1}), Dict[int, int]),
|
||||||
|
(ChildGenericDict({1: 1}), Dict[int, int]),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_figure_out_type(value, expected):
|
def test_figure_out_type(value, expected):
|
||||||
|
Loading…
Reference in New Issue
Block a user