Skip to content

Commit ffc9172

Browse files
authored
optimize various functions (#5744)
* optimize various functions * fix few stuff * add total
1 parent b362ef4 commit ffc9172

File tree

18 files changed

+182
-100
lines changed

18 files changed

+182
-100
lines changed

reflex/app.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,12 +1100,18 @@ def _validate_var_dependencies(self, state: type[BaseState] | None = None) -> No
11001100
for substate in state.class_subclasses:
11011101
self._validate_var_dependencies(substate)
11021102

1103-
def _compile(self, prerender_routes: bool = False, dry_run: bool = False):
1103+
def _compile(
1104+
self,
1105+
prerender_routes: bool = False,
1106+
dry_run: bool = False,
1107+
use_rich: bool = True,
1108+
):
11041109
"""Compile the app and output it to the pages folder.
11051110
11061111
Args:
11071112
prerender_routes: Whether to prerender the routes.
11081113
dry_run: Whether to compile the app without saving it.
1114+
use_rich: Whether to use rich progress bars.
11091115
11101116
Raises:
11111117
ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
@@ -1171,10 +1177,14 @@ def get_compilation_time() -> str:
11711177
return
11721178

11731179
# Create a progress bar.
1174-
progress = Progress(
1175-
*Progress.get_default_columns()[:-1],
1176-
MofNCompleteColumn(),
1177-
TimeElapsedColumn(),
1180+
progress = (
1181+
Progress(
1182+
*Progress.get_default_columns()[:-1],
1183+
MofNCompleteColumn(),
1184+
TimeElapsedColumn(),
1185+
)
1186+
if use_rich
1187+
else console.PoorProgress()
11781188
)
11791189

11801190
# try to be somewhat accurate - but still not 100%

reflex/compiler/templates.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ def context_template(
362362
export const DispatchContext = createContext(null);
363363
export const StateContexts = {{{state_contexts_str}}};
364364
export const EventLoopContext = createContext(null);
365-
export const clientStorage = {"{}" if client_storage is None else json_dumps(client_storage)}
365+
export const clientStorage = {"{}" if client_storage is None else json.dumps(client_storage)}
366366
367367
{state_str}
368368

reflex/compiler/utils.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,13 @@ def compile_state(state: type[BaseState]) -> dict:
220220

221221
def _compile_client_storage_field(
222222
field: Field,
223-
) -> tuple[
224-
type[Cookie] | type[LocalStorage] | type[SessionStorage] | None,
225-
dict[str, Any] | None,
226-
]:
223+
) -> (
224+
tuple[
225+
type[Cookie] | type[LocalStorage] | type[SessionStorage],
226+
dict[str, Any],
227+
]
228+
| tuple[None, None]
229+
):
227230
"""Compile the given cookie, local_storage or session_storage field.
228231
229232
Args:

reflex/components/component.py

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,6 @@ def evaluate_style_namespaces(style: ComponentStyle) -> dict:
419419

420420
# Map from component to styling.
421421
ComponentStyle = dict[str | type[BaseComponent] | Callable | ComponentNamespace, Any]
422-
ComponentChild = types.PrimitiveType | Var | BaseComponent
423422
ComponentChildTypes = (*types.PrimitiveTypes, Var, BaseComponent, type(None))
424423

425424

@@ -480,7 +479,21 @@ def _components_from(
480479
return ()
481480

482481

483-
def _deterministic_hash(value: object) -> int:
482+
def _hash_str(value: str) -> str:
483+
return md5(f'"{value}"'.encode(), usedforsecurity=False).hexdigest()
484+
485+
486+
def _hash_sequence(value: Sequence) -> str:
487+
return _hash_str(str([_deterministic_hash(v) for v in value]))
488+
489+
490+
def _hash_dict(value: dict) -> str:
491+
return _hash_sequence(
492+
sorted([(k, _deterministic_hash(v)) for k, v in value.items()])
493+
)
494+
495+
496+
def _deterministic_hash(value: object) -> str:
484497
"""Hash a rendered dictionary.
485498
486499
Args:
@@ -492,37 +505,28 @@ def _deterministic_hash(value: object) -> int:
492505
Raises:
493506
TypeError: If the value is not hashable.
494507
"""
495-
if isinstance(value, BaseComponent):
496-
# If the value is a component, hash its rendered code.
497-
rendered_code = value.render()
498-
return _deterministic_hash(rendered_code)
499-
if isinstance(value, Var):
500-
return _deterministic_hash((value._js_expr, value._get_all_var_data()))
501-
if isinstance(value, VarData):
502-
return _deterministic_hash(dataclasses.asdict(value))
503-
if isinstance(value, dict):
504-
# Sort the dictionary to ensure consistent hashing.
505-
return _deterministic_hash(
506-
tuple(sorted((k, _deterministic_hash(v)) for k, v in value.items()))
507-
)
508-
if isinstance(value, int):
508+
if value is None:
509+
# Hash None as a special case.
510+
return "None"
511+
if isinstance(value, (int, float, enum.Enum)):
509512
# Hash numbers and booleans directly.
510-
return int(value)
511-
if isinstance(value, float):
512-
return _deterministic_hash(str(value))
513+
return str(value)
513514
if isinstance(value, str):
514-
return int(md5(f'"{value}"'.encode()).hexdigest(), 16)
515+
return _hash_str(value)
516+
if isinstance(value, dict):
517+
return _hash_dict(value)
515518
if isinstance(value, (tuple, list)):
516519
# Hash tuples by hashing each element.
517-
return _deterministic_hash(
518-
"[" + ",".join(map(str, map(_deterministic_hash, value))) + "]"
520+
return _hash_sequence(value)
521+
if isinstance(value, Var):
522+
return _hash_str(
523+
str((value._js_expr, _deterministic_hash(value._get_all_var_data())))
519524
)
520-
if isinstance(value, enum.Enum):
521-
# Hash enums by their name.
522-
return _deterministic_hash(str(value))
523-
if value is None:
524-
# Hash None as a special case.
525-
return _deterministic_hash("None")
525+
if isinstance(value, VarData):
526+
return _hash_dict(dataclasses.asdict(value))
527+
if isinstance(value, BaseComponent):
528+
# If the value is a component, hash its rendered code.
529+
return _hash_dict(value.render())
526530

527531
msg = (
528532
f"Cannot hash value `{value}` of type `{type(value).__name__}`. "
@@ -1038,9 +1042,13 @@ def _get_component_prop_names(cls) -> set[str]:
10381042
name
10391043
for name in cls.get_fields()
10401044
if name in cls.get_props()
1041-
and types._issubclass(
1042-
types.value_inside_optional(types.get_field_type(cls, name)), Component
1045+
and isinstance(
1046+
field_type := types.value_inside_optional(
1047+
types.get_field_type(cls, name)
1048+
),
1049+
type,
10431050
)
1051+
and issubclass(field_type, Component)
10441052
}
10451053

10461054
def _get_components_in_props(self) -> Sequence[BaseComponent]:
@@ -1508,8 +1516,9 @@ def _iter_parent_classes_with_method(cls, method: str) -> Sequence[type[Componen
15081516
Returns:
15091517
A sequence of parent classes that define the method (differently than the base).
15101518
"""
1519+
current_class_method = getattr(Component, method, None)
15111520
seen_methods = (
1512-
{getattr(Component, method)} if hasattr(Component, method) else set()
1521+
{current_class_method} if current_class_method is not None else set()
15131522
)
15141523
clzs: list[type[Component]] = []
15151524
for clz in cls.mro():
@@ -1807,7 +1816,7 @@ def extract_var_hooks(hook: Var):
18071816

18081817
# Add the hook code from add_hooks for each parent class (this is reversed to preserve
18091818
# the order of the hooks in the final output)
1810-
for clz in reversed(tuple(self._iter_parent_classes_with_method("add_hooks"))):
1819+
for clz in reversed(self._iter_parent_classes_with_method("add_hooks")):
18111820
for hook in clz.add_hooks(self):
18121821
if isinstance(hook, Var):
18131822
extract_var_hooks(hook)
@@ -2457,7 +2466,7 @@ def _get_tag_name(cls, component: Component) -> str | None:
24572466
return None
24582467

24592468
# Compute the hash based on the rendered code.
2460-
code_hash = _deterministic_hash(rendered_code)
2469+
code_hash = _hash_str(_deterministic_hash(rendered_code))
24612470

24622471
# Format the tag name including the hash.
24632472
return format.format_state_name(

reflex/environment.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import dataclasses
77
import enum
88
import importlib
9-
import inspect
109
import multiprocessing
1110
import os
1211
import platform
@@ -159,7 +158,7 @@ def interpret_plugin_env(value: str, field_name: str) -> Plugin:
159158
msg = f"Failed to get plugin class {plugin_name!r} from module {import_path!r} for {field_name}: {e}"
160159
raise EnvironmentVarValueError(msg) from e
161160

162-
if not inspect.isclass(plugin_class) or not issubclass(plugin_class, Plugin):
161+
if not isinstance(plugin_class, type) or not issubclass(plugin_class, Plugin):
163162
msg = f"Invalid plugin class: {plugin_name!r} for {field_name}. Must be a subclass of Plugin."
164163
raise EnvironmentVarValueError(msg)
165164

@@ -236,7 +235,7 @@ def interpret_env_var_value(
236235
)
237236
for i, v in enumerate(value.split(":"))
238237
]
239-
if inspect.isclass(field_type) and issubclass(field_type, enum.Enum):
238+
if isinstance(field_type, type) and issubclass(field_type, enum.Enum):
240239
return interpret_enum_env(value, field_type, field_name)
241240

242241
msg = f"Invalid type for environment variable {field_name}: {field_type}. This is probably an issue in Reflex."

reflex/istate/data.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ def from_router_data(cls, router_data: dict) -> "HeaderData":
8383
)
8484

8585

86+
@serializer(to=dict)
87+
def _serialize_header_data(obj: HeaderData) -> dict:
88+
return {k.name: getattr(obj, k.name) for k in dataclasses.fields(obj)}
89+
90+
8691
@serializer(to=dict)
8792
def serialize_frozen_dict_str_str(obj: _FrozenDictStrStr) -> dict:
8893
"""Serialize a _FrozenDictStrStr object to a dict.
@@ -165,6 +170,11 @@ def from_router_data(cls, router_data: dict) -> "PageData":
165170
)
166171

167172

173+
@serializer(to=dict)
174+
def _serialize_page_data(obj: PageData) -> dict:
175+
return dataclasses.asdict(obj)
176+
177+
168178
@dataclasses.dataclass(frozen=True)
169179
class SessionData:
170180
"""An object containing session data."""
@@ -190,6 +200,11 @@ def from_router_data(cls, router_data: dict) -> "SessionData":
190200
)
191201

192202

203+
@serializer(to=dict)
204+
def _serialize_session_data(obj: SessionData) -> dict:
205+
return dataclasses.asdict(obj)
206+
207+
193208
@dataclasses.dataclass(frozen=True)
194209
class RouterData:
195210
"""An object containing RouterData."""

reflex/plugins/sitemap.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from pathlib import Path
66
from types import SimpleNamespace
77
from typing import TYPE_CHECKING, Literal, TypedDict
8-
from xml.dom import minidom
9-
from xml.etree.ElementTree import Element, SubElement, tostring
8+
from xml.etree.ElementTree import Element, SubElement, indent, tostring
109

1110
from typing_extensions import NotRequired
1211

@@ -104,10 +103,8 @@ def generate_xml(links: Sequence[SitemapLink]) -> str:
104103
if (priority := link.get("priority")) is not None:
105104
priority_element = SubElement(url, "priority")
106105
priority_element.text = str(priority)
107-
108-
rough_string = tostring(urlset, "utf-8")
109-
reparsed = minidom.parseString(rough_string)
110-
return reparsed.toprettyxml(indent=" ")
106+
indent(urlset, " ")
107+
return tostring(urlset, encoding="utf-8", xml_declaration=True).decode("utf-8")
111108

112109

113110
def is_route_dynamic(route: str) -> bool:

reflex/reflex.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,13 @@ def run(
360360
default=False,
361361
help="Run the command without making any changes.",
362362
)
363-
def compile(dry: bool):
363+
@click.option(
364+
"--rich/--no-rich",
365+
default=True,
366+
is_flag=True,
367+
help="Whether to use rich progress bars.",
368+
)
369+
def compile(dry: bool, rich: bool):
364370
"""Compile the app in the current directory."""
365371
import time
366372

@@ -371,7 +377,7 @@ def compile(dry: bool):
371377
_init(name=get_config().app_name)
372378
get_config(reload=True)
373379
starting_time = time.monotonic()
374-
prerequisites.get_compiled_app(dry_run=dry)
380+
prerequisites.get_compiled_app(dry_run=dry, use_rich=rich)
375381
elapsed_time = time.monotonic() - starting_time
376382
console.success(f"App compiled successfully in {elapsed_time:.3f} seconds.")
377383

reflex/state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,7 +1810,7 @@ async def _process_event(
18101810
hinted_args = value_inside_optional(hinted_args)
18111811
if (
18121812
isinstance(value, dict)
1813-
and inspect.isclass(hinted_args)
1813+
and isinstance(hinted_args, type)
18141814
and not types.is_generic_alias(hinted_args) # py3.10
18151815
):
18161816
if issubclass(hinted_args, Model):
@@ -2341,7 +2341,7 @@ def _serialize_type(type_: Any) -> str:
23412341
Returns:
23422342
The serialized type.
23432343
"""
2344-
if not inspect.isclass(type_):
2344+
if not isinstance(type_, type):
23452345
return f"{type_}"
23462346
return f"{type_.__module__}.{type_.__qualname__}"
23472347

reflex/testing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ def get_app_global_source(key: str, value: Any):
472472
Returns:
473473
The rendered app global code.
474474
"""
475-
if not inspect.isclass(value) and not inspect.isfunction(value):
475+
if not isinstance(value, type) and not inspect.isfunction(value):
476476
return f"{key} = {value!r}"
477477
return inspect.getsource(value)
478478

0 commit comments

Comments
 (0)