Browse Source

Merge branch 'main' into lendemor/builtins_states

Lendemor 7 months ago
parent
commit
703a6da3d7

+ 25 - 11
poetry.lock

@@ -570,18 +570,18 @@ test = ["pytest (>=6)"]
 
 
 [[package]]
 [[package]]
 name = "fastapi"
 name = "fastapi"
-version = "0.115.2"
+version = "0.115.3"
 description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
 description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
 optional = false
 optional = false
 python-versions = ">=3.8"
 python-versions = ">=3.8"
 files = [
 files = [
-    {file = "fastapi-0.115.2-py3-none-any.whl", hash = "sha256:61704c71286579cc5a598763905928f24ee98bfcc07aabe84cfefb98812bbc86"},
-    {file = "fastapi-0.115.2.tar.gz", hash = "sha256:3995739e0b09fa12f984bce8fa9ae197b35d433750d3d312422d846e283697ee"},
+    {file = "fastapi-0.115.3-py3-none-any.whl", hash = "sha256:8035e8f9a2b0aa89cea03b6c77721178ed5358e1aea4cd8570d9466895c0638c"},
+    {file = "fastapi-0.115.3.tar.gz", hash = "sha256:c091c6a35599c036d676fa24bd4a6e19fa30058d93d950216cdc672881f6f7db"},
 ]
 ]
 
 
 [package.dependencies]
 [package.dependencies]
 pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
 pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
-starlette = ">=0.37.2,<0.41.0"
+starlette = ">=0.40.0,<0.42.0"
 typing-extensions = ">=4.8.0"
 typing-extensions = ">=4.8.0"
 
 
 [package.extras]
 [package.extras]
