Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move client storage classes to their own file #4216

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions reflex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,15 @@
"upload_files",
"window_alert",
],
"istate.storage": [
"Cookie",
"LocalStorage",
"SessionStorage",
],
"middleware": ["middleware", "Middleware"],
"model": ["session", "Model"],
"state": [
"var",
"Cookie",
"LocalStorage",
"SessionStorage",
"ComponentState",
"State",
],
Expand Down
6 changes: 3 additions & 3 deletions reflex/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,15 @@ from .event import stop_propagation as stop_propagation
from .event import upload_files as upload_files
from .event import window_alert as window_alert
from .experimental import _x as _x
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 .model import Model as Model
from .model import session as session
from .page import page as page
from .state import ComponentState as ComponentState
from .state import Cookie as Cookie
from .state import LocalStorage as LocalStorage
from .state import SessionStorage as SessionStorage
from .state import State as State
from .state import var as var
from .style import Style as Style
Expand Down
3 changes: 2 additions & 1 deletion reflex/compiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
Title,
)
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.utils import console, format, imports, path_ops
from reflex.utils.imports import ImportVar, ParsedImportDict
Expand Down
144 changes: 144 additions & 0 deletions reflex/istate/storage.py
Original file line number Diff line number Diff line change
@@ -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
140 changes: 3 additions & 137 deletions reflex/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
from reflex import event
from reflex.config import get_config
from reflex.istate.data import RouterData
from reflex.istate.storage import (
ClientStorageBase,
)
from reflex.vars.base import (
ComputedVar,
DynamicRouteVar,
Expand Down Expand Up @@ -3349,143 +3352,6 @@ def get_state_manager() -> StateManager:
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):
"""A proxy for a mutable object that tracks changes."""

Expand Down
Loading