reflex 0.7.0a5__py3-none-any.whl → 0.7.1a2__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 +255 -81
- 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 +40 -9
- reflex/components/core/banner.pyi +400 -87
- 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.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 +41 -12
- reflex/components/sonner/toast.pyi +20 -6
- 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 +30 -41
- reflex/state.py +49 -44
- reflex/style.py +15 -22
- 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 +16 -1
- reflex/utils/prerequisites.py +34 -8
- 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.0a5.dist-info → reflex-0.7.1a2.dist-info}/METADATA +4 -5
- {reflex-0.7.0a5.dist-info → reflex-0.7.1a2.dist-info}/RECORD +126 -122
- {reflex-0.7.0a5.dist-info → reflex-0.7.1a2.dist-info}/LICENSE +0 -0
- {reflex-0.7.0a5.dist-info → reflex-0.7.1a2.dist-info}/WHEEL +0 -0
- {reflex-0.7.0a5.dist-info → reflex-0.7.1a2.dist-info}/entry_points.txt +0 -0
reflex/utils/exec.py
CHANGED
|
@@ -10,6 +10,7 @@ import re
|
|
|
10
10
|
import subprocess
|
|
11
11
|
import sys
|
|
12
12
|
from pathlib import Path
|
|
13
|
+
from typing import Sequence
|
|
13
14
|
from urllib.parse import urljoin
|
|
14
15
|
|
|
15
16
|
import psutil
|
|
@@ -242,29 +243,63 @@ def run_backend(
|
|
|
242
243
|
run_uvicorn_backend(host, port, loglevel)
|
|
243
244
|
|
|
244
245
|
|
|
245
|
-
def
|
|
246
|
-
"""Get the reload
|
|
246
|
+
def get_reload_paths() -> Sequence[Path]:
|
|
247
|
+
"""Get the reload paths for the backend.
|
|
247
248
|
|
|
248
249
|
Returns:
|
|
249
|
-
The reload
|
|
250
|
+
The reload paths for the backend.
|
|
250
251
|
"""
|
|
251
252
|
config = get_config()
|
|
252
|
-
|
|
253
|
+
reload_paths = [Path(config.app_name).parent]
|
|
253
254
|
if config.app_module is not None and config.app_module.__file__:
|
|
254
255
|
module_path = Path(config.app_module.__file__).resolve().parent
|
|
255
256
|
|
|
256
|
-
while module_path.parent.name
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
module_path = module_path.parent
|
|
263
|
-
else:
|
|
264
|
-
break
|
|
257
|
+
while module_path.parent.name and any(
|
|
258
|
+
sibling_file.name == "__init__.py"
|
|
259
|
+
for sibling_file in module_path.parent.iterdir()
|
|
260
|
+
):
|
|
261
|
+
# go up a level to find dir without `__init__.py`
|
|
262
|
+
module_path = module_path.parent
|
|
265
263
|
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
reload_paths = [module_path]
|
|
265
|
+
|
|
266
|
+
include_dirs = tuple(
|
|
267
|
+
map(Path.absolute, environment.REFLEX_HOT_RELOAD_INCLUDE_PATHS.get())
|
|
268
|
+
)
|
|
269
|
+
exclude_dirs = tuple(
|
|
270
|
+
map(Path.absolute, environment.REFLEX_HOT_RELOAD_EXCLUDE_PATHS.get())
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def is_excluded_by_default(path: Path) -> bool:
|
|
274
|
+
if path.is_dir():
|
|
275
|
+
if path.name.startswith("."):
|
|
276
|
+
# exclude hidden directories
|
|
277
|
+
return True
|
|
278
|
+
if path.name.startswith("__"):
|
|
279
|
+
# ignore things like __pycache__
|
|
280
|
+
return True
|
|
281
|
+
return path.name in (".gitignore", "uploaded_files")
|
|
282
|
+
|
|
283
|
+
reload_paths = (
|
|
284
|
+
tuple(
|
|
285
|
+
path.absolute()
|
|
286
|
+
for dir in reload_paths
|
|
287
|
+
for path in dir.iterdir()
|
|
288
|
+
if not is_excluded_by_default(path)
|
|
289
|
+
)
|
|
290
|
+
+ include_dirs
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if exclude_dirs:
|
|
294
|
+
reload_paths = tuple(
|
|
295
|
+
path
|
|
296
|
+
for path in reload_paths
|
|
297
|
+
if all(not path.samefile(exclude) for exclude in exclude_dirs)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
console.debug(f"Reload paths: {list(map(str, reload_paths))}")
|
|
301
|
+
|
|
302
|
+
return reload_paths
|
|
268
303
|
|
|
269
304
|
|
|
270
305
|
def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
|
|
@@ -283,7 +318,7 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
283
318
|
port=port,
|
|
284
319
|
log_level=loglevel.value,
|
|
285
320
|
reload=True,
|
|
286
|
-
reload_dirs=list(map(str,
|
|
321
|
+
reload_dirs=list(map(str, get_reload_paths())),
|
|
287
322
|
)
|
|
288
323
|
|
|
289
324
|
|
|
@@ -310,8 +345,7 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
310
345
|
interface=Interfaces.ASGI,
|
|
311
346
|
log_level=LogLevels(loglevel.value),
|
|
312
347
|
reload=True,
|
|
313
|
-
reload_paths=
|
|
314
|
-
reload_ignore_dirs=[".web", ".states"],
|
|
348
|
+
reload_paths=get_reload_paths(),
|
|
315
349
|
).serve()
|
|
316
350
|
except ImportError:
|
|
317
351
|
console.error(
|
|
@@ -368,34 +402,49 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
368
402
|
|
|
369
403
|
app_module = get_app_module()
|
|
370
404
|
|
|
371
|
-
run_backend_prod = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split()
|
|
372
|
-
run_backend_prod_windows = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split()
|
|
373
405
|
command = (
|
|
374
406
|
[
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
407
|
+
"uvicorn",
|
|
408
|
+
*(
|
|
409
|
+
[
|
|
410
|
+
"--limit-max-requests",
|
|
411
|
+
str(config.gunicorn_max_requests),
|
|
412
|
+
]
|
|
413
|
+
if config.gunicorn_max_requests > 0
|
|
414
|
+
else []
|
|
415
|
+
),
|
|
416
|
+
*("--timeout-keep-alive", str(config.timeout)),
|
|
417
|
+
*("--host", host),
|
|
418
|
+
*("--port", str(port)),
|
|
419
|
+
*("--workers", str(_get_backend_workers())),
|
|
380
420
|
app_module,
|
|
381
421
|
]
|
|
382
422
|
if constants.IS_WINDOWS
|
|
383
423
|
else [
|
|
384
|
-
|
|
385
|
-
"--
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
424
|
+
"gunicorn",
|
|
425
|
+
*("--worker-class", config.gunicorn_worker_class),
|
|
426
|
+
*(
|
|
427
|
+
[
|
|
428
|
+
"--max-requests",
|
|
429
|
+
str(config.gunicorn_max_requests),
|
|
430
|
+
"--max-requests-jitter",
|
|
431
|
+
str(config.gunicorn_max_requests_jitter),
|
|
432
|
+
]
|
|
433
|
+
if config.gunicorn_max_requests > 0
|
|
434
|
+
else []
|
|
435
|
+
),
|
|
436
|
+
"--preload",
|
|
437
|
+
*("--timeout", str(config.timeout)),
|
|
438
|
+
*("--bind", f"{host}:{port}"),
|
|
439
|
+
*("--threads", str(_get_backend_workers())),
|
|
389
440
|
f"{app_module}()",
|
|
390
441
|
]
|
|
391
442
|
)
|
|
392
443
|
|
|
393
444
|
command += [
|
|
394
|
-
"--log-level",
|
|
395
|
-
loglevel.value,
|
|
396
|
-
"--workers",
|
|
397
|
-
str(_get_backend_workers()),
|
|
445
|
+
*("--log-level", loglevel.value),
|
|
398
446
|
]
|
|
447
|
+
|
|
399
448
|
processes.new_process(
|
|
400
449
|
command,
|
|
401
450
|
run=True,
|
|
@@ -535,3 +584,12 @@ def is_prod_mode() -> bool:
|
|
|
535
584
|
"""
|
|
536
585
|
current_mode = environment.REFLEX_ENV_MODE.get()
|
|
537
586
|
return current_mode == constants.Env.PROD
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def get_compile_context() -> constants.CompileContext:
|
|
590
|
+
"""Check if the app is compiled for deploy.
|
|
591
|
+
|
|
592
|
+
Returns:
|
|
593
|
+
Whether the app is being compiled for deploy.
|
|
594
|
+
"""
|
|
595
|
+
return environment.REFLEX_COMPILE_CONTEXT.get()
|
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
|
|
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
|
|
|
@@ -1679,9 +1689,11 @@ def validate_and_create_app_using_remote_template(
|
|
|
1679
1689
|
|
|
1680
1690
|
template_url = templates[template].code_url
|
|
1681
1691
|
else:
|
|
1692
|
+
template_parsed_url = urlparse(template)
|
|
1682
1693
|
# Check if the template is a github repo.
|
|
1683
|
-
if
|
|
1684
|
-
|
|
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"
|
|
1685
1697
|
else:
|
|
1686
1698
|
console.error(f"Template `{template}` not found or invalid.")
|
|
1687
1699
|
raise typer.Exit(1)
|
|
@@ -1998,6 +2010,22 @@ def is_generation_hash(template: str) -> bool:
|
|
|
1998
2010
|
return re.match(r"^[0-9a-f]{32,}$", template) is not None
|
|
1999
2011
|
|
|
2000
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
|
+
|
|
2001
2029
|
def check_config_option_in_tier(
|
|
2002
2030
|
option_name: str,
|
|
2003
2031
|
allowed_tiers: list[str],
|
|
@@ -2012,23 +2040,21 @@ def check_config_option_in_tier(
|
|
|
2012
2040
|
fallback_value: The fallback value if the option is not allowed.
|
|
2013
2041
|
help_link: The help link to show to a user that is authenticated.
|
|
2014
2042
|
"""
|
|
2015
|
-
from reflex_cli.v2.utils import hosting
|
|
2016
|
-
|
|
2017
2043
|
config = get_config()
|
|
2018
|
-
|
|
2019
|
-
|
|
2044
|
+
current_tier = get_user_tier()
|
|
2045
|
+
|
|
2046
|
+
if current_tier == "anonymous":
|
|
2020
2047
|
the_remedy = (
|
|
2021
2048
|
"You are currently logged out. Run `reflex login` to access this option."
|
|
2022
2049
|
)
|
|
2023
|
-
current_tier = "anonymous"
|
|
2024
2050
|
else:
|
|
2025
|
-
current_tier = authenticated_token[1].get("tier", "").lower()
|
|
2026
2051
|
the_remedy = (
|
|
2027
2052
|
f"Your current subscription tier is `{current_tier}`. "
|
|
2028
2053
|
f"Please upgrade to {allowed_tiers} to access this option. "
|
|
2029
2054
|
)
|
|
2030
2055
|
if help_link:
|
|
2031
2056
|
the_remedy += f"See {help_link} for more information."
|
|
2057
|
+
|
|
2032
2058
|
if current_tier not in allowed_tiers:
|
|
2033
2059
|
console.warn(f"Config option `{option_name}` is restricted. {the_remedy}")
|
|
2034
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
|
|