@@ -1977,6 +1977,20 @@ files = [
 [package.dependencies]
 [package.dependencies]
 six = ">=1.5"
 six = ">=1.5"
 
 
+[[package]]
+name = "python-dotenv"
+version = "1.0.1"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
+    {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
+]
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
 [[package]]
 [[package]]
 name = "python-engineio"
 name = "python-engineio"
 version = "4.10.1"
 version = "4.10.1"
@@ -2253,13 +2267,13 @@ idna2008 = ["idna"]
 
 
 [[package]]
 [[package]]
 name = "rich"
 name = "rich"
-version = "13.9.2"
+version = "13.9.3"
 description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
 description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
 optional = false
 optional = false
 python-versions = ">=3.8.0"
 python-versions = ">=3.8.0"
 files = [
 files = [
-    {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"},
-    {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"},
+    {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"},
+    {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"},
 ]
 ]
 
 
 [package.dependencies]
 [package.dependencies]
@@ -2525,13 +2539,13 @@ SQLAlchemy = ">=2.0.14,<2.1.0"
 
 
 [[package]]
 [[package]]
 name = "starlette"
 name = "starlette"
-version = "0.40.0"
+version = "0.41.0"
 description = "The little ASGI library that shines."
 description = "The little ASGI library that shines."
 optional = false
 optional = false
 python-versions = ">=3.8"
 python-versions = ">=3.8"
 files = [
 files = [
-    {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"},
-    {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"},
+    {file = "starlette-0.41.0-py3-none-any.whl", hash = "sha256:a0193a3c413ebc9c78bff1c3546a45bb8c8bcb4a84cae8747d650a65bd37210a"},
+    {file = "starlette-0.41.0.tar.gz", hash = "sha256:39cbd8768b107d68bfe1ff1672b38a2c38b49777de46d2a592841d58e3bf7c2a"},
 ]
 ]
 
 
 [package.dependencies]
 [package.dependencies]
@@ -3033,4 +3047,4 @@ type = ["pytest-mypy"]
 [metadata]
 [metadata]
 lock-version = "2.0"
 lock-version = "2.0"
 python-versions = "^3.9"
 python-versions = "^3.9"
-content-hash = "8090ccaeca173bd8612e17a0b8d157d7492618e49450abd1c8373e2976349db0"
+content-hash = "c5da15520cef58124f6699007c81158036840469d4f9972592d72bd456c45e7e"

+ 1 - 0
pyproject.toml

@@ -33,6 +33,7 @@ jinja2 = ">=3.1.2,<4.0"
 psutil = ">=5.9.4,<7.0"
 psutil = ">=5.9.4,<7.0"
 pydantic = ">=1.10.2,<3.0"
 pydantic = ">=1.10.2,<3.0"
 python-multipart = ">=0.0.5,<0.1"
 python-multipart = ">=0.0.5,<0.1"
+python-dotenv = ">=1.0.1"
 python-socketio = ">=5.7.0,<6.0"
 python-socketio = ">=5.7.0,<6.0"
 redis = ">=4.3.5,<6.0"
 redis = ">=4.3.5,<6.0"
 rich = ">=13.0.0,<14.0"
 rich = ">=13.0.0,<14.0"

+ 4 - 4
reflex/__init__.py

@@ -321,14 +321,14 @@ _MAPPING: dict = {
         "window_alert",
         "window_alert",
     ],
     ],
     "istate.builtins": ["ComponentState", "State"],
     "istate.builtins": ["ComponentState", "State"],
-    "middleware": ["middleware", "Middleware"],
-    "model": ["session", "Model"],
-    "state": [
-        "var",
+    "istate.storage": [
         "Cookie",
         "Cookie",
         "LocalStorage",
         "LocalStorage",
         "SessionStorage",
         "SessionStorage",
     ],
     ],
+    "middleware": ["middleware", "Middleware"],
+    "model": ["session", "Model"],
+    "state": ["var"],
     "style": ["Style", "toggle_color_mode"],
     "style": ["Style", "toggle_color_mode"],
     "utils.imports": ["ImportVar"],
     "utils.imports": ["ImportVar"],
     "utils.serializers": ["serializer"],
     "utils.serializers": ["serializer"],

+ 3 - 3
reflex/__init__.pyi

@@ -176,14 +176,14 @@ from .event import window_alert as window_alert
 from .experimental import _x as _x
 from .experimental import _x as _x
 from .istate.builtins import ComponentState as ComponentState
 from .istate.builtins import ComponentState as ComponentState
 from .istate.builtins import State as State
 from .istate.builtins import State as State
+from .istate.storage import Cookie as Cookie
+from .istate.storage import LocalStorage as LocalStorage
+from .istate.storage import SessionStorage as SessionStorage
 from .middleware import Middleware as Middleware
 from .middleware import Middleware as Middleware
 from .middleware import middleware as middleware
 from .middleware import middleware as middleware
 from .model import Model as Model
 from .model import Model as Model
 from .model import session as session
 from .model import session as session
 from .page import page as page
 from .page import page as page
-from .state import Cookie as Cookie
-from .state import LocalStorage as LocalStorage
-from .state import SessionStorage as SessionStorage
 from .state import var as var
 from .state import var as var
 from .style import Style as Style
 from .style import Style as Style
 from .style import toggle_color_mode as toggle_color_mode
 from .style import toggle_color_mode as toggle_color_mode

+ 2 - 1
reflex/compiler/utils.py

@@ -28,7 +28,8 @@ from reflex.components.base import (
     Title,
     Title,
 )
 )
 from reflex.components.component import Component, ComponentStyle, CustomComponent
 from reflex.components.component import Component, ComponentStyle, CustomComponent
-from reflex.state import BaseState, Cookie, LocalStorage, SessionStorage
+from reflex.istate.storage import Cookie, LocalStorage, SessionStorage
+from reflex.state import BaseState
 from reflex.style import Style
 from reflex.style import Style
 from reflex.utils import console, format, imports, path_ops
 from reflex.utils import console, format, imports, path_ops
 from reflex.utils.imports import ImportVar, ParsedImportDict
 from reflex.utils.imports import ImportVar, ParsedImportDict

+ 8 - 19
reflex/components/datadisplay/dataeditor.py

@@ -109,19 +109,6 @@ class DataEditorTheme(Base):
     text_medium: Optional[str] = None
     text_medium: Optional[str] = None
 
 
 
 
-def on_edit_spec(pos, data: dict[str, Any]):
-    """The on edit spec function.
-
-    Args:
-        pos: The position of the edit event.
-        data: The data of the edit event.
-
-    Returns:
-        The position and data.
-    """
-    return [pos, data]
-
-
 class Bounds(TypedDict):
 class Bounds(TypedDict):
     """The bounds of the group header."""
     """The bounds of the group header."""
 
 
@@ -149,7 +136,7 @@ class Rectangle(TypedDict):
 class GridSelectionCurrent(TypedDict):
 class GridSelectionCurrent(TypedDict):
     """The current selection."""
     """The current selection."""
 
 
-    cell: list[int]
+    cell: tuple[int, int]
     range: Rectangle
     range: Rectangle
     rangeStack: list[Rectangle]
     rangeStack: list[Rectangle]
 
 
@@ -167,7 +154,7 @@ class GroupHeaderClickedEventArgs(TypedDict):
 
 
     kind: str
     kind: str
     group: str
     group: str
-    location: list[int]
+    location: tuple[int, int]
     bounds: Bounds
     bounds: Bounds
     isEdge: bool
     isEdge: bool
     shiftKey: bool
     shiftKey: bool
@@ -178,7 +165,7 @@ class GroupHeaderClickedEventArgs(TypedDict):
     localEventY: int
     localEventY: int
     button: int
     button: int
     buttons: int
     buttons: int
-    scrollEdge: list[int]
+    scrollEdge: tuple[int, int]
 
 
 
 
 class GridCell(TypedDict):
 class GridCell(TypedDict):
@@ -306,10 +293,10 @@ class DataEditor(NoSSRComponent):
     on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])]
     on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])]
 
 
     # Fired when a cell is edited.
     # Fired when a cell is edited.
