diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 50cb8e442..beb8236bd 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -9,9 +9,8 @@ import Router, { useRouter } from "next/router"; // Endpoint URLs. -const PINGURL = env.pingUrl -const EVENTURL = env.eventUrl -const UPLOADURL = env.uploadUrl +const EVENTURL = env.EVENT +const UPLOADURL = env.UPLOAD // Global variable to hold the token. let token; diff --git a/reflex/app.py b/reflex/app.py index ef5b165d0..8bbda6b6f 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -13,7 +13,6 @@ from typing import ( Dict, List, Optional, - Tuple, Type, Union, ) @@ -210,7 +209,7 @@ class App(Base): allow_origins=["*"], ) - async def preprocess(self, state: State, event: Event) -> Optional[StateUpdate]: + async def preprocess(self, state: State, event: Event) -> StateUpdate | None: """Preprocess the event. This is where middleware can modify the event before it is processed. @@ -263,7 +262,7 @@ class App(Base): return out # type: ignore return update - def add_middleware(self, middleware: Middleware, index: Optional[int] = None): + def add_middleware(self, middleware: Middleware, index: int | None = None): """Add middleware to the app. Args: @@ -302,16 +301,17 @@ class App(Base): def add_page( self, - component: Union[Component, ComponentCallable], - route: Optional[str] = None, + component: Component | ComponentCallable, + route: str | None = None, title: str = constants.DEFAULT_TITLE, description: str = constants.DEFAULT_DESCRIPTION, image=constants.DEFAULT_IMAGE, - on_load: Optional[ - Union[EventHandler, EventSpec, List[Union[EventHandler, EventSpec]]] - ] = None, - meta: List[Dict] = constants.DEFAULT_META_LIST, - script_tags: Optional[List[Component]] = None, + on_load: EventHandler + | EventSpec + | list[EventHandler | EventSpec] + | None = None, + meta: list[dict[str, str]] = constants.DEFAULT_META_LIST, + script_tags: list[Component] | None = None, ): """Add a page to the app. @@ -379,7 +379,7 @@ class App(Base): on_load = [on_load] self.load_events[route] = on_load - def get_load_events(self, route: str) -> List[Union[EventHandler, EventSpec]]: + def get_load_events(self, route: str) -> list[EventHandler | EventSpec]: """Get the load events for a route. Args: @@ -428,14 +428,15 @@ class App(Base): def add_custom_404_page( self, - component: Optional[Union[Component, ComponentCallable]] = None, + component: Component | ComponentCallable | None = None, title: str = constants.TITLE_404, image: str = constants.FAVICON_404, description: str = constants.DESCRIPTION_404, - on_load: Optional[ - Union[EventHandler, EventSpec, List[Union[EventHandler, EventSpec]]] - ] = None, - meta: List[Dict] = constants.DEFAULT_META_LIST, + on_load: EventHandler + | EventSpec + | list[EventHandler | EventSpec] + | None = None, + meta: list[dict[str, str]] = constants.DEFAULT_META_LIST, ): """Define a custom 404 page for any url having no match. @@ -694,7 +695,7 @@ def upload(app: App): # get the current state(parent state/substate) path = handler.split(".")[:-1] current_state = state.get_substate(path) - handler_upload_param: Tuple = () + handler_upload_param = () # get handler function func = getattr(current_state, handler.split(".")[-1]) diff --git a/reflex/base.py b/reflex/base.py index 17bf8d7a9..53510786b 100644 --- a/reflex/base.py +++ b/reflex/base.py @@ -1,7 +1,7 @@ """Define the base Reflex class.""" from __future__ import annotations -from typing import Any, Dict +from typing import Any import pydantic from pydantic.fields import ModelField @@ -46,7 +46,7 @@ class Base(pydantic.BaseModel): return self @classmethod - def get_fields(cls) -> Dict[str, Any]: + def get_fields(cls) -> dict[str, Any]: """Get the fields of the object. Returns: diff --git a/reflex/components/base/meta.py b/reflex/components/base/meta.py index 953e90c54..55cb42f0a 100644 --- a/reflex/components/base/meta.py +++ b/reflex/components/base/meta.py @@ -1,6 +1,8 @@ """Display the title of the current page.""" -from typing import Dict, Optional +from __future__ import annotations + +from typing import Optional from reflex.components.base.bare import Bare from reflex.components.component import Component @@ -11,7 +13,7 @@ class Title(Component): tag = "title" - def render(self) -> Dict: + def render(self) -> dict: """Render the title component. Returns: diff --git a/reflex/components/base/script.py b/reflex/components/base/script.py index 3d9d6497b..6b62d552a 100644 --- a/reflex/components/base/script.py +++ b/reflex/components/base/script.py @@ -2,7 +2,7 @@ https://nextjs.org/docs/app/api-reference/components/script """ -from typing import Set +from __future__ import annotations from reflex.components.component import Component from reflex.event import EventChain @@ -57,7 +57,7 @@ class Script(Component): raise ValueError("Must provide inline script or `src` prop.") return super().create(*children, **props) - def get_triggers(self) -> Set[str]: + def get_triggers(self) -> set[str]: """Get the event triggers for the component. Returns: diff --git a/reflex/config.py b/reflex/config.py index 2ce3cb86c..41d804bf4 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -28,9 +28,9 @@ class DBConfig(Base): cls, database: str, username: str, - password: Optional[str] = None, - host: Optional[str] = None, - port: Optional[int] = 5432, + password: str | None = None, + host: str | None = None, + port: int | None = 5432, ) -> DBConfig: """Create an instance with postgresql engine. @@ -58,9 +58,9 @@ class DBConfig(Base): cls, database: str, username: str, - password: Optional[str] = None, - host: Optional[str] = None, - port: Optional[int] = 5432, + password: str | None = None, + host: str | None = None, + port: int | None = 5432, ) -> DBConfig: """Create an instance with postgresql+psycopg2 engine. @@ -259,7 +259,7 @@ class Config(Base): # Set the value. setattr(self, key, env_var) - def get_event_namespace(self) -> Optional[str]: + def get_event_namespace(self) -> str | None: """Get the websocket event namespace. Returns: diff --git a/reflex/constants.py b/reflex/constants.py index 2f26e12a4..9d3d3ec05 100644 --- a/reflex/constants.py +++ b/reflex/constants.py @@ -6,7 +6,6 @@ import platform import re from enum import Enum from types import SimpleNamespace -from typing import Optional from platformdirs import PlatformDirs @@ -19,7 +18,7 @@ except ImportError: IS_WINDOWS = platform.system() == "Windows" -def get_fnm_name() -> Optional[str]: +def get_fnm_name() -> str | None: """Get the appropriate fnm executable name based on the current platform. Returns: diff --git a/reflex/event.py b/reflex/event.py index 7fdc7ae55..4dc076baa 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -2,7 +2,7 @@ from __future__ import annotations import inspect -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Callable, Dict, List, Tuple from reflex import constants from reflex.base import Base @@ -162,7 +162,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec: ) -def redirect(path: Union[str, Var[str]]) -> EventSpec: +def redirect(path: str | Var[str]) -> EventSpec: """Redirect to a new path. Args: @@ -174,7 +174,7 @@ def redirect(path: Union[str, Var[str]]) -> EventSpec: return server_side("_redirect", get_fn_signature(redirect), path=path) -def console_log(message: Union[str, Var[str]]) -> EventSpec: +def console_log(message: str | Var[str]) -> EventSpec: """Do a console.log on the browser. Args: @@ -186,7 +186,7 @@ def console_log(message: Union[str, Var[str]]) -> EventSpec: return server_side("_console", get_fn_signature(console_log), message=message) -def window_alert(message: Union[str, Var[str]]) -> EventSpec: +def window_alert(message: str | Var[str]) -> EventSpec: """Create a window alert on the browser. Args: @@ -250,7 +250,7 @@ def set_cookie(key: str, value: str) -> EventSpec: ) -def remove_cookie(key: str, options: Dict[str, Any] = {}) -> EventSpec: # noqa: B006 +def remove_cookie(key: str, options: dict[str, Any] = {}) -> EventSpec: # noqa: B006 """Remove a cookie on the frontend. Args: @@ -378,7 +378,7 @@ def call_event_handler(event_handler: EventHandler, arg: Var) -> EventSpec: return event_handler(arg) -def call_event_fn(fn: Callable, arg: Var) -> List[EventSpec]: +def call_event_fn(fn: Callable, arg: Var) -> list[EventSpec]: """Call a function to a list of event specs. The function should return either a single EventSpec or a list of EventSpecs. @@ -434,7 +434,7 @@ def call_event_fn(fn: Callable, arg: Var) -> List[EventSpec]: return events -def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[Var, Var], ...]: +def get_handler_args(event_spec: EventSpec, arg: Var) -> tuple[tuple[Var, Var], ...]: """Get the handler args for the given event spec. Args: @@ -449,9 +449,7 @@ def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[Var, Var], return event_spec.args if len(args) > 1 else tuple() -def fix_events( - events: Optional[List[Union[EventHandler, EventSpec]]], token: str -) -> List[Event]: +def fix_events(events: list[EventHandler | EventSpec], token: str) -> list[Event]: """Fix a list of events returned by an event handler. Args: @@ -510,7 +508,7 @@ def get_fn_signature(fn: Callable) -> inspect.Signature: # A set of common event triggers. -EVENT_TRIGGERS: Set[str] = { +EVENT_TRIGGERS: set[str] = { "on_focus", "on_blur", "on_click", diff --git a/reflex/model.py b/reflex/model.py index 2e9d275b5..c8724f736 100644 --- a/reflex/model.py +++ b/reflex/model.py @@ -1,5 +1,7 @@ """Database built into Reflex.""" +from __future__ import annotations + import os from collections import defaultdict from pathlib import Path @@ -21,7 +23,7 @@ from reflex.config import get_config from reflex.utils import console -def get_engine(url: Optional[str] = None): +def get_engine(url: str | None = None): """Get the database engine. Args: @@ -142,7 +144,7 @@ class Model(Base, sqlmodel.SQLModel): def alembic_autogenerate( cls, connection: sqlalchemy.engine.Connection, - message: Optional[str] = None, + message: str | None = None, write_migration_scripts: bool = True, ) -> bool: """Generate migration scripts for alembic-detectable changes. @@ -233,7 +235,7 @@ class Model(Base, sqlmodel.SQLModel): env.run_migrations() @classmethod - def migrate(cls, autogenerate: bool = False) -> Optional[bool]: + def migrate(cls, autogenerate: bool = False) -> bool | None: """Execute alembic migrations for all sqlmodel Model classes. If alembic is not installed or has not been initialized for the project, @@ -277,7 +279,7 @@ class Model(Base, sqlmodel.SQLModel): return sqlmodel.select(cls) -def session(url: Optional[str] = None) -> sqlmodel.Session: +def session(url: str | None = None) -> sqlmodel.Session: """Get a session to interact with the database. Args: diff --git a/reflex/page.py b/reflex/page.py index e6d776b3d..e5ad8918a 100644 --- a/reflex/page.py +++ b/reflex/page.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import List, Optional, Union - from reflex.components.component import Component from reflex.event import EventHandler @@ -11,13 +9,13 @@ DECORATED_PAGES = [] def page( - route: Optional[str] = None, - title: Optional[str] = None, - image: Optional[str] = None, - description: Optional[str] = None, - meta: Optional[str] = None, - script_tags: Optional[List[Component]] = None, - on_load: Optional[Union[EventHandler, List[EventHandler]]] = None, + route: str | None = None, + title: str | None = None, + image: str | None = None, + description: str | None = None, + meta: str | None = None, + script_tags: list[Component] | None = None, + on_load: EventHandler | list[EventHandler] | None = None, ): """Decorate a function as a page. @@ -40,7 +38,6 @@ def page( Returns: The decorated function. """ - ... def decorator(render_fn): kwargs = {} diff --git a/reflex/reflex.py b/reflex/reflex.py index 9f662c4e1..a797dbe88 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -81,7 +81,6 @@ def init( if not os.path.exists(constants.CONFIG_FILE): prerequisites.create_config(app_name) prerequisites.initialize_app_directory(app_name, template) - build.set_reflex_project_hash() telemetry.send("init", config.telemetry_enabled) else: telemetry.send("reinit", config.telemetry_enabled) diff --git a/reflex/route.py b/reflex/route.py index badaa1c4b..47e1e5dfa 100644 --- a/reflex/route.py +++ b/reflex/route.py @@ -3,7 +3,6 @@ from __future__ import annotations import re -from typing import Dict, List, Optional, Union from reflex import constants from reflex.event import EventHandler @@ -12,11 +11,11 @@ from reflex.utils.console import deprecate def route( - route: Optional[str] = None, - title: Optional[str] = None, - image: Optional[str] = None, - description: Optional[str] = None, - on_load: Optional[Union[EventHandler, List[EventHandler]]] = None, + route: str | None = None, + title: str | None = None, + image: str | None = None, + description: str | None = None, + on_load: EventHandler | list[EventHandler] | None = None, ): """Decorate a function as a page. @@ -62,7 +61,7 @@ def verify_route_validity(route: str) -> None: raise ValueError(f"Catch-all must be the last part of the URL: {route}") -def get_route_args(route: str) -> Dict[str, str]: +def get_route_args(route: str) -> dict[str, str]: """Get the dynamic arguments for the given route. Args: diff --git a/reflex/state.py b/reflex/state.py index 97a02dcdb..c749f0cd6 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -21,7 +21,6 @@ from typing import ( Optional, Sequence, Set, - Tuple, Type, Union, ) @@ -87,7 +86,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): # Per-instance copy of backend variable values _backend_vars: Dict[str, Any] = {} - def __init__(self, *args, parent_state: Optional[State] = None, **kwargs): + def __init__(self, *args, parent_state: State | None = None, **kwargs): """Initialize the state. Args: @@ -287,7 +286,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): ) @classmethod - def get_skip_vars(cls) -> Set[str]: + def get_skip_vars(cls) -> set[str]: """Get the vars to skip when serializing. Returns: @@ -306,7 +305,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): @classmethod @functools.lru_cache() - def get_parent_state(cls) -> Optional[Type[State]]: + def get_parent_state(cls) -> Type[State] | None: """Get the parent state. Returns: @@ -322,7 +321,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): @classmethod @functools.lru_cache() - def get_substates(cls) -> Set[Type[State]]: + def get_substates(cls) -> set[Type[State]]: """Get the substates of the state. Returns: @@ -493,7 +492,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): field.default = default_value @staticmethod - def _get_base_functions() -> Dict[str, FunctionType]: + def _get_base_functions() -> dict[str, FunctionType]: """Get all functions of the state class excluding dunder methods. Returns: @@ -551,7 +550,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): else: return self.router_data.get(constants.RouteVar.PATH, "") - def get_query_params(self) -> Dict[str, str]: + def get_query_params(self) -> dict[str, str]: """Obtain the query parameters for the queried page. The query object contains both the URI parameters and the GET parameters. @@ -561,7 +560,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): """ return self.router_data.get(constants.RouteVar.QUERY, {}) - def get_cookies(self) -> Dict[str, str]: + def get_cookies(self) -> dict[str, str]: """Obtain the cookies of the client stored in the browser. Returns: @@ -712,7 +711,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): for substate in self.substates.values(): substate._reset_client_storage() - def get_substate(self, path: Sequence[str]) -> Optional[State]: + def get_substate(self, path: Sequence[str]) -> State | None: """Get the substate. Args: @@ -812,7 +811,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): async def _process_event( self, handler: EventHandler, state: State, payload: Dict - ) -> AsyncIterator[Tuple[Optional[List[EventSpec]], bool]]: + ) -> AsyncIterator[tuple[list[EventSpec] | None, bool]]: """Process event. Args: @@ -865,7 +864,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): print(error) yield [window_alert("An error occurred. See logs for details.")], True - def _always_dirty_computed_vars(self) -> Set[str]: + def _always_dirty_computed_vars(self) -> set[str]: """The set of ComputedVars that always need to be recalculated. Returns: @@ -889,7 +888,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): if actual_var: actual_var.mark_dirty(instance=self) - def _dirty_computed_vars(self, from_vars: Optional[Set[str]] = None) -> Set[str]: + def _dirty_computed_vars(self, from_vars: set[str] | None = None) -> set[str]: """Determine ComputedVars that need to be recalculated based on the given vars. Args: @@ -976,7 +975,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow): self.dirty_vars = set() self.dirty_substates = set() - def dict(self, include_computed: bool = True, **kwargs) -> Dict[str, Any]: + def dict(self, include_computed: bool = True, **kwargs) -> dict[str, Any]: """Convert the object to a dictionary. Args: diff --git a/reflex/style.py b/reflex/style.py index f89ff5c4a..9a16467a8 100644 --- a/reflex/style.py +++ b/reflex/style.py @@ -1,6 +1,6 @@ """Handle styling.""" -from typing import Optional +from __future__ import annotations from reflex import constants from reflex.event import EventChain @@ -35,7 +35,7 @@ def convert(style_dict): class Style(dict): """A style dictionary.""" - def __init__(self, style_dict: Optional[dict] = None): + def __init__(self, style_dict: dict | None = None): """Initialize the style. Args: diff --git a/reflex/utils/build.py b/reflex/utils/build.py index 41f19b0f6..ad5ab9a22 100644 --- a/reflex/utils/build.py +++ b/reflex/utils/build.py @@ -4,12 +4,10 @@ from __future__ import annotations import json import os -import random import subprocess import zipfile from enum import Enum from pathlib import Path -from typing import Optional, Union from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn @@ -18,42 +16,11 @@ from reflex.config import get_config from reflex.utils import console, path_ops, prerequisites, processes -def update_json_file(file_path: str, update_dict: dict[str, Union[int, str]]): - """Update the contents of a json file. - - Args: - file_path: the path to the JSON file. - update_dict: object to update json. - """ - fp = Path(file_path) - # create file if it doesn't exist - fp.touch(exist_ok=True) - # create an empty json object if file is empty - fp.write_text("{}") if fp.stat().st_size == 0 else None - - with open(fp) as f: # type: ignore - json_object: dict = json.load(f) - json_object.update(update_dict) - with open(fp, "w") as f: - json.dump(json_object, f, ensure_ascii=False) - - -def set_reflex_project_hash(): - """Write the hash of the Reflex project to a REFLEX_JSON.""" - project_hash = random.getrandbits(128) - console.debug(f"Setting project hash to {project_hash}.") - update_json_file(constants.REFLEX_JSON, {"project_hash": project_hash}) - - def set_env_json(): """Write the upload url to a REFLEX_JSON.""" - update_json_file( + path_ops.update_json_file( constants.ENV_JSON, - { - "uploadUrl": constants.Endpoint.UPLOAD.get_url(), - "eventUrl": constants.Endpoint.EVENT.get_url(), - "pingUrl": constants.Endpoint.PING.get_url(), - }, + {endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint}, ) @@ -152,7 +119,7 @@ def export( backend: bool = True, frontend: bool = True, zip: bool = False, - deploy_url: Optional[str] = None, + deploy_url: str | None = None, ): """Export the app for deployment. diff --git a/reflex/utils/format.py b/reflex/utils/format.py index 26a41b2a0..b799b8a06 100644 --- a/reflex/utils/format.py +++ b/reflex/utils/format.py @@ -9,7 +9,7 @@ import os import os.path as op import re import sys -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type +from typing import TYPE_CHECKING, Any, Type import plotly.graph_objects as go from plotly.io import to_json @@ -33,7 +33,7 @@ WRAP_MAP = { } -def get_close_char(open: str, close: Optional[str] = None) -> str: +def get_close_char(open: str, close: str | None = None) -> str: """Check if the given character is a valid brace. Args: @@ -53,7 +53,7 @@ def get_close_char(open: str, close: Optional[str] = None) -> str: return WRAP_MAP[open] -def is_wrapped(text: str, open: str, close: Optional[str] = None) -> bool: +def is_wrapped(text: str, open: str, close: str | None = None) -> bool: """Check if the given text is wrapped in the given open and close characters. Args: @@ -71,7 +71,7 @@ def is_wrapped(text: str, open: str, close: Optional[str] = None) -> bool: def wrap( text: str, open: str, - close: Optional[str] = None, + close: str | None = None, check_first: bool = True, num: int = 1, ) -> str: @@ -258,7 +258,7 @@ def format_cond( return wrap(f"{cond} ? {true_value} : {false_value}", "{") -def get_event_handler_parts(handler: EventHandler) -> Tuple[str, str]: +def get_event_handler_parts(handler: EventHandler) -> tuple[str, str]: """Get the state and function name of an event handler. Args: @@ -370,7 +370,7 @@ def format_event_chain( ) -def format_query_params(router_data: Dict[str, Any]) -> Dict[str, str]: +def format_query_params(router_data: dict[str, Any]) -> dict[str, str]: """Convert back query params name to python-friendly case. Args: @@ -383,7 +383,7 @@ def format_query_params(router_data: Dict[str, Any]) -> Dict[str, str]: return {k.replace("-", "_"): v for k, v in params.items()} -def format_dataframe_values(value: Type) -> List[Any]: +def format_dataframe_values(value: Type) -> list[Any]: """Format dataframe values. Args: diff --git a/reflex/utils/imports.py b/reflex/utils/imports.py index 62d66a9c4..d1e37ae5d 100644 --- a/reflex/utils/imports.py +++ b/reflex/utils/imports.py @@ -1,5 +1,7 @@ """Import operations.""" +from __future__ import annotations + from collections import defaultdict from typing import Dict, Set diff --git a/reflex/utils/path_ops.py b/reflex/utils/path_ops.py index b60771c4c..db58c8f47 100644 --- a/reflex/utils/path_ops.py +++ b/reflex/utils/path_ops.py @@ -2,10 +2,10 @@ from __future__ import annotations +import json import os import shutil from pathlib import Path -from typing import Optional from reflex import constants @@ -100,7 +100,7 @@ def ln(src: str, dest: str, overwrite: bool = False) -> bool: return True -def which(program: str) -> Optional[str]: +def which(program: str) -> str | None: """Find the path to an executable. Args: @@ -112,7 +112,7 @@ def which(program: str) -> Optional[str]: return shutil.which(program) -def get_node_bin_path() -> Optional[str]: +def get_node_bin_path() -> str | None: """Get the node binary dir path. Returns: @@ -124,7 +124,7 @@ def get_node_bin_path() -> Optional[str]: return constants.NODE_BIN_PATH -def get_node_path() -> Optional[str]: +def get_node_path() -> str | None: """Get the node binary path. Returns: @@ -135,7 +135,7 @@ def get_node_path() -> Optional[str]: return constants.NODE_PATH -def get_npm_path() -> Optional[str]: +def get_npm_path() -> str | None: """Get npm binary path. Returns: @@ -144,3 +144,32 @@ def get_npm_path() -> Optional[str]: if not os.path.exists(constants.NODE_PATH): return which("npm") return constants.NPM_PATH + + +def update_json_file(file_path: str, update_dict: dict[str, int | str]): + """Update the contents of a json file. + + Args: + file_path: the path to the JSON file. + update_dict: object to update json. + """ + fp = Path(file_path) + + # Create the file if it doesn't exist. + fp.touch(exist_ok=True) + + # Create an empty json object if file is empty + fp.write_text("{}") if fp.stat().st_size == 0 else None + + # Read the existing json object from the file. + json_object = {} + if fp.stat().st_size == 0: + with open(fp) as f: + json_object = json.load(f) + + # Update the json object with the new data. + json_object.update(update_dict) + + # Write the updated json object to the file + with open(fp, "w") as f: + json.dump(json_object, f, ensure_ascii=False) diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index fefea00f3..09f7365c9 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -6,6 +6,7 @@ import glob import json import os import platform +import random import re import stat import sys @@ -14,7 +15,6 @@ import zipfile from fileinput import FileInput from pathlib import Path from types import ModuleType -from typing import List, Optional import httpx import typer @@ -44,7 +44,7 @@ def check_node_version() -> bool: return False -def get_node_version() -> Optional[version.Version]: +def get_node_version() -> version.Version | None: """Get the version of node. Returns: @@ -58,7 +58,7 @@ def get_node_version() -> Optional[version.Version]: return None -def get_bun_version() -> Optional[version.Version]: +def get_bun_version() -> version.Version | None: """Get the version of bun. Returns: @@ -72,7 +72,7 @@ def get_bun_version() -> Optional[version.Version]: return None -def get_install_package_manager() -> Optional[str]: +def get_install_package_manager() -> str | None: """Get the package manager executable for installation. Currently on unix systems, bun is used for installation only. @@ -87,7 +87,7 @@ def get_install_package_manager() -> Optional[str]: return get_config().bun_path -def get_package_manager() -> Optional[str]: +def get_package_manager() -> str | None: """Get the package manager executable for running app. Currently on unix systems, npm is used for running the app only. @@ -109,7 +109,7 @@ def get_app() -> ModuleType: return __import__(module, fromlist=(constants.APP_VAR,)) -def get_redis() -> Optional[Redis]: +def get_redis() -> Redis | None: """Get the redis client. Returns: @@ -227,10 +227,22 @@ def initialize_web_directory(): with open(next_config_file, "w") as file: file.writelines(lines) - # Write the current version of distributed reflex package to a REFLEX_JSON.""" - with open(constants.REFLEX_JSON, "w") as f: - reflex_json = {"version": constants.VERSION} - json.dump(reflex_json, f, ensure_ascii=False) + # Initialize the reflex json file. + init_reflex_json() + + +def init_reflex_json(): + """Write the hash of the Reflex project to a REFLEX_JSON.""" + # Get a random project hash. + project_hash = random.getrandbits(128) + console.debug(f"Setting project hash to {project_hash}.") + + # Write the hash and version to the reflex json file. + reflex_json = { + "version": constants.VERSION, + "project_hash": project_hash, + } + path_ops.update_json_file(constants.REFLEX_JSON, reflex_json) def remove_existing_bun_installation(): @@ -376,11 +388,11 @@ def install_bun(): ) -def install_frontend_packages(packages: List[str]): +def install_frontend_packages(packages: list[str]): """Installs the base and custom frontend packages. Args: - packages (List[str]): A list of package names to be installed. + packages: A list of package names to be installed. Example: >>> install_frontend_packages(["react", "react-dom"]) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index dbd7ce4f4..3d51b2cd2 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -4,7 +4,7 @@ from __future__ import annotations import contextlib import typing -from typing import Any, Callable, Tuple, Type, Union, _GenericAlias # type: ignore +from typing import Any, Callable, Type, Union, _GenericAlias # type: ignore from reflex.base import Base @@ -17,7 +17,7 @@ StateVar = Union[PrimitiveType, Base, None] StateIterVar = Union[list, set, tuple] -def get_args(alias: _GenericAlias) -> Tuple[Type, ...]: +def get_args(alias: _GenericAlias) -> tuple[Type, ...]: """Get the arguments of a type alias. Args: diff --git a/reflex/vars.py b/reflex/vars.py index 8a87454f9..c5c31d401 100644 --- a/reflex/vars.py +++ b/reflex/vars.py @@ -73,7 +73,7 @@ class Var(ABC): @classmethod def create( cls, value: Any, is_local: bool = True, is_string: bool = False - ) -> Optional[Var]: + ) -> Var | None: """Create a var from a value. Args: @@ -358,10 +358,10 @@ class Var(ABC): def operation( self, op: str = "", - other: Optional[Var] = None, - type_: Optional[Type] = None, + other: Var | None = None, + type_: Type | None = None, flip: bool = False, - fn: Optional[str] = None, + fn: str | None = None, ) -> Var: """Perform an operation on a var. @@ -983,8 +983,8 @@ class ComputedVar(Var, property): def deps( self, objclass: Type, - obj: Optional[FunctionType] = None, - ) -> Set[str]: + obj: FunctionType | None = None, + ) -> set[str]: """Determine var dependencies of this ComputedVar. Save references to attributes accessed on "self". Recursively called @@ -1375,10 +1375,8 @@ class ImportVar(Base): class NoRenderImportVar(ImportVar): """A import that doesn't need to be rendered.""" - ... - -def get_local_storage(key: Optional[Union[Var, str]] = None) -> BaseVar: +def get_local_storage(key: Var | str | None = None) -> BaseVar: """Provide a base var as payload to get local storage item(s). Args: diff --git a/reflex/vars.pyi b/reflex/vars.pyi index 66e4b34ea..7bb9810cd 100644 --- a/reflex/vars.pyi +++ b/reflex/vars.pyi @@ -160,4 +160,7 @@ class ImportVar(Base): def name(self) -> str: ... def __hash__(self) -> int: ... +class NoRenderImportVar(ImportVar): + """A import that doesn't need to be rendered.""" + def get_local_storage(key: Optional[Union[Var, str]] = ...) -> BaseVar: ...