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.

Files changed (42) hide show
  1. reflex/.templates/jinja/web/vite.config.js.jinja2 +4 -1
  2. reflex/.templates/web/app/routes.js +0 -1
  3. reflex/.templates/web/utils/state.js +1 -11
  4. reflex/app.py +27 -23
  5. reflex/components/base/error_boundary.py +2 -0
  6. reflex/components/component.py +6 -4
  7. reflex/components/lucide/icon.py +4 -1
  8. reflex/components/lucide/icon.pyi +4 -1
  9. reflex/components/plotly/plotly.py +9 -9
  10. reflex/components/recharts/recharts.py +2 -2
  11. reflex/components/sonner/toast.py +7 -7
  12. reflex/components/sonner/toast.pyi +8 -8
  13. reflex/config.py +9 -2
  14. reflex/constants/base.py +2 -0
  15. reflex/constants/installer.py +6 -6
  16. reflex/constants/state.py +1 -0
  17. reflex/custom_components/custom_components.py +3 -3
  18. reflex/istate/manager.py +2 -1
  19. reflex/plugins/__init__.py +2 -0
  20. reflex/plugins/_screenshot.py +144 -0
  21. reflex/plugins/base.py +14 -1
  22. reflex/reflex.py +7 -6
  23. reflex/route.py +4 -0
  24. reflex/state.py +2 -2
  25. reflex/testing.py +3 -5
  26. reflex/utils/build.py +21 -3
  27. reflex/utils/exec.py +11 -11
  28. reflex/utils/frontend_skeleton.py +254 -0
  29. reflex/utils/js_runtimes.py +411 -0
  30. reflex/utils/prerequisites.py +17 -1383
  31. reflex/utils/rename.py +170 -0
  32. reflex/utils/telemetry.py +101 -10
  33. reflex/utils/templates.py +443 -0
  34. reflex/vars/base.py +3 -3
  35. {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/METADATA +2 -2
  36. {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/RECORD +39 -37
  37. reflex/.templates/web/utils/client_side_routing.js +0 -45
  38. reflex/components/core/client_side_routing.py +0 -70
  39. reflex/components/core/client_side_routing.pyi +0 -68
  40. {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/WHEEL +0 -0
  41. {reflex-0.8.6a0.dist-info → reflex-0.8.7.dist-info}/entry_points.txt +0 -0
  42. {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.prerequisites import (
21
- ensure_reflex_installation_id,
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