-    on_cell_edited: EventHandler[on_edit_spec]
+    on_cell_edited: EventHandler[identity_event(Tuple[int, int], GridCell)]
 
 
     # Fired when a group header is clicked.
     # Fired when a group header is clicked.
-    on_group_header_clicked: EventHandler[on_edit_spec]
+    on_group_header_clicked: EventHandler[identity_event(Tuple[int, int], GridCell)]
 
 
     # Fired when a group header is right-clicked.
     # Fired when a group header is right-clicked.
     on_group_header_context_menu: EventHandler[
     on_group_header_context_menu: EventHandler[
@@ -335,7 +322,9 @@ class DataEditor(NoSSRComponent):
     on_delete: EventHandler[identity_event(GridSelection)]
     on_delete: EventHandler[identity_event(GridSelection)]
 
 
     # Fired when editing is finished.
     # Fired when editing is finished.
-    on_finished_editing: EventHandler[identity_event(Union[GridCell, None], list[int])]
+    on_finished_editing: EventHandler[
+        identity_event(Union[GridCell, None], tuple[int, int])
+    ]
 
 
     # Fired when a row is appended.
     # Fired when a row is appended.
     on_row_appended: EventHandler[empty_event]
     on_row_appended: EventHandler[empty_event]

+ 6 - 8
reflex/components/datadisplay/dataeditor.pyi

@@ -78,8 +78,6 @@ class DataEditorTheme(Base):
     text_light: Optional[str]
     text_light: Optional[str]
     text_medium: Optional[str]
     text_medium: Optional[str]
 
 
-def on_edit_spec(pos, data: dict[str, Any]): ...
-
 class Bounds(TypedDict):
 class Bounds(TypedDict):
     x: int
     x: int
     y: int
     y: int
@@ -96,7 +94,7 @@ class Rectangle(TypedDict):
     height: int
     height: int
 
 
 class GridSelectionCurrent(TypedDict):
 class GridSelectionCurrent(TypedDict):
-    cell: list[int]
+    cell: tuple[int, int]
     range: Rectangle
     range: Rectangle
     rangeStack: list[Rectangle]
     rangeStack: list[Rectangle]
 
 
@@ -108,7 +106,7 @@ class GridSelection(TypedDict):
 class GroupHeaderClickedEventArgs(TypedDict):
 class GroupHeaderClickedEventArgs(TypedDict):
     kind: str
     kind: str
     group: str
     group: str
-    location: list[int]
+    location: tuple[int, int]
     bounds: Bounds
     bounds: Bounds
     isEdge: bool
     isEdge: bool
     shiftKey: bool
     shiftKey: bool
@@ -119,7 +117,7 @@ class GroupHeaderClickedEventArgs(TypedDict):
     localEventY: int
     localEventY: int
     button: int
     button: int
     buttons: int
     buttons: int
-    scrollEdge: list[int]
+    scrollEdge: tuple[int, int]
 
 
 class GridCell(TypedDict):
 class GridCell(TypedDict):
     span: Optional[List[int]]
     span: Optional[List[int]]
@@ -189,17 +187,17 @@ class DataEditor(NoSSRComponent):
         on_cell_activated: Optional[EventType[tuple[int, int]]] = None,
         on_cell_activated: Optional[EventType[tuple[int, int]]] = None,
         on_cell_clicked: Optional[EventType[tuple[int, int]]] = None,
         on_cell_clicked: Optional[EventType[tuple[int, int]]] = None,
         on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None,
         on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None,
-        on_cell_edited: Optional[EventType] = None,
+        on_cell_edited: Optional[EventType[tuple[int, int], GridCell]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_click: Optional[EventType[[]]] = None,
         on_column_resize: Optional[EventType[GridColumn, int]] = None,
         on_column_resize: Optional[EventType[GridColumn, int]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_context_menu: Optional[EventType[[]]] = None,
         on_delete: Optional[EventType[GridSelection]] = None,
         on_delete: Optional[EventType[GridSelection]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_double_click: Optional[EventType[[]]] = None,
         on_finished_editing: Optional[
         on_finished_editing: Optional[
-            EventType[Union[GridCell, None], list[int]]
+            EventType[Union[GridCell, None], tuple[int, int]]
         ] = None,
         ] = None,
         on_focus: Optional[EventType[[]]] = None,
         on_focus: Optional[EventType[[]]] = None,
-        on_group_header_clicked: Optional[EventType] = None,
+        on_group_header_clicked: Optional[EventType[tuple[int, int], GridCell]] = None,
         on_group_header_context_menu: Optional[
         on_group_header_context_menu: Optional[
             EventType[int, GroupHeaderClickedEventArgs]
             EventType[int, GroupHeaderClickedEventArgs]
         ] = None,
         ] = None,

+ 3 - 1
reflex/components/radix/themes/components/tooltip.pyi

@@ -5,7 +5,9 @@
 # ------------------------------------------------------
 # ------------------------------------------------------
 from typing import Any, Dict, Literal, Optional, Union, overload
 from typing import Any, Dict, Literal, Optional, Union, overload
 
 
-from reflex.event import EventType
+from reflex.event import (
+    EventType,
+)
 from reflex.style import Style
 from reflex.style import Style
 from reflex.vars.base import Var
 from reflex.vars.base import Var
 
 

+ 1 - 0
reflex/components/react_player/__init__.py

@@ -1,5 +1,6 @@
 """React Player component for audio and video."""
 """React Player component for audio and video."""
 
 
+from . import react_player
 from .audio import Audio
 from .audio import Audio
 from .video import Video
 from .video import Video
 
 

+ 4 - 1
reflex/components/react_player/audio.pyi

@@ -5,6 +5,7 @@
 # ------------------------------------------------------
 # ------------------------------------------------------
 from typing import Any, Dict, Optional, Union, overload
 from typing import Any, Dict, Optional, Union, overload
 
 
+import reflex
 from reflex.components.react_player.react_player import ReactPlayer
 from reflex.components.react_player.react_player import ReactPlayer
 from reflex.event import EventType
 from reflex.event import EventType
 from reflex.style import Style
 from reflex.style import Style
@@ -58,7 +59,9 @@ class Audio(ReactPlayer):
         on_play: Optional[EventType[[]]] = None,
         on_play: Optional[EventType[[]]] = None,
         on_playback_quality_change: Optional[EventType[[]]] = None,
         on_playback_quality_change: Optional[EventType[[]]] = None,
         on_playback_rate_change: Optional[EventType[[]]] = None,
         on_playback_rate_change: Optional[EventType[[]]] = None,
-        on_progress: Optional[EventType] = None,
+        on_progress: Optional[
+            EventType[reflex.components.react_player.react_player.Progress]
+        ] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_seek: Optional[EventType[float]] = None,
         on_seek: Optional[EventType[float]] = None,

+ 12 - 1
reflex/components/react_player/react_player.py

@@ -2,11 +2,22 @@
 
 
 from __future__ import annotations
 from __future__ import annotations
 
 
+from typing_extensions import TypedDict
+
 from reflex.components.component import NoSSRComponent
 from reflex.components.component import NoSSRComponent
 from reflex.event import EventHandler, empty_event, identity_event
 from reflex.event import EventHandler, empty_event, identity_event
 from reflex.vars.base import Var
 from reflex.vars.base import Var
 
 
 
 
+class Progress(TypedDict):
+    """Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds."""
+
+    played: float
+    playedSeconds: float
+    loaded: float
+    loadedSeconds: float
+
+
 class ReactPlayer(NoSSRComponent):
 class ReactPlayer(NoSSRComponent):
     """Using react-player and not implement all props and callback yet.
     """Using react-player and not implement all props and callback yet.
     reference: https://github.com/cookpete/react-player.
     reference: https://github.com/cookpete/react-player.
@@ -55,7 +66,7 @@ class ReactPlayer(NoSSRComponent):
     on_play: EventHandler[empty_event]
     on_play: EventHandler[empty_event]
 
 
     # Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
     # Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
-    on_progress: EventHandler[lambda progress: [progress]]
+    on_progress: EventHandler[identity_event(Progress)]
 
 
     # Callback containing duration of the media, in seconds.
     # Callback containing duration of the media, in seconds.
     on_duration: EventHandler[identity_event(float)]
     on_duration: EventHandler[identity_event(float)]

+ 9 - 1
reflex/components/react_player/react_player.pyi

@@ -5,11 +5,19 @@
 # ------------------------------------------------------
 # ------------------------------------------------------
 from typing import Any, Dict, Optional, Union, overload
 from typing import Any, Dict, Optional, Union, overload
 
 
+from typing_extensions import TypedDict
+
 from reflex.components.component import NoSSRComponent
 from reflex.components.component import NoSSRComponent
 from reflex.event import EventType
 from reflex.event import EventType
 from reflex.style import Style
 from reflex.style import Style
 from reflex.vars.base import Var
 from reflex.vars.base import Var
 
 
+class Progress(TypedDict):
+    played: float
+    playedSeconds: float
+    loaded: float
+    loadedSeconds: float
+
 class ReactPlayer(NoSSRComponent):
 class ReactPlayer(NoSSRComponent):
     @overload
     @overload
     @classmethod
     @classmethod
@@ -56,7 +64,7 @@ class ReactPlayer(NoSSRComponent):
         on_play: Optional[EventType[[]]] = None,
         on_play: Optional[EventType[[]]] = None,
         on_playback_quality_change: Optional[EventType[[]]] = None,
         on_playback_quality_change: Optional[EventType[[]]] = None,
         on_playback_rate_change: Optional[EventType[[]]] = None,
         on_playback_rate_change: Optional[EventType[[]]] = None,
-        on_progress: Optional[EventType] = None,
+        on_progress: Optional[EventType[Progress]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_seek: Optional[EventType[float]] = None,
         on_seek: Optional[EventType[float]] = None,

+ 4 - 1
reflex/components/react_player/video.pyi

@@ -5,6 +5,7 @@
 # ------------------------------------------------------
 # ------------------------------------------------------
 from typing import Any, Dict, Optional, Union, overload
 from typing import Any, Dict, Optional, Union, overload
 
 
+import reflex
 from reflex.components.react_player.react_player import ReactPlayer
 from reflex.components.react_player.react_player import ReactPlayer
 from reflex.event import EventType
 from reflex.event import EventType
 from reflex.style import Style
 from reflex.style import Style
@@ -58,7 +59,9 @@ class Video(ReactPlayer):
         on_play: Optional[EventType[[]]] = None,
         on_play: Optional[EventType[[]]] = None,
         on_playback_quality_change: Optional[EventType[[]]] = None,
         on_playback_quality_change: Optional[EventType[[]]] = None,
         on_playback_rate_change: Optional[EventType[[]]] = None,
         on_playback_rate_change: Optional[EventType[[]]] = None,
-        on_progress: Optional[EventType] = None,
+        on_progress: Optional[
+            EventType[reflex.components.react_player.react_player.Progress]
+        ] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_ready: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_scroll: Optional[EventType[[]]] = None,
         on_seek: Optional[EventType[float]] = None,
         on_seek: Optional[EventType[float]] = None,

+ 10 - 0
reflex/config.py

@@ -436,6 +436,9 @@ class Config(Base):
     # Attributes that were explicitly set by the user.
     # Attributes that were explicitly set by the user.
     _non_default_attributes: Set[str] = pydantic.PrivateAttr(set())
     _non_default_attributes: Set[str] = pydantic.PrivateAttr(set())
 
 
+    # Path to file containing key-values pairs to override in the environment; Dotenv format.
+    env_file: Optional[str] = None
+
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         """Initialize the config values.
         """Initialize the config values.
 
 
@@ -477,6 +480,7 @@ class Config(Base):
 
 
     def update_from_env(self) -> dict[str, Any]:
     def update_from_env(self) -> dict[str, Any]:
         """Update the config values based on set environment variables.
         """Update the config values based on set environment variables.
+        If there is a set env_file, it is loaded first.
 
 
         Returns:
         Returns:
             The updated config values.
             The updated config values.
@@ -486,6 +490,12 @@ class Config(Base):
         """
         """
         from reflex.utils.exceptions import EnvVarValueError
         from reflex.utils.exceptions import EnvVarValueError
 
 
+        if self.env_file:
+            from dotenv import load_dotenv
+
+            # load env file if exists
+            load_dotenv(self.env_file, override=True)
+
         updated_values = {}
         updated_values = {}
         # Iterate over the fields.
         # Iterate over the fields.
         for key, field in self.__fields__.items():
         for key, field in self.__fields__.items():

+ 144 - 0
reflex/istate/storage.py

@@ -0,0 +1,144 @@
+"""Client-side storage classes for reflex state variables."""
+
+from __future__ import annotations
+
+from typing import Any
+
+from reflex.utils import format
+
+
+class ClientStorageBase:
+    """Base class for client-side storage."""
+
+    def options(self) -> dict[str, Any]:
+        """Get the options for the storage.
+
+        Returns:
+            All set options for the storage (not None).
+        """
+        return {
+            format.to_camel_case(k): v for k, v in vars(self).items() if v is not None
+        }
+
+
+class Cookie(ClientStorageBase, str):
+    """Represents a state Var that is stored as a cookie in the browser."""
+
+    name: str | None
+    path: str
+    max_age: int | None
+    domain: str | None
+    secure: bool | None
+    same_site: str
+
+    def __new__(
+        cls,
+        object: Any = "",
+        encoding: str | None = None,
+        errors: str | None = None,
+        /,
+        name: str | None = None,
+        path: str = "/",
+        max_age: int | None = None,
+        domain: str | None = None,
+        secure: bool | None = None,
+        same_site: str = "lax",
+    ):
+        """Create a client-side Cookie (str).
+
+        Args:
+            object: The initial object.
+            encoding: The encoding to use.
+            errors: The error handling scheme to use.
+            name: The name of the cookie on the client side.
+            path: Cookie path. Use / as the path if the cookie should be accessible on all pages.
+            max_age: Relative max age of the cookie in seconds from when the client receives it.
+            domain: Domain for the cookie (sub.domain.com or .allsubdomains.com).
+            secure: Is the cookie only accessible through HTTPS?
+            same_site: Whether the cookie is sent with third party requests.
+                One of (true|false|none|lax|strict)
+
+        Returns:
+            The client-side Cookie object.
+
+        Note: expires (absolute Date) is not supported at this time.
+        """
+        if encoding or errors:
+            inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict")
+        else:
+            inst = super().__new__(cls, object)
+        inst.name = name
+        inst.path = path
+        inst.max_age = max_age
+        inst.domain = domain
+        inst.secure = secure
+        inst.same_site = same_site
+        return inst
+
+
+class LocalStorage(ClientStorageBase, str):
+    """Represents a state Var that is stored in localStorage in the browser."""
+
+    name: str | None
+    sync: bool = False
+
+    def __new__(
+        cls,
+        object: Any = "",
+        encoding: str | None = None,
+        errors: str | None = None,
+        /,
+        name: str | None = None,
+        sync: bool = False,
+    ) -> "LocalStorage":
+        """Create a client-side localStorage (str).
+
+        Args:
+            object: The initial object.
+            encoding: The encoding to use.
+            errors: The error handling scheme to use.
+            name: The name of the storage key on the client side.
+            sync: Whether changes should be propagated to other tabs.
+
+        Returns:
+            The client-side localStorage object.
+        """
+        if encoding or errors:
+            inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict")
+        else:
+            inst = super().__new__(cls, object)
+        inst.name = name
+        inst.sync = sync
+        return inst
+
+
+class SessionStorage(ClientStorageBase, str):
+    """Represents a state Var that is stored in sessionStorage in the browser."""
+
+    name: str | None
+
+    def __new__(
+        cls,
+        object: Any = "",
+        encoding: str | None = None,
+        errors: str | None = None,
+        /,
+        name: str | None = None,
+    ) -> "SessionStorage":
+        """Create a client-side sessionStorage (str).
+
+        Args:
+            object: The initial object.
+            encoding: The encoding to use.
+            errors: The error handling scheme to use
+            name: The name of the storage on the client side
+
+        Returns:
+            The client-side sessionStorage object.
+        """
+        if encoding or errors:
+            inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict")
+        else:
+            inst = super().__new__(cls, object)
+        inst.name = name
+        return inst

+ 3 - 137
reflex/state.py

@@ -41,6 +41,9 @@ from typing_extensions import Self
 
 
 from reflex.config import get_config
 from reflex.config import get_config
 from reflex.istate.data import RouterData
 from reflex.istate.data import RouterData
+from reflex.istate.storage import (
+    ClientStorageBase,
+)
 from reflex.vars.base import (
 from reflex.vars.base import (
     ComputedVar,
     ComputedVar,
     DynamicRouteVar,
     DynamicRouteVar,
@@ -3176,143 +3179,6 @@ def get_state_manager() -> StateManager:
     return app.state_manager
     return app.state_manager
 
 
 
 
-class ClientStorageBase:
-    """Base class for client-side storage."""
-
-    def options(self) -> dict[str, Any]:
-        """Get the options for the storage.
-
-        Returns:
-            All set options for the storage (not None).
-        """
-        return {
-            format.to_camel_case(k): v for k, v in vars(self).items() if v is not None
-        }
-
-
-class Cookie(ClientStorageBase, str):
-    """Represents a state Var that is stored as a cookie in the browser."""
-
-    name: str | None
-    path: str
-    max_age: int | None
-    domain: str | None
-    secure: bool | None
-    same_site: str
-
-    def __new__(
-        cls,
-        object: Any = "",
-        encoding: str | None = None,
-        errors: str | None = None,
-        /,
-        name: str | None = None,
-        path: str = "/",
-        max_age: int | None = None,
-        domain: str | None = None,
-        secure: bool | None = None,
-        same_site: str = "lax",
-    ):
-        """Create a client-side Cookie (str).
-
-        Args:
-            object: The initial object.
-            encoding: The encoding to use.
-            errors: The error handling scheme to use.
-            name: The name of the cookie on the client side.
-            path: Cookie path. Use / as the path if the cookie should be accessible on all pages.
-            max_age: Relative max age of the cookie in seconds from when the client receives it.
-            domain: Domain for the cookie (sub.domain.com or .allsubdomains.com).
-            secure: Is the cookie only accessible through HTTPS?
-            same_site: Whether the cookie is sent with third party requests.
-                One of (true|false|none|lax|strict)
-
-        Returns:
-            The client-side Cookie object.
-
-        Note: expires (absolute Date) is not supported at this time.
-        """
-        if encoding or errors:
-            inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict")
-        else:
-            inst = super().__new__(cls, object)
-        inst.name = name
-        inst.path = path
-        inst.max_age = max_age
-        inst.domain = domain
-        inst.secure = secure
-        inst.same_site = same_site
-        return inst
-
-
-class LocalStorage(ClientStorageBase, str):
-    """Represents a state Var that is stored in localStorage in the browser."""
-
-    name: str | None
-    sync: bool = False
-
-    def __new__(
-        cls,
-        object: Any = "",
-        encoding: str | None = None,
-        errors: str | None = None,
-        /,
-        name: str | None = None,
-        sync: bool = False,
-    ) -> "LocalStorage":
-        """Create a client-side localStorage (str).
-
-        Args:
-            object: The initial object.
-            encoding: The encoding to use.
-            errors: The error handling scheme to use.
-            name: The name of the storage key on the client side.
-            sync: Whether changes should be propagated to other tabs.
-
-        Returns:
-            The client-side localStorage object.
-        """
-        if encoding or errors:
-            inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict")
-        else:
-            inst = super().__new__(cls, object)
-        inst.name = name
-        inst.sync = sync
-        return inst
-
-
-class SessionStorage(ClientStorageBase, str):
-    """Represents a state Var that is stored in sessionStorage in the browser."""
-
-    name: str | None
-
-    def __new__(
-        cls,
-        object: Any = "",
-        encoding: str | None = None,
-        errors: str | None = None,
-        /,
-        name: str | None = None,
-    ) -> "SessionStorage":
-        """Create a client-side sessionStorage (str).
-
-        Args:
-            object: The initial object.
-            encoding: The encoding to use.
-            errors: The error handling scheme to use
-            name: The name of the storage on the client side
-
-        Returns:
-            The client-side sessionStorage object.
-        """
-        if encoding or errors:
-            inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict")
-        else:
-            inst = super().__new__(cls, object)
-        inst.name = name
-        return inst
-
-
 class MutableProxy(wrapt.ObjectProxy):
 class MutableProxy(wrapt.ObjectProxy):
     """A proxy for a mutable object that tracks changes."""
     """A proxy for a mutable object that tracks changes."""
 
 

+ 18 - 4
reflex/utils/pyi_generator.py

@@ -214,7 +214,9 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
     return res
     return res
 
 
 
 
-def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]:
+def _generate_imports(
+    typing_imports: Iterable[str],
+) -> list[ast.ImportFrom | ast.Import]:
     """Generate the import statements for the stub file.
     """Generate the import statements for the stub file.
 
 
     Args:
     Args:
@@ -228,6 +230,7 @@ def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]:
             ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values])
             ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values])
             for name, values in DEFAULT_IMPORTS.items()
             for name, values in DEFAULT_IMPORTS.items()
         ],
         ],
