reflex 0.8.6a0__py3-none-any.whl → 0.8.7__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/vite.config.js.jinja2 +4 -1
- reflex/.templates/web/app/routes.js +0 -1
- reflex/.templates/web/utils/state.js +1 -11
- reflex/app.py +27 -23
- reflex/components/base/error_boundary.py +2 -0
- reflex/components/component.py +6 -4
- reflex/components/lucide/icon.py +4 -1
- reflex/components/lucide/icon.pyi +4 -1
- reflex/components/plotly/plotly.py +9 -9
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +7 -7
- reflex/components/sonner/toast.pyi +8 -8
- reflex/config.py +9 -2
- reflex/constants/base.py +2 -0
- reflex/constants/installer.py +6 -6
- reflex/constants/state.py +1 -0
- reflex/custom_components/custom_components.py +3 -3
- reflex/istate/manager.py +2 -1
- reflex/plugins/__init__.py +2 -0
- reflex/plugins/_screenshot.py +144 -0
- reflex/plugins/base.py +14 -1
- reflex/reflex.py +7 -6
- reflex/route.py +4 -0
- reflex/state.py +2 -2
- reflex/testing.py +3 -5
- reflex/utils/build.py +21 -3
- reflex/utils/exec.py +11 -11
- reflex/utils/frontend_skeleton.py +254 -0
- reflex/utils/js_runtimes.py +411 -0
- reflex/utils/prerequisites.py +17 -1383
- reflex/utils/rename.py +170 -0
- reflex/utils/telemetry.py +101 -10
- reflex/utils/templates.py +443 -0
- reflex/vars/base.py +3 -3
- {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/METADATA +2 -2
- {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/RECORD +39 -37
- reflex/.templates/web/utils/client_side_routing.js +0 -45
- reflex/components/core/client_side_routing.py +0 -70
- reflex/components/core/client_side_routing.pyi +0 -68
- {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/WHEEL +0 -0
- {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/licenses/LICENSE +0 -0
reflex/utils/rename.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""This module provides utilities for renaming directories and files in a Reflex app."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from reflex import constants
|
|
10
|
+
from reflex.config import get_config
|
|
11
|
+
from reflex.utils import console
|
|
12
|
+
from reflex.utils.misc import get_module_path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def rename_path_up_tree(full_path: str | Path, old_name: str, new_name: str) -> Path:
|
|
16
|
+
"""Rename all instances of `old_name` in the path (file and directories) to `new_name`.
|
|
17
|
+
The renaming stops when we reach the directory containing `rxconfig.py`.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
full_path: The full path to start renaming from.
|
|
21
|
+
old_name: The name to be replaced.
|
|
22
|
+
new_name: The replacement name.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
The updated path after renaming.
|
|
26
|
+
"""
|
|
27
|
+
current_path = Path(full_path)
|
|
28
|
+
new_path = None
|
|
29
|
+
|
|
30
|
+
while True:
|
|
31
|
+
directory, base = current_path.parent, current_path.name
|
|
32
|
+
# Stop renaming when we reach the root dir (which contains rxconfig.py)
|
|
33
|
+
if current_path.is_dir() and (current_path / "rxconfig.py").exists():
|
|
34
|
+
new_path = current_path
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
if old_name == base.removesuffix(constants.Ext.PY):
|
|
38
|
+
new_base = base.replace(old_name, new_name)
|
|
39
|
+
new_path = directory / new_base
|
|
40
|
+
current_path.rename(new_path)
|
|
41
|
+
console.debug(f"Renamed {current_path} -> {new_path}")
|
|
42
|
+
current_path = new_path
|
|
43
|
+
else:
|
|
44
|
+
new_path = current_path
|
|
45
|
+
|
|
46
|
+
# Move up the directory tree
|
|
47
|
+
current_path = directory
|
|
48
|
+
|
|
49
|
+
return new_path
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def rename_app(new_app_name: str, loglevel: constants.LogLevel):
|
|
53
|
+
"""Rename the app directory.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
new_app_name: The new name for the app.
|
|
57
|
+
loglevel: The log level to use.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
Exit: If the command is not ran in the root dir or the app module cannot be imported.
|
|
61
|
+
"""
|
|
62
|
+
# Set the log level.
|
|
63
|
+
console.set_log_level(loglevel)
|
|
64
|
+
|
|
65
|
+
if not constants.Config.FILE.exists():
|
|
66
|
+
console.error(
|
|
67
|
+
"No rxconfig.py found. Make sure you are in the root directory of your app."
|
|
68
|
+
)
|
|
69
|
+
raise click.exceptions.Exit(1)
|
|
70
|
+
|
|
71
|
+
sys.path.insert(0, str(Path.cwd()))
|
|
72
|
+
|
|
73
|
+
config = get_config()
|
|
74
|
+
module_path = get_module_path(config.module)
|
|
75
|
+
if module_path is None:
|
|
76
|
+
console.error(f"Could not find module {config.module}.")
|
|
77
|
+
raise click.exceptions.Exit(1)
|
|
78
|
+
|
|
79
|
+
console.info(f"Renaming app directory to {new_app_name}.")
|
|
80
|
+
process_directory(
|
|
81
|
+
Path.cwd(),
|
|
82
|
+
config.app_name,
|
|
83
|
+
new_app_name,
|
|
84
|
+
exclude_dirs=[constants.Dirs.WEB, constants.Dirs.APP_ASSETS],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
rename_path_up_tree(module_path, config.app_name, new_app_name)
|
|
88
|
+
|
|
89
|
+
console.success(f"App directory renamed to [bold]{new_app_name}[/bold].")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def rename_imports_and_app_name(file_path: str | Path, old_name: str, new_name: str):
|
|
93
|
+
"""Rename imports the file using string replacement as well as app_name in rxconfig.py.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
file_path: The file to process.
|
|
97
|
+
old_name: The old name to replace.
|
|
98
|
+
new_name: The new name to use.
|
|
99
|
+
"""
|
|
100
|
+
file_path = Path(file_path)
|
|
101
|
+
content = file_path.read_text()
|
|
102
|
+
|
|
103
|
+
# Replace `from old_name.` or `from old_name` with `from new_name`
|
|
104
|
+
content = re.sub(
|
|
105
|
+
rf"\bfrom {re.escape(old_name)}(\b|\.|\s)",
|
|
106
|
+
lambda match: f"from {new_name}{match.group(1)}",
|
|
107
|
+
content,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Replace `import old_name` with `import new_name`
|
|
111
|
+
content = re.sub(
|
|
112
|
+
rf"\bimport {re.escape(old_name)}\b",
|
|
113
|
+
f"import {new_name}",
|
|
114
|
+
content,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Replace `app_name="old_name"` in rx.Config
|
|
118
|
+
content = re.sub(
|
|
119
|
+
rf'\bapp_name\s*=\s*["\']{re.escape(old_name)}["\']',
|
|
120
|
+
f'app_name="{new_name}"',
|
|
121
|
+
content,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Replace positional argument `"old_name"` in rx.Config
|
|
125
|
+
content = re.sub(
|
|
126
|
+
rf'\brx\.Config\(\s*["\']{re.escape(old_name)}["\']',
|
|
127
|
+
f'rx.Config("{new_name}"',
|
|
128
|
+
content,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
file_path.write_text(content)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def process_directory(
|
|
135
|
+
directory: str | Path,
|
|
136
|
+
old_name: str,
|
|
137
|
+
new_name: str,
|
|
138
|
+
exclude_dirs: list | None = None,
|
|
139
|
+
extensions: list | None = None,
|
|
140
|
+
):
|
|
141
|
+
"""Process files with specified extensions in a directory, excluding specified directories.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
directory: The root directory to process.
|
|
145
|
+
old_name: The old name to replace.
|
|
146
|
+
new_name: The new name to use.
|
|
147
|
+
exclude_dirs: List of directory names to exclude. Defaults to None.
|
|
148
|
+
extensions: List of file extensions to process.
|
|
149
|
+
"""
|
|
150
|
+
exclude_dirs = exclude_dirs or []
|
|
151
|
+
extensions = extensions or [
|
|
152
|
+
constants.Ext.PY,
|
|
153
|
+
constants.Ext.MD,
|
|
154
|
+
] # include .md files, typically used in reflex-web.
|
|
155
|
+
extensions_set = {ext.lstrip(".") for ext in extensions}
|
|
156
|
+
directory = Path(directory)
|
|
157
|
+
|
|
158
|
+
root_exclude_dirs = {directory / exclude_dir for exclude_dir in exclude_dirs}
|
|
159
|
+
|
|
160
|
+
files = (
|
|
161
|
+
p.resolve()
|
|
162
|
+
for p in directory.glob("**/*")
|
|
163
|
+
if p.is_file() and p.suffix.lstrip(".") in extensions_set
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
for file_path in files:
|
|
167
|
+
if not any(
|
|
168
|
+
file_path.is_relative_to(exclude_dir) for exclude_dir in root_exclude_dirs
|
|
169
|
+
):
|
|
170
|
+
rename_imports_and_app_name(file_path, old_name, new_name)
|
reflex/utils/telemetry.py
CHANGED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import dataclasses
|
|
7
7
|
import importlib.metadata
|
|
8
|
+
import json
|
|
8
9
|
import multiprocessing
|
|
9
10
|
import platform
|
|
10
11
|
import warnings
|
|
@@ -14,20 +15,112 @@ from typing import TypedDict
|
|
|
14
15
|
|
|
15
16
|
from reflex import constants
|
|
16
17
|
from reflex.environment import environment
|
|
17
|
-
from reflex.utils import console
|
|
18
|
-
from reflex.utils.decorator import once_unless_none
|
|
18
|
+
from reflex.utils import console, processes
|
|
19
|
+
from reflex.utils.decorator import once, once_unless_none
|
|
19
20
|
from reflex.utils.exceptions import ReflexError
|
|
20
|
-
from reflex.utils.
|
|
21
|
-
|
|
22
|
-
get_bun_version,
|
|
23
|
-
get_node_version,
|
|
24
|
-
get_project_hash,
|
|
25
|
-
)
|
|
21
|
+
from reflex.utils.js_runtimes import get_bun_version, get_node_version
|
|
22
|
+
from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
|
|
26
23
|
|
|
27
24
|
UTC = timezone.utc
|
|
28
25
|
POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
|
|
29
26
|
|
|
30
27
|
|
|
28
|
+
@dataclasses.dataclass(frozen=True)
|
|
29
|
+
class CpuInfo:
|
|
30
|
+
"""Model to save cpu info."""
|
|
31
|
+
|
|
32
|
+
manufacturer_id: str | None
|
|
33
|
+
model_name: str | None
|
|
34
|
+
address_width: int | None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def format_address_width(address_width: str | None) -> int | None:
|
|
38
|
+
"""Cast address width to an int.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
address_width: The address width.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Address width int
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
return int(address_width) if address_width else None
|
|
48
|
+
except ValueError:
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _retrieve_cpu_info() -> CpuInfo | None:
|
|
53
|
+
"""Retrieve the CPU info of the host.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The CPU info.
|
|
57
|
+
"""
|
|
58
|
+
platform_os = platform.system()
|
|
59
|
+
cpuinfo = {}
|
|
60
|
+
try:
|
|
61
|
+
if platform_os == "Windows":
|
|
62
|
+
cmd = 'powershell -Command "Get-CimInstance Win32_Processor | Select-Object -First 1 | Select-Object AddressWidth,Manufacturer,Name | ConvertTo-Json"'
|
|
63
|
+
output = processes.execute_command_and_return_output(cmd)
|
|
64
|
+
if output:
|
|
65
|
+
cpu_data = json.loads(output)
|
|
66
|
+
cpuinfo["address_width"] = cpu_data["AddressWidth"]
|
|
67
|
+
cpuinfo["manufacturer_id"] = cpu_data["Manufacturer"]
|
|
68
|
+
cpuinfo["model_name"] = cpu_data["Name"]
|
|
69
|
+
elif platform_os == "Linux":
|
|
70
|
+
output = processes.execute_command_and_return_output("lscpu")
|
|
71
|
+
if output:
|
|
72
|
+
lines = output.split("\n")
|
|
73
|
+
for line in lines:
|
|
74
|
+
if "Architecture" in line:
|
|
75
|
+
cpuinfo["address_width"] = (
|
|
76
|
+
64 if line.split(":")[1].strip() == "x86_64" else 32
|
|
77
|
+
)
|
|
78
|
+
if "Vendor ID:" in line:
|
|
79
|
+
cpuinfo["manufacturer_id"] = line.split(":")[1].strip()
|
|
80
|
+
if "Model name" in line:
|
|
81
|
+
cpuinfo["model_name"] = line.split(":")[1].strip()
|
|
82
|
+
elif platform_os == "Darwin":
|
|
83
|
+
cpuinfo["address_width"] = format_address_width(
|
|
84
|
+
processes.execute_command_and_return_output("getconf LONG_BIT")
|
|
85
|
+
)
|
|
86
|
+
cpuinfo["manufacturer_id"] = processes.execute_command_and_return_output(
|
|
87
|
+
"sysctl -n machdep.cpu.brand_string"
|
|
88
|
+
)
|
|
89
|
+
cpuinfo["model_name"] = processes.execute_command_and_return_output(
|
|
90
|
+
"uname -m"
|
|
91
|
+
)
|
|
92
|
+
except Exception as err:
|
|
93
|
+
console.error(f"Failed to retrieve CPU info. {err}")
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
CpuInfo(
|
|
98
|
+
manufacturer_id=cpuinfo.get("manufacturer_id"),
|
|
99
|
+
model_name=cpuinfo.get("model_name"),
|
|
100
|
+
address_width=cpuinfo.get("address_width"),
|
|
101
|
+
)
|
|
102
|
+
if cpuinfo
|
|
103
|
+
else None
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@once
|
|
108
|
+
def get_cpu_info() -> CpuInfo | None:
|
|
109
|
+
"""Get the CPU info of the underlining host.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
The CPU info.
|
|
113
|
+
"""
|
|
114
|
+
cpu_info_file = environment.REFLEX_DIR.get() / "cpu_info.json"
|
|
115
|
+
if cpu_info_file.exists() and (cpu_info := json.loads(cpu_info_file.read_text())):
|
|
116
|
+
return CpuInfo(**cpu_info)
|
|
117
|
+
cpu_info = _retrieve_cpu_info()
|
|
118
|
+
if cpu_info:
|
|
119
|
+
cpu_info_file.parent.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
cpu_info_file.write_text(json.dumps(dataclasses.asdict(cpu_info)))
|
|
121
|
+
return cpu_info
|
|
122
|
+
|
|
123
|
+
|
|
31
124
|
def get_os() -> str:
|
|
32
125
|
"""Get the operating system.
|
|
33
126
|
|
|
@@ -136,8 +229,6 @@ def _get_event_defaults() -> _DefaultEvent | None:
|
|
|
136
229
|
Returns:
|
|
137
230
|
The default event data.
|
|
138
231
|
"""
|
|
139
|
-
from reflex.utils.prerequisites import get_cpu_info
|
|
140
|
-
|
|
141
232
|
installation_id = ensure_reflex_installation_id()
|
|
142
233
|
project_hash = get_project_hash(raise_on_fail=_raise_on_missing_project_hash())
|
|
143
234
|
|