reflex 0.8.14a2__py3-none-any.whl → 0.8.15__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/utils/state.js +68 -8
- reflex/__init__.py +12 -7
- reflex/__init__.pyi +11 -3
- reflex/app.py +10 -7
- reflex/base.py +58 -33
- reflex/components/datadisplay/dataeditor.py +17 -2
- reflex/components/datadisplay/dataeditor.pyi +6 -2
- reflex/components/field.py +3 -1
- reflex/components/lucide/icon.py +2 -1
- reflex/components/lucide/icon.pyi +2 -1
- reflex/components/markdown/markdown.py +101 -27
- reflex/components/sonner/toast.py +3 -2
- reflex/components/sonner/toast.pyi +3 -2
- reflex/constants/base.py +5 -0
- reflex/constants/installer.py +3 -3
- reflex/environment.py +9 -1
- reflex/event.py +3 -0
- reflex/experimental/client_state.py +1 -1
- reflex/istate/manager/__init__.py +120 -0
- reflex/istate/manager/disk.py +210 -0
- reflex/istate/manager/memory.py +76 -0
- reflex/istate/{manager.py → manager/redis.py} +5 -372
- reflex/istate/proxy.py +35 -24
- reflex/model.py +534 -511
- reflex/plugins/tailwind_v4.py +2 -2
- reflex/reflex.py +16 -10
- reflex/state.py +35 -34
- reflex/testing.py +12 -14
- reflex/utils/build.py +11 -1
- reflex/utils/codespaces.py +30 -1
- reflex/utils/compat.py +51 -48
- reflex/utils/misc.py +2 -1
- reflex/utils/monitoring.py +1 -2
- reflex/utils/prerequisites.py +19 -4
- reflex/utils/processes.py +3 -1
- reflex/utils/redir.py +21 -37
- reflex/utils/serializers.py +21 -20
- reflex/utils/telemetry.py +0 -2
- reflex/utils/templates.py +4 -4
- reflex/utils/types.py +89 -90
- reflex/vars/base.py +108 -41
- reflex/vars/color.py +28 -8
- reflex/vars/datetime.py +6 -2
- reflex/vars/dep_tracking.py +2 -2
- reflex/vars/number.py +26 -0
- reflex/vars/object.py +51 -7
- reflex/vars/sequence.py +32 -1
- {reflex-0.8.14a2.dist-info → reflex-0.8.15.dist-info}/METADATA +8 -3
- {reflex-0.8.14a2.dist-info → reflex-0.8.15.dist-info}/RECORD +52 -49
- {reflex-0.8.14a2.dist-info → reflex-0.8.15.dist-info}/WHEEL +0 -0
- {reflex-0.8.14a2.dist-info → reflex-0.8.15.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.14a2.dist-info → reflex-0.8.15.dist-info}/licenses/LICENSE +0 -0
reflex/utils/redir.py
CHANGED
|
@@ -1,59 +1,43 @@
|
|
|
1
1
|
"""Utilities to handle redirection to browser UI."""
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import webbrowser
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
from
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from urllib.parse import SplitResult
|
|
8
7
|
|
|
9
|
-
from . import console
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
def open_browser(target_url: str) -> None:
|
|
9
|
+
def open_browser(target_url: "SplitResult") -> None:
|
|
13
10
|
"""Open a browser window to target_url.
|
|
14
11
|
|
|
15
12
|
Args:
|
|
16
13
|
target_url: The URL to open in the browser.
|
|
17
14
|
"""
|
|
18
|
-
|
|
15
|
+
import webbrowser
|
|
16
|
+
|
|
17
|
+
from reflex.utils import console
|
|
18
|
+
|
|
19
|
+
if not webbrowser.open(target_url.geturl()):
|
|
19
20
|
console.warn(
|
|
20
21
|
f"Unable to automatically open the browser. Please navigate to {target_url} in your browser."
|
|
21
22
|
)
|
|
22
23
|
else:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def open_browser_and_wait(target_url: str, poll_url: str, interval: int = 2):
|
|
27
|
-
"""Open a browser window to target_url and request poll_url until it returns successfully.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
target_url: The URL to open in the browser.
|
|
31
|
-
poll_url: The URL to poll for success.
|
|
32
|
-
interval: The interval in seconds to wait between polling.
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
The response from the poll_url.
|
|
36
|
-
"""
|
|
37
|
-
import httpx
|
|
38
|
-
|
|
39
|
-
open_browser(target_url)
|
|
40
|
-
console.info("[b]Complete the workflow in the browser to continue.[/b]")
|
|
41
|
-
while True:
|
|
42
|
-
try:
|
|
43
|
-
response = net.get(poll_url, follow_redirects=True)
|
|
44
|
-
if response.is_success:
|
|
45
|
-
break
|
|
46
|
-
except httpx.RequestError as err:
|
|
47
|
-
console.info(f"Will retry after error occurred while polling: {err}.")
|
|
48
|
-
time.sleep(interval)
|
|
49
|
-
return response
|
|
24
|
+
simplified_url = target_url._replace(path="", query="", fragment="").geturl()
|
|
25
|
+
console.info(f"Opened browser to {simplified_url}")
|
|
50
26
|
|
|
51
27
|
|
|
52
28
|
def reflex_build_redirect() -> None:
|
|
53
29
|
"""Open the browser window to reflex.build."""
|
|
54
|
-
|
|
30
|
+
from urllib.parse import urlsplit
|
|
31
|
+
|
|
32
|
+
from reflex import constants
|
|
33
|
+
|
|
34
|
+
open_browser(urlsplit(constants.Templates.REFLEX_BUILD_FRONTEND_WITH_REFERRER))
|
|
55
35
|
|
|
56
36
|
|
|
57
37
|
def reflex_templates():
|
|
58
38
|
"""Open the browser window to reflex.build/templates."""
|
|
59
|
-
|
|
39
|
+
from urllib.parse import urlsplit
|
|
40
|
+
|
|
41
|
+
from reflex import constants
|
|
42
|
+
|
|
43
|
+
open_browser(urlsplit(constants.Templates.REFLEX_TEMPLATES_URL))
|
reflex/utils/serializers.py
CHANGED
|
@@ -12,13 +12,11 @@ import warnings
|
|
|
12
12
|
from collections.abc import Callable, Mapping, Sequence
|
|
13
13
|
from datetime import date, datetime, time, timedelta
|
|
14
14
|
from enum import Enum
|
|
15
|
+
from importlib.util import find_spec
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from typing import Any, Literal, TypeVar, get_type_hints, overload
|
|
17
18
|
from uuid import UUID
|
|
18
19
|
|
|
19
|
-
from pydantic import BaseModel as BaseModelV2
|
|
20
|
-
from pydantic.v1 import BaseModel as BaseModelV1
|
|
21
|
-
|
|
22
20
|
from reflex.base import Base
|
|
23
21
|
from reflex.constants.colors import Color
|
|
24
22
|
from reflex.utils import console, types
|
|
@@ -281,24 +279,13 @@ def serialize_base(value: Base) -> dict:
|
|
|
281
279
|
}
|
|
282
280
|
|
|
283
281
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
Args:
|
|
289
|
-
model: The BaseModel to serialize.
|
|
290
|
-
|
|
291
|
-
Returns:
|
|
292
|
-
The serialized BaseModel.
|
|
293
|
-
"""
|
|
294
|
-
return model.dict()
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if BaseModelV1 is not BaseModelV2:
|
|
282
|
+
if find_spec("pydantic"):
|
|
283
|
+
from pydantic import BaseModel as BaseModelV2
|
|
284
|
+
from pydantic.v1 import BaseModel as BaseModelV1
|
|
298
285
|
|
|
299
286
|
@serializer(to=dict)
|
|
300
|
-
def
|
|
301
|
-
"""Serialize a pydantic
|
|
287
|
+
def serialize_base_model_v1(model: BaseModelV1) -> dict:
|
|
288
|
+
"""Serialize a pydantic v1 BaseModel instance.
|
|
302
289
|
|
|
303
290
|
Args:
|
|
304
291
|
model: The BaseModel to serialize.
|
|
@@ -306,7 +293,21 @@ if BaseModelV1 is not BaseModelV2:
|
|
|
306
293
|
Returns:
|
|
307
294
|
The serialized BaseModel.
|
|
308
295
|
"""
|
|
309
|
-
return model.
|
|
296
|
+
return model.dict()
|
|
297
|
+
|
|
298
|
+
if BaseModelV1 is not BaseModelV2:
|
|
299
|
+
|
|
300
|
+
@serializer(to=dict)
|
|
301
|
+
def serialize_base_model_v2(model: BaseModelV2) -> dict:
|
|
302
|
+
"""Serialize a pydantic v2 BaseModel instance.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
model: The BaseModel to serialize.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
The serialized BaseModel.
|
|
309
|
+
"""
|
|
310
|
+
return model.model_dump()
|
|
310
311
|
|
|
311
312
|
|
|
312
313
|
@serializer
|
reflex/utils/telemetry.py
CHANGED
reflex/utils/templates.py
CHANGED
|
@@ -419,13 +419,13 @@ def get_init_cli_prompt_options() -> list[Template]:
|
|
|
419
419
|
"""
|
|
420
420
|
return [
|
|
421
421
|
Template(
|
|
422
|
-
name=constants.Templates.
|
|
423
|
-
description="
|
|
422
|
+
name=constants.Templates.AI,
|
|
423
|
+
description="[bold]Try our free AI builder.",
|
|
424
424
|
code_url="",
|
|
425
425
|
),
|
|
426
426
|
Template(
|
|
427
|
-
name=constants.Templates.
|
|
428
|
-
description="
|
|
427
|
+
name=constants.Templates.DEFAULT,
|
|
428
|
+
description="A blank Reflex app.",
|
|
429
429
|
code_url="",
|
|
430
430
|
),
|
|
431
431
|
Template(
|
reflex/utils/types.py
CHANGED
|
@@ -8,6 +8,7 @@ import types
|
|
|
8
8
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
9
9
|
from enum import Enum
|
|
10
10
|
from functools import cached_property, lru_cache
|
|
11
|
+
from importlib.util import find_spec
|
|
11
12
|
from types import GenericAlias
|
|
12
13
|
from typing import ( # noqa: UP035
|
|
13
14
|
TYPE_CHECKING,
|
|
@@ -33,7 +34,6 @@ from typing import ( # noqa: UP035
|
|
|
33
34
|
from typing import get_origin as get_origin_og
|
|
34
35
|
from typing import get_type_hints as get_type_hints_og
|
|
35
36
|
|
|
36
|
-
from pydantic.v1.fields import ModelField
|
|
37
37
|
from typing_extensions import Self as Self
|
|
38
38
|
from typing_extensions import override as override
|
|
39
39
|
|
|
@@ -291,6 +291,7 @@ def is_literal(cls: GenericType) -> bool:
|
|
|
291
291
|
return getattr(cls, "__origin__", None) is Literal
|
|
292
292
|
|
|
293
293
|
|
|
294
|
+
@lru_cache
|
|
294
295
|
def has_args(cls: type) -> bool:
|
|
295
296
|
"""Check if the class has generic parameters.
|
|
296
297
|
|
|
@@ -346,33 +347,6 @@ def is_classvar(a_type: Any) -> bool:
|
|
|
346
347
|
)
|
|
347
348
|
|
|
348
349
|
|
|
349
|
-
def true_type_for_pydantic_field(f: ModelField):
|
|
350
|
-
"""Get the type for a pydantic field.
|
|
351
|
-
|
|
352
|
-
Args:
|
|
353
|
-
f: The field to get the type for.
|
|
354
|
-
|
|
355
|
-
Returns:
|
|
356
|
-
The type for the field.
|
|
357
|
-
"""
|
|
358
|
-
if not isinstance(f.annotation, (str, ForwardRef)):
|
|
359
|
-
return f.annotation
|
|
360
|
-
|
|
361
|
-
type_ = f.outer_type_
|
|
362
|
-
|
|
363
|
-
if (
|
|
364
|
-
f.field_info.default is None
|
|
365
|
-
or (isinstance(f.annotation, str) and f.annotation.startswith("Optional"))
|
|
366
|
-
or (
|
|
367
|
-
isinstance(f.annotation, ForwardRef)
|
|
368
|
-
and f.annotation.__forward_arg__.startswith("Optional")
|
|
369
|
-
)
|
|
370
|
-
) and not is_optional(type_):
|
|
371
|
-
return type_ | None
|
|
372
|
-
|
|
373
|
-
return type_
|
|
374
|
-
|
|
375
|
-
|
|
376
350
|
def value_inside_optional(cls: GenericType) -> GenericType:
|
|
377
351
|
"""Get the value inside an Optional type or the original type.
|
|
378
352
|
|
|
@@ -412,6 +386,13 @@ def get_field_type(cls: GenericType, field_name: str) -> GenericType | None:
|
|
|
412
386
|
return type_hints.get(field_name, None)
|
|
413
387
|
|
|
414
388
|
|
|
389
|
+
PROPERTY_CLASSES = (property,)
|
|
390
|
+
if find_spec("sqlalchemy") and find_spec("sqlalchemy.ext"):
|
|
391
|
+
from sqlalchemy.ext.hybrid import hybrid_property
|
|
392
|
+
|
|
393
|
+
PROPERTY_CLASSES += (hybrid_property,)
|
|
394
|
+
|
|
395
|
+
|
|
415
396
|
def get_property_hint(attr: Any | None) -> GenericType | None:
|
|
416
397
|
"""Check if an attribute is a property and return its type hint.
|
|
417
398
|
|
|
@@ -421,9 +402,7 @@ def get_property_hint(attr: Any | None) -> GenericType | None:
|
|
|
421
402
|
Returns:
|
|
422
403
|
The type hint of the property, if it is a property, else None.
|
|
423
404
|
"""
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if not isinstance(attr, (property, hybrid_property)):
|
|
405
|
+
if not isinstance(attr, PROPERTY_CLASSES):
|
|
427
406
|
return None
|
|
428
407
|
hints = get_type_hints(attr.fget)
|
|
429
408
|
return hints.get("return", None)
|
|
@@ -441,12 +420,6 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|
|
441
420
|
Returns:
|
|
442
421
|
The type of the attribute, if accessible, or None
|
|
443
422
|
"""
|
|
444
|
-
import sqlalchemy
|
|
445
|
-
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
|
|
446
|
-
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
|
|
447
|
-
|
|
448
|
-
from reflex.model import Model
|
|
449
|
-
|
|
450
423
|
try:
|
|
451
424
|
attr = getattr(cls, name, None)
|
|
452
425
|
except NotImplementedError:
|
|
@@ -458,67 +431,93 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|
|
458
431
|
if hasattr(cls, "__fields__") and name in cls.__fields__:
|
|
459
432
|
# pydantic models
|
|
460
433
|
return get_field_type(cls, name)
|
|
461
|
-
if
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
434
|
+
if find_spec("sqlalchemy") and find_spec("sqlalchemy.orm"):
|
|
435
|
+
import sqlalchemy
|
|
436
|
+
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
|
|
437
|
+
from sqlalchemy.orm import (
|
|
438
|
+
DeclarativeBase,
|
|
439
|
+
Mapped,
|
|
440
|
+
QueryableAttribute,
|
|
441
|
+
Relationship,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
from reflex.model import Model
|
|
445
|
+
|
|
446
|
+
if find_spec("sqlmodel"):
|
|
447
|
+
from sqlmodel import SQLModel
|
|
448
|
+
|
|
449
|
+
sqlmodel_types = (Model, SQLModel)
|
|
450
|
+
else:
|
|
451
|
+
sqlmodel_types = (Model,)
|
|
452
|
+
|
|
453
|
+
if isinstance(cls, type) and issubclass(cls, DeclarativeBase):
|
|
454
|
+
insp = sqlalchemy.inspect(cls)
|
|
455
|
+
if name in insp.columns:
|
|
456
|
+
# check for list types
|
|
457
|
+
column = insp.columns[name]
|
|
458
|
+
column_type = column.type
|
|
459
|
+
try:
|
|
460
|
+
type_ = insp.columns[name].type.python_type
|
|
461
|
+
except NotImplementedError:
|
|
462
|
+
type_ = None
|
|
463
|
+
if type_ is not None:
|
|
464
|
+
if hasattr(column_type, "item_type"):
|
|
465
|
+
try:
|
|
466
|
+
item_type = column_type.item_type.python_type # pyright: ignore [reportAttributeAccessIssue]
|
|
467
|
+
except NotImplementedError:
|
|
468
|
+
item_type = None
|
|
469
|
+
if item_type is not None:
|
|
470
|
+
if type_ in PrimitiveToAnnotation:
|
|
471
|
+
type_ = PrimitiveToAnnotation[type_]
|
|
472
|
+
type_ = type_[item_type] # pyright: ignore [reportIndexIssue]
|
|
473
|
+
if hasattr(column, "nullable") and column.nullable:
|
|
474
|
+
type_ = type_ | None
|
|
475
|
+
return type_
|
|
476
|
+
if name in insp.all_orm_descriptors:
|
|
477
|
+
descriptor = insp.all_orm_descriptors[name]
|
|
478
|
+
if hint := get_property_hint(descriptor):
|
|
479
|
+
return hint
|
|
480
|
+
if isinstance(descriptor, QueryableAttribute):
|
|
481
|
+
prop = descriptor.property
|
|
482
|
+
if isinstance(prop, Relationship):
|
|
483
|
+
type_ = prop.mapper.class_
|
|
484
|
+
# TODO: check for nullable?
|
|
485
|
+
return list[type_] if prop.uselist else type_ | None
|
|
486
|
+
if isinstance(attr, AssociationProxyInstance):
|
|
487
|
+
return list[
|
|
488
|
+
get_attribute_access_type(
|
|
489
|
+
attr.target_class,
|
|
490
|
+
attr.remote_attr.key, # pyright: ignore [reportAttributeAccessIssue]
|
|
491
|
+
)
|
|
492
|
+
]
|
|
493
|
+
elif (
|
|
494
|
+
isinstance(cls, type)
|
|
495
|
+
and not is_generic_alias(cls)
|
|
496
|
+
and issubclass(cls, sqlmodel_types)
|
|
497
|
+
):
|
|
498
|
+
# Check in the annotations directly (for sqlmodel.Relationship)
|
|
499
|
+
hints = get_type_hints(cls) # pyright: ignore [reportArgumentType]
|
|
500
|
+
if name in hints:
|
|
501
|
+
type_ = hints[name]
|
|
502
|
+
type_origin = get_origin(type_)
|
|
503
|
+
if isinstance(type_origin, type) and issubclass(type_origin, Mapped):
|
|
504
|
+
return get_args(type_)[0] # SQLAlchemy v2
|
|
505
|
+
if find_spec("pydantic"):
|
|
506
|
+
from pydantic.v1.fields import ModelField
|
|
507
|
+
|
|
508
|
+
if isinstance(type_, ModelField):
|
|
509
|
+
return type_.type_ # SQLAlchemy v1.4
|
|
483
510
|
return type_
|
|
484
|
-
|
|
485
|
-
descriptor = insp.all_orm_descriptors[name]
|
|
486
|
-
if hint := get_property_hint(descriptor):
|
|
487
|
-
return hint
|
|
488
|
-
if isinstance(descriptor, QueryableAttribute):
|
|
489
|
-
prop = descriptor.property
|
|
490
|
-
if isinstance(prop, Relationship):
|
|
491
|
-
type_ = prop.mapper.class_
|
|
492
|
-
# TODO: check for nullable?
|
|
493
|
-
return list[type_] if prop.uselist else type_ | None
|
|
494
|
-
if isinstance(attr, AssociationProxyInstance):
|
|
495
|
-
return list[
|
|
496
|
-
get_attribute_access_type(
|
|
497
|
-
attr.target_class,
|
|
498
|
-
attr.remote_attr.key, # pyright: ignore [reportAttributeAccessIssue]
|
|
499
|
-
)
|
|
500
|
-
]
|
|
501
|
-
elif isinstance(cls, type) and not is_generic_alias(cls) and issubclass(cls, Model):
|
|
502
|
-
# Check in the annotations directly (for sqlmodel.Relationship)
|
|
503
|
-
hints = get_type_hints(cls)
|
|
504
|
-
if name in hints:
|
|
505
|
-
type_ = hints[name]
|
|
506
|
-
type_origin = get_origin(type_)
|
|
507
|
-
if isinstance(type_origin, type) and issubclass(type_origin, Mapped):
|
|
508
|
-
return get_args(type_)[0] # SQLAlchemy v2
|
|
509
|
-
if isinstance(type_, ModelField):
|
|
510
|
-
return type_.type_ # SQLAlchemy v1.4
|
|
511
|
-
return type_
|
|
512
|
-
elif is_union(cls):
|
|
511
|
+
if is_union(cls):
|
|
513
512
|
# Check in each arg of the annotation.
|
|
514
513
|
return unionize(
|
|
515
514
|
*(get_attribute_access_type(arg, name) for arg in get_args(cls))
|
|
516
515
|
)
|
|
517
|
-
|
|
516
|
+
if isinstance(cls, type):
|
|
518
517
|
# Bare class
|
|
519
518
|
exceptions = NameError
|
|
520
519
|
try:
|
|
521
|
-
hints = get_type_hints(cls)
|
|
520
|
+
hints = get_type_hints(cls) # pyright: ignore [reportArgumentType]
|
|
522
521
|
if name in hints:
|
|
523
522
|
return hints[name]
|
|
524
523
|
except exceptions as e:
|
reflex/vars/base.py
CHANGED
|
@@ -40,10 +40,10 @@ from rich.markup import escape
|
|
|
40
40
|
from typing_extensions import dataclass_transform, override
|
|
41
41
|
|
|
42
42
|
from reflex import constants
|
|
43
|
-
from reflex.base import Base
|
|
44
43
|
from reflex.constants.compiler import Hooks
|
|
45
44
|
from reflex.constants.state import FIELD_MARKER
|
|
46
45
|
from reflex.utils import console, exceptions, imports, serializers, types
|
|
46
|
+
from reflex.utils.compat import annotations_from_namespace
|
|
47
47
|
from reflex.utils.decorator import once
|
|
48
48
|
from reflex.utils.exceptions import (
|
|
49
49
|
ComputedVarSignatureError,
|
|
@@ -1505,21 +1505,6 @@ class LiteralVar(Var):
|
|
|
1505
1505
|
)
|
|
1506
1506
|
return LiteralVar.create(serialized_value, _var_data=_var_data)
|
|
1507
1507
|
|
|
1508
|
-
if isinstance(value, Base):
|
|
1509
|
-
# get the fields of the pydantic class
|
|
1510
|
-
fields = value.__fields__.keys()
|
|
1511
|
-
one_level_dict = {field: getattr(value, field) for field in fields}
|
|
1512
|
-
|
|
1513
|
-
return LiteralObjectVar.create(
|
|
1514
|
-
{
|
|
1515
|
-
field: value
|
|
1516
|
-
for field, value in one_level_dict.items()
|
|
1517
|
-
if not callable(value)
|
|
1518
|
-
},
|
|
1519
|
-
_var_type=type(value),
|
|
1520
|
-
_var_data=_var_data,
|
|
1521
|
-
)
|
|
1522
|
-
|
|
1523
1508
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
1524
1509
|
return LiteralObjectVar.create(
|
|
1525
1510
|
{
|
|
@@ -1542,6 +1527,82 @@ class LiteralVar(Var):
|
|
|
1542
1527
|
def __post_init__(self):
|
|
1543
1528
|
"""Post-initialize the var."""
|
|
1544
1529
|
|
|
1530
|
+
@classmethod
|
|
1531
|
+
def _get_all_var_data_without_creating_var(
|
|
1532
|
+
cls,
|
|
1533
|
+
value: Any,
|
|
1534
|
+
) -> VarData | None:
|
|
1535
|
+
return cls.create(value)._get_all_var_data()
|
|
1536
|
+
|
|
1537
|
+
@classmethod
|
|
1538
|
+
def _get_all_var_data_without_creating_var_dispatch(
|
|
1539
|
+
cls,
|
|
1540
|
+
value: Any,
|
|
1541
|
+
) -> VarData | None:
|
|
1542
|
+
"""Get all the var data without creating a var.
|
|
1543
|
+
|
|
1544
|
+
Args:
|
|
1545
|
+
value: The value to get the var data from.
|
|
1546
|
+
|
|
1547
|
+
Returns:
|
|
1548
|
+
The var data or None.
|
|
1549
|
+
|
|
1550
|
+
Raises:
|
|
1551
|
+
TypeError: If the value is not a supported type for LiteralVar.
|
|
1552
|
+
"""
|
|
1553
|
+
from .object import LiteralObjectVar
|
|
1554
|
+
from .sequence import LiteralStringVar
|
|
1555
|
+
|
|
1556
|
+
if isinstance(value, Var):
|
|
1557
|
+
return value._get_all_var_data()
|
|
1558
|
+
|
|
1559
|
+
for literal_subclass, var_subclass in _var_literal_subclasses[::-1]:
|
|
1560
|
+
if isinstance(value, var_subclass.python_types):
|
|
1561
|
+
return literal_subclass._get_all_var_data_without_creating_var(value)
|
|
1562
|
+
|
|
1563
|
+
if (
|
|
1564
|
+
(as_var_method := getattr(value, "_as_var", None)) is not None
|
|
1565
|
+
and callable(as_var_method)
|
|
1566
|
+
and isinstance((resulting_var := as_var_method()), Var)
|
|
1567
|
+
):
|
|
1568
|
+
return resulting_var._get_all_var_data()
|
|
1569
|
+
|
|
1570
|
+
from reflex.event import EventHandler
|
|
1571
|
+
from reflex.utils.format import get_event_handler_parts
|
|
1572
|
+
|
|
1573
|
+
if isinstance(value, EventHandler):
|
|
1574
|
+
return Var(
|
|
1575
|
+
_js_expr=".".join(filter(None, get_event_handler_parts(value)))
|
|
1576
|
+
)._get_all_var_data()
|
|
1577
|
+
|
|
1578
|
+
serialized_value = serializers.serialize(value)
|
|
1579
|
+
if serialized_value is not None:
|
|
1580
|
+
if isinstance(serialized_value, Mapping):
|
|
1581
|
+
return LiteralObjectVar._get_all_var_data_without_creating_var(
|
|
1582
|
+
serialized_value
|
|
1583
|
+
)
|
|
1584
|
+
if isinstance(serialized_value, str):
|
|
1585
|
+
return LiteralStringVar._get_all_var_data_without_creating_var(
|
|
1586
|
+
serialized_value
|
|
1587
|
+
)
|
|
1588
|
+
return LiteralVar._get_all_var_data_without_creating_var_dispatch(
|
|
1589
|
+
serialized_value
|
|
1590
|
+
)
|
|
1591
|
+
|
|
1592
|
+
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
1593
|
+
return LiteralObjectVar._get_all_var_data_without_creating_var(
|
|
1594
|
+
{
|
|
1595
|
+
k: (None if callable(v) else v)
|
|
1596
|
+
for k, v in dataclasses.asdict(value).items()
|
|
1597
|
+
}
|
|
1598
|
+
)
|
|
1599
|
+
|
|
1600
|
+
if isinstance(value, range):
|
|
1601
|
+
return None
|
|
1602
|
+
|
|
1603
|
+
msg = f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
|
|
1604
|
+
raise TypeError(msg)
|
|
1605
|
+
|
|
1545
1606
|
@property
|
|
1546
1607
|
def _var_value(self) -> Any:
|
|
1547
1608
|
msg = "LiteralVar subclasses must implement the _var_value property."
|
|
@@ -1703,30 +1764,30 @@ def figure_out_type(value: Any) -> types.GenericType:
|
|
|
1703
1764
|
Returns:
|
|
1704
1765
|
The type of the value.
|
|
1705
1766
|
"""
|
|
1706
|
-
if isinstance(value, Var):
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1767
|
+
if isinstance(value, (list, set, tuple, Mapping, Var)):
|
|
1768
|
+
if isinstance(value, Var):
|
|
1769
|
+
return value._var_type
|
|
1770
|
+
if has_args(value_type := type(value)):
|
|
1771
|
+
return value_type
|
|
1772
|
+
if isinstance(value, list):
|
|
1773
|
+
if not value:
|
|
1774
|
+
return Sequence[NoReturn]
|
|
1775
|
+
return Sequence[unionize(*{figure_out_type(v) for v in value[:100]})]
|
|
1776
|
+
if isinstance(value, set):
|
|
1777
|
+
return set[unionize(*{figure_out_type(v) for v in value})]
|
|
1778
|
+
if isinstance(value, tuple):
|
|
1779
|
+
if not value:
|
|
1780
|
+
return tuple[NoReturn, ...]
|
|
1781
|
+
if len(value) <= 5:
|
|
1782
|
+
return tuple[tuple(figure_out_type(v) for v in value)]
|
|
1783
|
+
return tuple[unionize(*{figure_out_type(v) for v in value[:100]}), ...]
|
|
1784
|
+
if isinstance(value, Mapping):
|
|
1785
|
+
if not value:
|
|
1786
|
+
return Mapping[NoReturn, NoReturn]
|
|
1787
|
+
return Mapping[
|
|
1788
|
+
unionize(*{figure_out_type(k) for k in list(value.keys())[:100]}),
|
|
1789
|
+
unionize(*{figure_out_type(v) for v in list(value.values())[:100]}),
|
|
1790
|
+
]
|
|
1730
1791
|
return type(value)
|
|
1731
1792
|
|
|
1732
1793
|
|
|
@@ -2898,6 +2959,10 @@ class LiteralNoneVar(LiteralVar, NoneVar):
|
|
|
2898
2959
|
"""
|
|
2899
2960
|
return "null"
|
|
2900
2961
|
|
|
2962
|
+
@classmethod
|
|
2963
|
+
def _get_all_var_data_without_creating_var(cls, value: None) -> VarData | None:
|
|
2964
|
+
return None
|
|
2965
|
+
|
|
2901
2966
|
@classmethod
|
|
2902
2967
|
def create(
|
|
2903
2968
|
cls,
|
|
@@ -3201,6 +3266,8 @@ if TYPE_CHECKING:
|
|
|
3201
3266
|
from _typeshed import DataclassInstance
|
|
3202
3267
|
from sqlalchemy.orm import DeclarativeBase
|
|
3203
3268
|
|
|
3269
|
+
from reflex.base import Base
|
|
3270
|
+
|
|
3204
3271
|
SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
|
|
3205
3272
|
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
|
|
3206
3273
|
DATACLASS_TYPE = TypeVar("DATACLASS_TYPE", bound=DataclassInstance | None)
|
|
@@ -3492,7 +3559,7 @@ class BaseStateMeta(ABCMeta):
|
|
|
3492
3559
|
inherited_fields: dict[str, Field] = {}
|
|
3493
3560
|
own_fields: dict[str, Field] = {}
|
|
3494
3561
|
resolved_annotations = types.resolve_annotations(
|
|
3495
|
-
namespace
|
|
3562
|
+
annotations_from_namespace(namespace), namespace["__module__"]
|
|
3496
3563
|
)
|
|
3497
3564
|
|
|
3498
3565
|
for base in bases[::-1]:
|
reflex/vars/color.py
CHANGED
|
@@ -29,6 +29,23 @@ class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar):
|
|
|
29
29
|
|
|
30
30
|
_var_value: Color = dataclasses.field(default_factory=lambda: Color(color="black"))
|
|
31
31
|
|
|
32
|
+
@classmethod
|
|
33
|
+
def _get_all_var_data_without_creating_var(
|
|
34
|
+
cls,
|
|
35
|
+
value: Color,
|
|
36
|
+
) -> VarData | None:
|
|
37
|
+
return VarData.merge(
|
|
38
|
+
LiteralStringVar._get_all_var_data_without_creating_var(value.color)
|
|
39
|
+
if isinstance(value.color, str)
|
|
40
|
+
else value.color._get_all_var_data(),
|
|
41
|
+
value.alpha._get_all_var_data()
|
|
42
|
+
if not isinstance(value.alpha, bool)
|
|
43
|
+
else None,
|
|
44
|
+
value.shade._get_all_var_data()
|
|
45
|
+
if not isinstance(value.shade, int)
|
|
46
|
+
else None,
|
|
47
|
+
)
|
|
48
|
+
|
|
32
49
|
@classmethod
|
|
33
50
|
def create(
|
|
34
51
|
cls,
|
|
@@ -111,14 +128,17 @@ class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar):
|
|
|
111
128
|
The var data.
|
|
112
129
|
"""
|
|
113
130
|
return VarData.merge(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
LiteralStringVar._get_all_var_data_without_creating_var(
|
|
132
|
+
self._var_value.color
|
|
133
|
+
)
|
|
134
|
+
if isinstance(self._var_value.color, str)
|
|
135
|
+
else self._var_value.color._get_all_var_data(),
|
|
136
|
+
self._var_value.alpha._get_all_var_data()
|
|
137
|
+
if not isinstance(self._var_value.alpha, bool)
|
|
138
|
+
else None,
|
|
139
|
+
self._var_value.shade._get_all_var_data()
|
|
140
|
+
if not isinstance(self._var_value.shade, int)
|
|
141
|
+
else None,
|
|
122
142
|
self._var_data,
|
|
123
143
|
)
|
|
124
144
|
|