+        ast.Import([ast.alias("reflex")]),
     ]
     ]
 
 
 
 
@@ -372,12 +375,13 @@ def _extract_class_props_as_ast_nodes(
     return kwargs
     return kwargs
 
 
 
 
-def type_to_ast(typ) -> ast.AST:
+def type_to_ast(typ, cls: type) -> ast.AST:
     """Converts any type annotation into its AST representation.
     """Converts any type annotation into its AST representation.
     Handles nested generic types, unions, etc.
     Handles nested generic types, unions, etc.
 
 
     Args:
     Args:
         typ: The type annotation to convert.
         typ: The type annotation to convert.
+        cls: The class where the type annotation is used.
 
 
     Returns:
     Returns:
         The AST representation of the type annotation.
         The AST representation of the type annotation.
@@ -390,6 +394,16 @@ def type_to_ast(typ) -> ast.AST:
     # Handle plain types (int, str, custom classes, etc.)
     # Handle plain types (int, str, custom classes, etc.)
     if origin is None:
     if origin is None:
         if hasattr(typ, "__name__"):
         if hasattr(typ, "__name__"):
+            if typ.__module__.startswith("reflex."):
+                typ_parts = typ.__module__.split(".")
+                cls_parts = cls.__module__.split(".")
+
+                zipped = list(zip(typ_parts, cls_parts, strict=False))
+
+                if all(a == b for a, b in zipped) and len(typ_parts) == len(cls_parts):
+                    return ast.Name(id=typ.__name__)
+
+                return ast.Name(id=typ.__module__ + "." + typ.__name__)
             return ast.Name(id=typ.__name__)
             return ast.Name(id=typ.__name__)
         elif hasattr(typ, "_name"):
         elif hasattr(typ, "_name"):
             return ast.Name(id=typ._name)
             return ast.Name(id=typ._name)
@@ -406,7 +420,7 @@ def type_to_ast(typ) -> ast.AST:
         return ast.Name(id=base_name)
         return ast.Name(id=base_name)
 
 
     # Convert all type arguments recursively
     # Convert all type arguments recursively
-    arg_nodes = [type_to_ast(arg) for arg in args]
+    arg_nodes = [type_to_ast(arg, cls) for arg in args]
 
 
     # Special case for single-argument types (like List[T] or Optional[T])
     # Special case for single-argument types (like List[T] or Optional[T])
     if len(arg_nodes) == 1:
     if len(arg_nodes) == 1:
@@ -487,7 +501,7 @@ def _generate_component_create_functiondef(
             ]
             ]
 
 
             # Convert each argument type to its AST representation
             # Convert each argument type to its AST representation
-            type_args = [type_to_ast(arg) for arg in arguments_without_var]
+            type_args = [type_to_ast(arg, cls=clz) for arg in arguments_without_var]
 
 
             # Join the type arguments with commas for EventType
             # Join the type arguments with commas for EventType
             args_str = ", ".join(ast.unparse(arg) for arg in type_args)
             args_str = ", ".join(ast.unparse(arg) for arg in type_args)