reflex 0.7.0a4__py3-none-any.whl → 0.7.1a1__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/web/components/reflex/radix_themes_color_mode_provider.js +3 -1
- reflex/__init__.py +1 -0
- reflex/__init__.pyi +1 -0
- reflex/app.py +251 -68
- reflex/base.py +4 -10
- reflex/compiler/compiler.py +46 -12
- reflex/compiler/templates.py +1 -2
- reflex/compiler/utils.py +23 -14
- reflex/components/base/bare.py +109 -16
- reflex/components/component.py +179 -124
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +1 -0
- reflex/components/core/auto_scroll.py +111 -0
- reflex/components/core/auto_scroll.pyi +284 -0
- reflex/components/core/banner.py +35 -5
- reflex/components/core/banner.pyi +398 -36
- reflex/components/core/breakpoints.py +1 -1
- reflex/components/core/cond.py +0 -8
- reflex/components/core/foreach.py +12 -2
- reflex/components/core/html.pyi +200 -19
- reflex/components/core/match.py +4 -4
- reflex/components/core/sticky.py +4 -30
- reflex/components/core/sticky.pyi +874 -90
- reflex/components/core/upload.py +3 -5
- reflex/components/core/upload.pyi +2 -4
- reflex/components/datadisplay/code.py +36 -10
- reflex/components/datadisplay/code.pyi +1 -1
- reflex/components/datadisplay/dataeditor.py +1 -3
- reflex/components/datadisplay/dataeditor.pyi +1 -3
- reflex/components/el/elements/base.py +95 -17
- reflex/components/el/elements/base.pyi +278 -19
- reflex/components/el/elements/forms.py +124 -102
- reflex/components/el/elements/forms.pyi +2787 -365
- reflex/components/el/elements/inline.py +24 -15
- reflex/components/el/elements/inline.pyi +5655 -546
- reflex/components/el/elements/media.py +79 -95
- reflex/components/el/elements/media.pyi +5167 -565
- reflex/components/el/elements/metadata.py +19 -17
- reflex/components/el/elements/metadata.pyi +841 -89
- reflex/components/el/elements/other.py +3 -5
- reflex/components/el/elements/other.pyi +1404 -137
- reflex/components/el/elements/scripts.py +10 -13
- reflex/components/el/elements/scripts.pyi +634 -65
- reflex/components/el/elements/sectioning.pyi +3001 -286
- reflex/components/el/elements/tables.py +14 -35
- reflex/components/el/elements/tables.pyi +2029 -218
- reflex/components/el/elements/typography.py +10 -13
- reflex/components/el/elements/typography.pyi +3014 -297
- reflex/components/lucide/icon.py +22 -6
- reflex/components/markdown/markdown.py +30 -10
- reflex/components/markdown/markdown.pyi +3 -2
- reflex/components/plotly/plotly.py +1 -3
- reflex/components/plotly/plotly.pyi +1 -3
- reflex/components/radix/primitives/form.pyi +624 -93
- reflex/components/radix/themes/color_mode.py +1 -1
- reflex/components/radix/themes/color_mode.pyi +213 -31
- reflex/components/radix/themes/components/alert_dialog.pyi +199 -18
- reflex/components/radix/themes/components/badge.pyi +199 -18
- reflex/components/radix/themes/components/button.pyi +213 -31
- reflex/components/radix/themes/components/callout.pyi +1000 -95
- reflex/components/radix/themes/components/card.pyi +199 -18
- reflex/components/radix/themes/components/context_menu.py +79 -1
- reflex/components/radix/themes/components/context_menu.pyi +320 -1
- reflex/components/radix/themes/components/dialog.pyi +199 -18
- reflex/components/radix/themes/components/hover_card.pyi +199 -18
- reflex/components/radix/themes/components/icon_button.pyi +213 -31
- reflex/components/radix/themes/components/inset.pyi +199 -18
- reflex/components/radix/themes/components/popover.pyi +199 -18
- reflex/components/radix/themes/components/table.pyi +1437 -154
- reflex/components/radix/themes/components/text_area.py +2 -2
- reflex/components/radix/themes/components/text_area.pyi +201 -20
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/text_field.pyi +444 -88
- reflex/components/radix/themes/layout/box.pyi +200 -19
- reflex/components/radix/themes/layout/center.pyi +199 -18
- reflex/components/radix/themes/layout/container.pyi +199 -18
- reflex/components/radix/themes/layout/flex.pyi +199 -18
- reflex/components/radix/themes/layout/grid.pyi +199 -18
- reflex/components/radix/themes/layout/list.pyi +604 -57
- reflex/components/radix/themes/layout/section.pyi +199 -18
- reflex/components/radix/themes/layout/spacer.pyi +199 -18
- reflex/components/radix/themes/layout/stack.pyi +597 -54
- reflex/components/radix/themes/typography/blockquote.pyi +200 -19
- reflex/components/radix/themes/typography/code.pyi +199 -18
- reflex/components/radix/themes/typography/heading.pyi +199 -18
- reflex/components/radix/themes/typography/link.pyi +238 -28
- reflex/components/radix/themes/typography/text.pyi +1394 -127
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/react_player/react_player.pyi +1 -3
- reflex/components/sonner/toast.py +19 -1
- reflex/components/sonner/toast.pyi +10 -1
- reflex/components/tags/iter_tag.py +4 -0
- reflex/components/tags/tag.py +3 -3
- reflex/config.py +187 -28
- reflex/constants/__init__.py +2 -0
- reflex/constants/base.py +6 -0
- reflex/constants/compiler.py +9 -0
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +4 -5
- reflex/constants/utils.py +1 -3
- reflex/event.py +7 -16
- reflex/experimental/layout.pyi +597 -54
- reflex/py.typed +0 -0
- reflex/reflex.py +44 -48
- reflex/state.py +49 -44
- reflex/style.py +6 -4
- reflex/testing.py +2 -0
- reflex/utils/build.py +12 -0
- reflex/utils/console.py +4 -0
- reflex/utils/decorator.py +25 -0
- reflex/utils/exec.py +92 -34
- reflex/utils/format.py +35 -6
- reflex/utils/path_ops.py +32 -1
- reflex/utils/prerequisites.py +54 -10
- reflex/utils/processes.py +12 -13
- reflex/utils/serializers.py +20 -43
- reflex/utils/telemetry.py +4 -15
- reflex/utils/types.py +36 -66
- reflex/vars/base.py +53 -76
- reflex/vars/function.py +17 -5
- reflex/vars/number.py +1 -1
- reflex/vars/sequence.py +80 -4
- {reflex-0.7.0a4.dist-info → reflex-0.7.1a1.dist-info}/METADATA +4 -5
- {reflex-0.7.0a4.dist-info → reflex-0.7.1a1.dist-info}/RECORD +127 -123
- {reflex-0.7.0a4.dist-info → reflex-0.7.1a1.dist-info}/LICENSE +0 -0
- {reflex-0.7.0a4.dist-info → reflex-0.7.1a1.dist-info}/WHEEL +0 -0
- {reflex-0.7.0a4.dist-info → reflex-0.7.1a1.dist-info}/entry_points.txt +0 -0
reflex/utils/format.py
CHANGED
|
@@ -27,6 +27,36 @@ WRAP_MAP = {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
def length_of_largest_common_substring(str1: str, str2: str) -> int:
|
|
31
|
+
"""Find the length of the largest common substring between two strings.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
str1: The first string.
|
|
35
|
+
str2: The second string.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
The length of the largest common substring.
|
|
39
|
+
"""
|
|
40
|
+
if not str1 or not str2:
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
# Create a matrix of size (len(str1) + 1) x (len(str2) + 1)
|
|
44
|
+
dp = [[0] * (len(str2) + 1) for _ in range(len(str1) + 1)]
|
|
45
|
+
|
|
46
|
+
# Variables to keep track of maximum length and ending position
|
|
47
|
+
max_length = 0
|
|
48
|
+
|
|
49
|
+
# Fill the dp matrix
|
|
50
|
+
for i in range(1, len(str1) + 1):
|
|
51
|
+
for j in range(1, len(str2) + 1):
|
|
52
|
+
if str1[i - 1] == str2[j - 1]:
|
|
53
|
+
dp[i][j] = dp[i - 1][j - 1] + 1
|
|
54
|
+
if dp[i][j] > max_length:
|
|
55
|
+
max_length = dp[i][j]
|
|
56
|
+
|
|
57
|
+
return max_length
|
|
58
|
+
|
|
59
|
+
|
|
30
60
|
def get_close_char(open: str, close: str | None = None) -> str:
|
|
31
61
|
"""Check if the given character is a valid brace.
|
|
32
62
|
|
|
@@ -138,7 +168,7 @@ def to_snake_case(text: str) -> str:
|
|
|
138
168
|
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower().replace("-", "_")
|
|
139
169
|
|
|
140
170
|
|
|
141
|
-
def to_camel_case(text: str,
|
|
171
|
+
def to_camel_case(text: str, treat_hyphens_as_underscores: bool = True) -> str:
|
|
142
172
|
"""Convert a string to camel case.
|
|
143
173
|
|
|
144
174
|
The first word in the text is converted to lowercase and
|
|
@@ -146,17 +176,16 @@ def to_camel_case(text: str, allow_hyphens: bool = False) -> str:
|
|
|
146
176
|
|
|
147
177
|
Args:
|
|
148
178
|
text: The string to convert.
|
|
149
|
-
|
|
179
|
+
treat_hyphens_as_underscores: Whether to allow hyphens in the string.
|
|
150
180
|
|
|
151
181
|
Returns:
|
|
152
182
|
The camel case string.
|
|
153
183
|
"""
|
|
154
|
-
char = "_" if
|
|
155
|
-
words = re.split(f"[{char}]", text
|
|
156
|
-
leading_underscores_or_hyphens = "".join(re.findall(rf"^[{char}]+", text))
|
|
184
|
+
char = "_" if not treat_hyphens_as_underscores else "-_"
|
|
185
|
+
words = re.split(f"[{char}]", text)
|
|
157
186
|
# Capitalize the first letter of each word except the first one
|
|
158
187
|
converted_word = words[0] + "".join(x.capitalize() for x in words[1:])
|
|
159
|
-
return
|
|
188
|
+
return converted_word
|
|
160
189
|
|
|
161
190
|
|
|
162
191
|
def to_title_case(text: str, sep: str = "") -> str:
|
reflex/utils/path_ops.py
CHANGED
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
8
|
import shutil
|
|
9
|
+
import stat
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
11
12
|
from reflex import constants
|
|
@@ -15,6 +16,19 @@ from reflex.config import environment, get_config
|
|
|
15
16
|
join = os.linesep.join
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
def chmod_rm(path: Path):
|
|
20
|
+
"""Remove a file or directory with chmod.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
path: The path to the file or directory.
|
|
24
|
+
"""
|
|
25
|
+
path.chmod(stat.S_IWRITE)
|
|
26
|
+
if path.is_dir():
|
|
27
|
+
shutil.rmtree(path)
|
|
28
|
+
elif path.is_file():
|
|
29
|
+
path.unlink()
|
|
30
|
+
|
|
31
|
+
|
|
18
32
|
def rm(path: str | Path):
|
|
19
33
|
"""Remove a file or directory.
|
|
20
34
|
|
|
@@ -23,7 +37,8 @@ def rm(path: str | Path):
|
|
|
23
37
|
"""
|
|
24
38
|
path = Path(path)
|
|
25
39
|
if path.is_dir():
|
|
26
|
-
|
|
40
|
+
# In Python 3.12, onerror is deprecated in favor of onexc
|
|
41
|
+
shutil.rmtree(path, onerror=lambda _func, _path, _info: chmod_rm(path))
|
|
27
42
|
elif path.is_file():
|
|
28
43
|
path.unlink()
|
|
29
44
|
|
|
@@ -247,6 +262,22 @@ def find_replace(directory: str | Path, find: str, replace: str):
|
|
|
247
262
|
filepath.write_text(text, encoding="utf-8")
|
|
248
263
|
|
|
249
264
|
|
|
265
|
+
def samefile(file1: Path, file2: Path) -> bool:
|
|
266
|
+
"""Check if two files are the same.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
file1: The first file.
|
|
270
|
+
file2: The second file.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Whether the files are the same. If either file does not exist, returns False.
|
|
274
|
+
"""
|
|
275
|
+
if file1.exists() and file2.exists():
|
|
276
|
+
return file1.samefile(file2)
|
|
277
|
+
|
|
278
|
+
return False
|
|
279
|
+
|
|
280
|
+
|
|
250
281
|
def update_directory_tree(src: Path, dest: Path):
|
|
251
282
|
"""Recursively copies a directory tree from src to dest.
|
|
252
283
|
Only copies files if the destination file is missing or modified earlier than the source file.
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -24,6 +24,7 @@ from datetime import datetime
|
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from types import ModuleType
|
|
26
26
|
from typing import Any, Callable, List, NamedTuple, Optional
|
|
27
|
+
from urllib.parse import urlparse
|
|
27
28
|
|
|
28
29
|
import httpx
|
|
29
30
|
import typer
|
|
@@ -98,6 +99,15 @@ def get_states_dir() -> Path:
|
|
|
98
99
|
return environment.REFLEX_STATES_WORKDIR.get()
|
|
99
100
|
|
|
100
101
|
|
|
102
|
+
def get_backend_dir() -> Path:
|
|
103
|
+
"""Get the working directory for the backend.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
The working directory.
|
|
107
|
+
"""
|
|
108
|
+
return get_web_dir() / constants.Dirs.BACKEND
|
|
109
|
+
|
|
110
|
+
|
|
101
111
|
def check_latest_package_version(package_name: str):
|
|
102
112
|
"""Check if the latest version of the package is installed.
|
|
103
113
|
|
|
@@ -1225,6 +1235,21 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|
|
1225
1235
|
)
|
|
1226
1236
|
|
|
1227
1237
|
|
|
1238
|
+
def check_running_mode(frontend: bool, backend: bool) -> tuple[bool, bool]:
|
|
1239
|
+
"""Check if the app is running in frontend or backend mode.
|
|
1240
|
+
|
|
1241
|
+
Args:
|
|
1242
|
+
frontend: Whether to run the frontend of the app.
|
|
1243
|
+
backend: Whether to run the backend of the app.
|
|
1244
|
+
|
|
1245
|
+
Returns:
|
|
1246
|
+
The running modes.
|
|
1247
|
+
"""
|
|
1248
|
+
if not frontend and not backend:
|
|
1249
|
+
return True, True
|
|
1250
|
+
return frontend, backend
|
|
1251
|
+
|
|
1252
|
+
|
|
1228
1253
|
def needs_reinit(frontend: bool = True) -> bool:
|
|
1229
1254
|
"""Check if an app needs to be reinitialized.
|
|
1230
1255
|
|
|
@@ -1293,10 +1318,13 @@ def validate_bun():
|
|
|
1293
1318
|
"""
|
|
1294
1319
|
bun_path = path_ops.get_bun_path()
|
|
1295
1320
|
|
|
1296
|
-
if bun_path
|
|
1321
|
+
if bun_path is None:
|
|
1322
|
+
return
|
|
1323
|
+
|
|
1324
|
+
if not path_ops.samefile(bun_path, constants.Bun.DEFAULT_PATH):
|
|
1297
1325
|
console.info(f"Using custom Bun path: {bun_path}")
|
|
1298
1326
|
bun_version = get_bun_version()
|
|
1299
|
-
if
|
|
1327
|
+
if bun_version is None:
|
|
1300
1328
|
console.error(
|
|
1301
1329
|
"Failed to obtain bun version. Make sure the specified bun path in your config is correct."
|
|
1302
1330
|
)
|
|
@@ -1661,9 +1689,11 @@ def validate_and_create_app_using_remote_template(
|
|
|
1661
1689
|
|
|
1662
1690
|
template_url = templates[template].code_url
|
|
1663
1691
|
else:
|
|
1692
|
+
template_parsed_url = urlparse(template)
|
|
1664
1693
|
# Check if the template is a github repo.
|
|
1665
|
-
if
|
|
1666
|
-
|
|
1694
|
+
if template_parsed_url.hostname == "github.com":
|
|
1695
|
+
path = template_parsed_url.path.strip("/").removesuffix(".git")
|
|
1696
|
+
template_url = f"https://github.com/{path}/archive/main.zip"
|
|
1667
1697
|
else:
|
|
1668
1698
|
console.error(f"Template `{template}` not found or invalid.")
|
|
1669
1699
|
raise typer.Exit(1)
|
|
@@ -1980,6 +2010,22 @@ def is_generation_hash(template: str) -> bool:
|
|
|
1980
2010
|
return re.match(r"^[0-9a-f]{32,}$", template) is not None
|
|
1981
2011
|
|
|
1982
2012
|
|
|
2013
|
+
def get_user_tier():
|
|
2014
|
+
"""Get the current user's tier.
|
|
2015
|
+
|
|
2016
|
+
Returns:
|
|
2017
|
+
The current user's tier.
|
|
2018
|
+
"""
|
|
2019
|
+
from reflex_cli.v2.utils import hosting
|
|
2020
|
+
|
|
2021
|
+
authenticated_token = hosting.authenticated_token()
|
|
2022
|
+
return (
|
|
2023
|
+
authenticated_token[1].get("tier", "").lower()
|
|
2024
|
+
if authenticated_token[0]
|
|
2025
|
+
else "anonymous"
|
|
2026
|
+
)
|
|
2027
|
+
|
|
2028
|
+
|
|
1983
2029
|
def check_config_option_in_tier(
|
|
1984
2030
|
option_name: str,
|
|
1985
2031
|
allowed_tiers: list[str],
|
|
@@ -1994,23 +2040,21 @@ def check_config_option_in_tier(
|
|
|
1994
2040
|
fallback_value: The fallback value if the option is not allowed.
|
|
1995
2041
|
help_link: The help link to show to a user that is authenticated.
|
|
1996
2042
|
"""
|
|
1997
|
-
from reflex_cli.v2.utils import hosting
|
|
1998
|
-
|
|
1999
2043
|
config = get_config()
|
|
2000
|
-
|
|
2001
|
-
|
|
2044
|
+
current_tier = get_user_tier()
|
|
2045
|
+
|
|
2046
|
+
if current_tier == "anonymous":
|
|
2002
2047
|
the_remedy = (
|
|
2003
2048
|
"You are currently logged out. Run `reflex login` to access this option."
|
|
2004
2049
|
)
|
|
2005
|
-
current_tier = "anonymous"
|
|
2006
2050
|
else:
|
|
2007
|
-
current_tier = authenticated_token[1].get("tier", "").lower()
|
|
2008
2051
|
the_remedy = (
|
|
2009
2052
|
f"Your current subscription tier is `{current_tier}`. "
|
|
2010
2053
|
f"Please upgrade to {allowed_tiers} to access this option. "
|
|
2011
2054
|
)
|
|
2012
2055
|
if help_link:
|
|
2013
2056
|
the_remedy += f"See {help_link} for more information."
|
|
2057
|
+
|
|
2014
2058
|
if current_tier not in allowed_tiers:
|
|
2015
2059
|
console.warn(f"Config option `{option_name}` is restricted. {the_remedy}")
|
|
2016
2060
|
setattr(config, option_name, fallback_value)
|
reflex/utils/processes.py
CHANGED
|
@@ -116,17 +116,14 @@ def change_port(port: int, _type: str) -> int:
|
|
|
116
116
|
return new_port
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
def handle_port(service_name: str, port: int,
|
|
119
|
+
def handle_port(service_name: str, port: int, auto_increment: bool) -> int:
|
|
120
120
|
"""Change port if the specified port is in use and is not explicitly specified as a CLI arg or config arg.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
We make an assumption that when port is the default port,then it hasn't been explicitly set since its not straightforward
|
|
124
|
-
to know whether a port was explicitly provided by the user unless its any other than the default.
|
|
121
|
+
Otherwise tell the user the port is in use and exit the app.
|
|
125
122
|
|
|
126
123
|
Args:
|
|
127
124
|
service_name: The frontend or backend.
|
|
128
125
|
port: The provided port.
|
|
129
|
-
|
|
126
|
+
auto_increment: Whether to automatically increment the port.
|
|
130
127
|
|
|
131
128
|
Returns:
|
|
132
129
|
The port to run the service on.
|
|
@@ -134,13 +131,15 @@ def handle_port(service_name: str, port: int, default_port: int) -> int:
|
|
|
134
131
|
Raises:
|
|
135
132
|
Exit:when the port is in use.
|
|
136
133
|
"""
|
|
137
|
-
if
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
134
|
+
if (process := get_process_on_port(port)) is None:
|
|
135
|
+
return port
|
|
136
|
+
if auto_increment:
|
|
137
|
+
return change_port(port, service_name)
|
|
138
|
+
else:
|
|
139
|
+
console.error(
|
|
140
|
+
f"{service_name.capitalize()} port: {port} is already in use by PID: {process.pid}."
|
|
141
|
+
)
|
|
142
|
+
raise typer.Exit()
|
|
144
143
|
|
|
145
144
|
|
|
146
145
|
def new_process(
|
reflex/utils/serializers.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import contextlib
|
|
5
6
|
import dataclasses
|
|
6
7
|
import functools
|
|
7
8
|
import json
|
|
@@ -24,6 +25,9 @@ from typing import (
|
|
|
24
25
|
overload,
|
|
25
26
|
)
|
|
26
27
|
|
|
28
|
+
from pydantic import BaseModel as BaseModelV2
|
|
29
|
+
from pydantic.v1 import BaseModel as BaseModelV1
|
|
30
|
+
|
|
27
31
|
from reflex.base import Base
|
|
28
32
|
from reflex.constants.colors import Color, format_color
|
|
29
33
|
from reflex.utils import types
|
|
@@ -270,43 +274,24 @@ def serialize_base(value: Base) -> dict:
|
|
|
270
274
|
}
|
|
271
275
|
|
|
272
276
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
@serializer(to=dict)
|
|
277
|
-
def serialize_base_model_v1(model: BaseModelV1) -> dict:
|
|
278
|
-
"""Serialize a pydantic v1 BaseModel instance.
|
|
279
|
-
|
|
280
|
-
Args:
|
|
281
|
-
model: The BaseModel to serialize.
|
|
282
|
-
|
|
283
|
-
Returns:
|
|
284
|
-
The serialized BaseModel.
|
|
285
|
-
"""
|
|
286
|
-
return model.dict()
|
|
287
|
-
|
|
288
|
-
from pydantic import BaseModel as BaseModelV2
|
|
277
|
+
@serializer(to=dict)
|
|
278
|
+
def serialize_base_model_v1(model: BaseModelV1) -> dict:
|
|
279
|
+
"""Serialize a pydantic v1 BaseModel instance.
|
|
289
280
|
|
|
290
|
-
|
|
281
|
+
Args:
|
|
282
|
+
model: The BaseModel to serialize.
|
|
291
283
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
284
|
+
Returns:
|
|
285
|
+
The serialized BaseModel.
|
|
286
|
+
"""
|
|
287
|
+
return model.dict()
|
|
295
288
|
|
|
296
|
-
Args:
|
|
297
|
-
model: The BaseModel to serialize.
|
|
298
289
|
|
|
299
|
-
|
|
300
|
-
The serialized BaseModel.
|
|
301
|
-
"""
|
|
302
|
-
return model.model_dump()
|
|
303
|
-
except ImportError:
|
|
304
|
-
# Older pydantic v1 import
|
|
305
|
-
from pydantic import BaseModel as BaseModelV1
|
|
290
|
+
if BaseModelV1 is not BaseModelV2:
|
|
306
291
|
|
|
307
292
|
@serializer(to=dict)
|
|
308
|
-
def
|
|
309
|
-
"""Serialize a pydantic
|
|
293
|
+
def serialize_base_model_v2(model: BaseModelV2) -> dict:
|
|
294
|
+
"""Serialize a pydantic v2 BaseModel instance.
|
|
310
295
|
|
|
311
296
|
Args:
|
|
312
297
|
model: The BaseModel to serialize.
|
|
@@ -314,7 +299,7 @@ except ImportError:
|
|
|
314
299
|
Returns:
|
|
315
300
|
The serialized BaseModel.
|
|
316
301
|
"""
|
|
317
|
-
return model.
|
|
302
|
+
return model.model_dump()
|
|
318
303
|
|
|
319
304
|
|
|
320
305
|
@serializer
|
|
@@ -382,7 +367,7 @@ def serialize_color(color: Color) -> str:
|
|
|
382
367
|
return format_color(color.color, color.shade, color.alpha)
|
|
383
368
|
|
|
384
369
|
|
|
385
|
-
|
|
370
|
+
with contextlib.suppress(ImportError):
|
|
386
371
|
from pandas import DataFrame
|
|
387
372
|
|
|
388
373
|
def format_dataframe_values(df: DataFrame) -> List[List[Any]]:
|
|
@@ -414,10 +399,8 @@ try:
|
|
|
414
399
|
"data": format_dataframe_values(df),
|
|
415
400
|
}
|
|
416
401
|
|
|
417
|
-
except ImportError:
|
|
418
|
-
pass
|
|
419
402
|
|
|
420
|
-
|
|
403
|
+
with contextlib.suppress(ImportError):
|
|
421
404
|
from plotly.graph_objects import Figure, layout
|
|
422
405
|
from plotly.io import to_json
|
|
423
406
|
|
|
@@ -448,11 +431,8 @@ try:
|
|
|
448
431
|
"layout": json.loads(str(to_json(template.layout))),
|
|
449
432
|
}
|
|
450
433
|
|
|
451
|
-
except ImportError:
|
|
452
|
-
pass
|
|
453
434
|
|
|
454
|
-
|
|
455
|
-
try:
|
|
435
|
+
with contextlib.suppress(ImportError):
|
|
456
436
|
import base64
|
|
457
437
|
import io
|
|
458
438
|
|
|
@@ -489,6 +469,3 @@ try:
|
|
|
489
469
|
mime_type = "image/png"
|
|
490
470
|
|
|
491
471
|
return f"data:{mime_type};base64,{base64_image}"
|
|
492
|
-
|
|
493
|
-
except ImportError:
|
|
494
|
-
pass
|
reflex/utils/telemetry.py
CHANGED
|
@@ -8,23 +8,17 @@ import multiprocessing
|
|
|
8
8
|
import platform
|
|
9
9
|
import warnings
|
|
10
10
|
from contextlib import suppress
|
|
11
|
-
|
|
12
|
-
from reflex.config import environment
|
|
13
|
-
|
|
14
|
-
try:
|
|
15
|
-
from datetime import UTC, datetime
|
|
16
|
-
except ImportError:
|
|
17
|
-
from datetime import datetime
|
|
18
|
-
|
|
19
|
-
UTC = None
|
|
11
|
+
from datetime import datetime, timezone
|
|
20
12
|
|
|
21
13
|
import httpx
|
|
22
14
|
import psutil
|
|
23
15
|
|
|
24
16
|
from reflex import constants
|
|
17
|
+
from reflex.config import environment
|
|
25
18
|
from reflex.utils import console
|
|
26
19
|
from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
|
|
27
20
|
|
|
21
|
+
UTC = timezone.utc
|
|
28
22
|
POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
|
|
29
23
|
|
|
30
24
|
|
|
@@ -121,12 +115,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
|
|
|
121
115
|
)
|
|
122
116
|
return {}
|
|
123
117
|
|
|
124
|
-
|
|
125
|
-
# for python 3.10
|
|
126
|
-
stamp = datetime.utcnow().isoformat()
|
|
127
|
-
else:
|
|
128
|
-
# for python 3.11 & 3.12
|
|
129
|
-
stamp = datetime.now(UTC).isoformat()
|
|
118
|
+
stamp = datetime.now(UTC).isoformat()
|
|
130
119
|
|
|
131
120
|
cpuinfo = get_cpu_info()
|
|
132
121
|
|
reflex/utils/types.py
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import contextlib
|
|
6
5
|
import dataclasses
|
|
7
6
|
import inspect
|
|
8
7
|
import sys
|
|
9
8
|
import types
|
|
10
9
|
from functools import cached_property, lru_cache, wraps
|
|
10
|
+
from types import GenericAlias
|
|
11
11
|
from typing import (
|
|
12
12
|
TYPE_CHECKING,
|
|
13
13
|
Any,
|
|
@@ -25,66 +25,29 @@ from typing import (
|
|
|
25
25
|
Type,
|
|
26
26
|
Union,
|
|
27
27
|
_GenericAlias, # pyright: ignore [reportAttributeAccessIssue]
|
|
28
|
+
_SpecialGenericAlias, # pyright: ignore [reportAttributeAccessIssue]
|
|
28
29
|
get_args,
|
|
29
30
|
get_type_hints,
|
|
30
31
|
)
|
|
31
32
|
from typing import get_origin as get_origin_og
|
|
32
33
|
|
|
33
34
|
import sqlalchemy
|
|
34
|
-
from
|
|
35
|
-
|
|
36
|
-
import reflex
|
|
37
|
-
from reflex.components.core.breakpoints import Breakpoints
|
|
38
|
-
|
|
39
|
-
try:
|
|
40
|
-
from pydantic.v1.fields import ModelField
|
|
41
|
-
except ModuleNotFoundError:
|
|
42
|
-
from pydantic.fields import (
|
|
43
|
-
ModelField, # pyright: ignore [reportAttributeAccessIssue]
|
|
44
|
-
)
|
|
45
|
-
|
|
35
|
+
from pydantic.v1.fields import ModelField
|
|
46
36
|
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
|
|
47
37
|
from sqlalchemy.ext.hybrid import hybrid_property
|
|
48
38
|
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
|
|
39
|
+
from typing_extensions import Self as Self
|
|
40
|
+
from typing_extensions import is_typeddict
|
|
41
|
+
from typing_extensions import override as override
|
|
49
42
|
|
|
43
|
+
import reflex
|
|
50
44
|
from reflex import constants
|
|
51
45
|
from reflex.base import Base
|
|
46
|
+
from reflex.components.core.breakpoints import Breakpoints
|
|
52
47
|
from reflex.utils import console
|
|
53
48
|
|
|
54
|
-
if sys.version_info >= (3, 12):
|
|
55
|
-
from typing import override as override
|
|
56
|
-
else:
|
|
57
|
-
|
|
58
|
-
def override(func: Callable) -> Callable:
|
|
59
|
-
"""Fallback for @override decorator.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
func: The function to decorate.
|
|
63
|
-
|
|
64
|
-
Returns:
|
|
65
|
-
The unmodified function.
|
|
66
|
-
"""
|
|
67
|
-
return func
|
|
68
|
-
|
|
69
|
-
|
|
70
49
|
# Potential GenericAlias types for isinstance checks.
|
|
71
|
-
GenericAliasTypes =
|
|
72
|
-
|
|
73
|
-
with contextlib.suppress(ImportError):
|
|
74
|
-
# For newer versions of Python.
|
|
75
|
-
from types import GenericAlias
|
|
76
|
-
|
|
77
|
-
GenericAliasTypes.append(GenericAlias)
|
|
78
|
-
|
|
79
|
-
with contextlib.suppress(ImportError):
|
|
80
|
-
# For older versions of Python.
|
|
81
|
-
from typing import (
|
|
82
|
-
_SpecialGenericAlias, # pyright: ignore [reportAttributeAccessIssue]
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
GenericAliasTypes.append(_SpecialGenericAlias)
|
|
86
|
-
|
|
87
|
-
GenericAliasTypes = tuple(GenericAliasTypes)
|
|
50
|
+
GenericAliasTypes = (_GenericAlias, GenericAlias, _SpecialGenericAlias)
|
|
88
51
|
|
|
89
52
|
# Potential Union types for isinstance checks (UnionType added in py3.10).
|
|
90
53
|
UnionTypes = (Union, types.UnionType) if hasattr(types, "UnionType") else (Union,)
|
|
@@ -95,6 +58,7 @@ GenericType = Union[Type, _GenericAlias]
|
|
|
95
58
|
# Valid state var types.
|
|
96
59
|
JSONType = {str, int, float, bool}
|
|
97
60
|
PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
|
|
61
|
+
PrimitiveTypes = (int, float, bool, str, list, dict, set, tuple)
|
|
98
62
|
StateVar = Union[PrimitiveType, Base, None]
|
|
99
63
|
StateIterVar = Union[list, set, tuple]
|
|
100
64
|
|
|
@@ -127,11 +91,6 @@ RESERVED_BACKEND_VAR_NAMES = {
|
|
|
127
91
|
"_was_touched",
|
|
128
92
|
}
|
|
129
93
|
|
|
130
|
-
if sys.version_info >= (3, 11):
|
|
131
|
-
from typing import Self as Self
|
|
132
|
-
else:
|
|
133
|
-
from typing_extensions import Self as Self
|
|
134
|
-
|
|
135
94
|
|
|
136
95
|
class Unset:
|
|
137
96
|
"""A class to represent an unset value.
|
|
@@ -551,13 +510,13 @@ def does_obj_satisfy_typed_dict(obj: Any, cls: GenericType) -> bool:
|
|
|
551
510
|
return required_keys.issubset(required_keys)
|
|
552
511
|
|
|
553
512
|
|
|
554
|
-
def _isinstance(obj: Any, cls: GenericType, nested:
|
|
513
|
+
def _isinstance(obj: Any, cls: GenericType, nested: int = 0) -> bool:
|
|
555
514
|
"""Check if an object is an instance of a class.
|
|
556
515
|
|
|
557
516
|
Args:
|
|
558
517
|
obj: The object to check.
|
|
559
518
|
cls: The class to check against.
|
|
560
|
-
nested:
|
|
519
|
+
nested: How many levels deep to check.
|
|
561
520
|
|
|
562
521
|
Returns:
|
|
563
522
|
Whether the object is an instance of the class.
|
|
@@ -565,15 +524,24 @@ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
|
|
565
524
|
if cls is Any:
|
|
566
525
|
return True
|
|
567
526
|
|
|
527
|
+
from reflex.vars import LiteralVar, Var
|
|
528
|
+
|
|
529
|
+
if cls is Var:
|
|
530
|
+
return isinstance(obj, Var)
|
|
531
|
+
if isinstance(obj, LiteralVar):
|
|
532
|
+
return _isinstance(obj._var_value, cls, nested=nested)
|
|
533
|
+
if isinstance(obj, Var):
|
|
534
|
+
return _issubclass(obj._var_type, cls)
|
|
535
|
+
|
|
568
536
|
if cls is None or cls is type(None):
|
|
569
537
|
return obj is None
|
|
570
538
|
|
|
539
|
+
if cls and is_union(cls):
|
|
540
|
+
return any(_isinstance(obj, arg, nested=nested) for arg in get_args(cls))
|
|
541
|
+
|
|
571
542
|
if is_literal(cls):
|
|
572
543
|
return obj in get_args(cls)
|
|
573
544
|
|
|
574
|
-
if is_union(cls):
|
|
575
|
-
return any(_isinstance(obj, arg) for arg in get_args(cls))
|
|
576
|
-
|
|
577
545
|
origin = get_origin(cls)
|
|
578
546
|
|
|
579
547
|
if origin is None:
|
|
@@ -596,38 +564,40 @@ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
|
|
596
564
|
# cls is a simple generic class
|
|
597
565
|
return isinstance(obj, origin)
|
|
598
566
|
|
|
599
|
-
if nested and args:
|
|
567
|
+
if nested > 0 and args:
|
|
600
568
|
if origin is list:
|
|
601
569
|
return isinstance(obj, list) and all(
|
|
602
|
-
_isinstance(item, args[0]) for item in obj
|
|
570
|
+
_isinstance(item, args[0], nested=nested - 1) for item in obj
|
|
603
571
|
)
|
|
604
572
|
if origin is tuple:
|
|
605
573
|
if args[-1] is Ellipsis:
|
|
606
574
|
return isinstance(obj, tuple) and all(
|
|
607
|
-
_isinstance(item, args[0]) for item in obj
|
|
575
|
+
_isinstance(item, args[0], nested=nested - 1) for item in obj
|
|
608
576
|
)
|
|
609
577
|
return (
|
|
610
578
|
isinstance(obj, tuple)
|
|
611
579
|
and len(obj) == len(args)
|
|
612
580
|
and all(
|
|
613
|
-
_isinstance(item, arg
|
|
581
|
+
_isinstance(item, arg, nested=nested - 1)
|
|
582
|
+
for item, arg in zip(obj, args, strict=True)
|
|
614
583
|
)
|
|
615
584
|
)
|
|
616
|
-
if origin in (dict, Breakpoints):
|
|
617
|
-
return isinstance(obj,
|
|
618
|
-
_isinstance(key, args[0]
|
|
585
|
+
if origin in (dict, Mapping, Breakpoints):
|
|
586
|
+
return isinstance(obj, Mapping) and all(
|
|
587
|
+
_isinstance(key, args[0], nested=nested - 1)
|
|
588
|
+
and _isinstance(value, args[1], nested=nested - 1)
|
|
619
589
|
for key, value in obj.items()
|
|
620
590
|
)
|
|
621
591
|
if origin is set:
|
|
622
592
|
return isinstance(obj, set) and all(
|
|
623
|
-
_isinstance(item, args[0]) for item in obj
|
|
593
|
+
_isinstance(item, args[0], nested=nested - 1) for item in obj
|
|
624
594
|
)
|
|
625
595
|
|
|
626
596
|
if args:
|
|
627
597
|
from reflex.vars import Field
|
|
628
598
|
|
|
629
599
|
if origin is Field:
|
|
630
|
-
return _isinstance(obj, args[0])
|
|
600
|
+
return _isinstance(obj, args[0], nested=nested)
|
|
631
601
|
|
|
632
602
|
return isinstance(obj, get_base_class(cls))
|
|
633
603
|
|
|
@@ -749,7 +719,7 @@ def check_prop_in_allowed_types(prop: Any, allowed_types: Iterable) -> bool:
|
|
|
749
719
|
"""
|
|
750
720
|
from reflex.vars import Var
|
|
751
721
|
|
|
752
|
-
type_ = prop._var_type if
|
|
722
|
+
type_ = prop._var_type if isinstance(prop, Var) else type(prop)
|
|
753
723
|
return type_ in allowed_types
|
|
754
724
|
|
|
755
725
|
|