Skip to content

Commit 14cd91b

Browse files
committed
improve determinism
1 parent 314f485 commit 14cd91b

File tree

11 files changed

+88
-95
lines changed

11 files changed

+88
-95
lines changed

reflex/compiler/compiler.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def _compile_page(component: BaseComponent) -> str:
161161
# Compile the code to render the component.
162162
return templates.page_template(
163163
imports=imports,
164-
dynamic_imports=component._get_all_dynamic_imports(),
164+
dynamic_imports=sorted(component._get_all_dynamic_imports()),
165165
custom_codes=component._get_all_custom_code(),
166166
hooks=component._get_all_hooks(),
167167
render=component.render(),
@@ -379,7 +379,7 @@ def _compile_components(
379379
templates.custom_component_template(
380380
imports=utils.compile_imports(imports),
381381
components=component_renders,
382-
dynamic_imports=dynamic_imports,
382+
dynamic_imports=sorted(dynamic_imports),
383383
custom_codes=custom_codes,
384384
),
385385
imports,
@@ -435,9 +435,7 @@ def get_shared_components_recursive(component: BaseComponent):
435435
rendered_components.update(dict.fromkeys(dynamic_imports))
436436

437437
# Include custom code in the shared component.
438-
rendered_components.update(
439-
dict.fromkeys(component._get_all_custom_code(export=True)),
440-
)
438+
rendered_components.update(component._get_all_custom_code(export=True))
441439

442440
# Include all imports in the shared component.
443441
all_import_dicts.append(component._get_all_imports())

reflex/compiler/templates.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def document_root_template(*, imports: list[_ImportDict], document: dict[str, An
164164
def app_root_template(
165165
*,
166166
imports: list[_ImportDict],
167-
custom_codes: set[str],
167+
custom_codes: Iterable[str],
168168
hooks: dict[str, VarData | None],
169169
window_libraries: list[tuple[str, str]],
170170
render: dict[str, Any],

reflex/components/base/bare.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def _get_all_dynamic_imports(self) -> set[str]:
129129
dynamic_imports |= component._get_all_dynamic_imports()
130130
return dynamic_imports
131131

132-
def _get_all_custom_code(self) -> set[str]:
132+
def _get_all_custom_code(self) -> dict[str, None]:
133133
"""Get custom code for the component.
134134
135135
Returns:
@@ -166,7 +166,7 @@ def _get_all_app_wrap_components(
166166
)
167167
return app_wrap_components
168168

169-
def _get_all_refs(self) -> set[str]:
169+
def _get_all_refs(self) -> dict[str, None]:
170170
"""Get the refs for the children of the component.
171171
172172
Returns:

reflex/components/component.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import inspect
1111
import typing
1212
from abc import ABC, ABCMeta, abstractmethod
13-
from collections.abc import Callable, Iterator, Mapping, Sequence
13+
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
1414
from dataclasses import _MISSING_TYPE, MISSING
1515
from functools import wraps
1616
from hashlib import md5
@@ -374,15 +374,15 @@ def _get_all_dynamic_imports(self) -> set[str]:
374374
"""
375375

376376
@abstractmethod
377-
def _get_all_custom_code(self) -> set[str]:
377+
def _get_all_custom_code(self) -> dict[str, None]:
378378
"""Get custom code for the component.
379379
380380
Returns:
381381
The custom code.
382382
"""
383383

384384
@abstractmethod
385-
def _get_all_refs(self) -> set[str]:
385+
def _get_all_refs(self) -> dict[str, None]:
386386
"""Get the refs for the children of the component.
387387
388388
Returns:
@@ -1003,13 +1003,13 @@ def _render(self, props: dict[str, Any] | None = None) -> Tag:
10031003

10041004
@classmethod
10051005
@functools.cache
1006-
def get_props(cls) -> set[str]:
1006+
def get_props(cls) -> Iterable[str]:
10071007
"""Get the unique fields for the component.
10081008
10091009
Returns:
10101010
The unique fields.
10111011
"""
1012-
return set(cls.get_js_fields())
1012+
return cls.get_js_fields()
10131013

10141014
@classmethod
10151015
@functools.cache
@@ -1509,27 +1509,27 @@ def _get_custom_code(self) -> str | None:
15091509
"""
15101510
return None
15111511

1512-
def _get_all_custom_code(self) -> set[str]:
1512+
def _get_all_custom_code(self) -> dict[str, None]:
15131513
"""Get custom code for the component and its children.
15141514
15151515
Returns:
15161516
The custom code.
15171517
"""
15181518
# Store the code in a set to avoid duplicates.
1519-
code = set()
1519+
code: dict[str, None] = {}
15201520

15211521
# Add the custom code for this component.
15221522
custom_code = self._get_custom_code()
15231523
if custom_code is not None:
1524-
code.add(custom_code)
1524+
code[custom_code] = None
15251525

15261526
for component in self._get_components_in_props():
15271527
code |= component._get_all_custom_code()
15281528

15291529
# Add the custom code from add_custom_code method.
15301530
for clz in self._iter_parent_classes_with_method("add_custom_code"):
15311531
for item in clz.add_custom_code(self):
1532-
code.add(item)
1532+
code[item] = None
15331533

15341534
# Add the custom code for the children.
15351535
for child in self.children:
@@ -1814,7 +1814,7 @@ def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
18141814

18151815
# Add the hook code for the children.
18161816
for child in self.children:
1817-
code = {**code, **child._get_all_hooks_internal()}
1817+
code.update(child._get_all_hooks_internal())
18181818

18191819
return code
18201820

@@ -1838,7 +1838,7 @@ def _get_all_hooks(self) -> dict[str, VarData | None]:
18381838

18391839
# Add the hook code for the children.
18401840
for child in self.children:
1841-
code = {**code, **child._get_all_hooks()}
1841+
code.update(child._get_all_hooks())
18421842

18431843
return code
18441844

@@ -1853,16 +1853,16 @@ def get_ref(self) -> str | None:
18531853
return None
18541854
return format.format_ref(self.id)
18551855

1856-
def _get_all_refs(self) -> set[str]:
1856+
def _get_all_refs(self) -> dict[str, None]:
18571857
"""Get the refs for the children of the component.
18581858
18591859
Returns:
18601860
The refs for the children.
18611861
"""
1862-
refs = set()
1862+
refs = {}
18631863
ref = self.get_ref()
18641864
if ref is not None:
1865-
refs.add(ref)
1865+
refs[ref] = None
18661866
for child in self.children:
18671867
refs |= child._get_all_refs()
18681868
for component in self._get_components_in_props():
@@ -1994,7 +1994,7 @@ def get_args_spec(key: str) -> types.ArgsSpec | Sequence[types.ArgsSpec]:
19941994
)
19951995

19961996
to_camel_cased_props = {
1997-
format.to_camel_case(key + MEMO_MARKER)
1997+
format.to_camel_case(key + MEMO_MARKER): None
19981998
for key in props
19991999
if key not in event_types
20002000
}
@@ -2048,13 +2048,13 @@ def __hash__(self) -> int:
20482048
return hash(self.tag)
20492049

20502050
@classmethod
2051-
def get_props(cls) -> set[str]:
2051+
def get_props(cls) -> Iterable[str]:
20522052
"""Get the props for the component.
20532053
20542054
Returns:
20552055
The set of component props.
20562056
"""
2057-
return set()
2057+
return ()
20582058

20592059
@staticmethod
20602060
def _get_event_spec_from_args_spec(name: str, event: EventChain) -> Callable:
@@ -2656,7 +2656,7 @@ def _get_all_dynamic_imports(self) -> set[str]:
26562656
return set()
26572657
return self.component._get_all_dynamic_imports()
26582658

2659-
def _get_all_custom_code(self, export: bool = False) -> set[str]:
2659+
def _get_all_custom_code(self, export: bool = False) -> dict[str, None]:
26602660
"""Get custom code for the component.
26612661
26622662
Args:
@@ -2666,19 +2666,19 @@ def _get_all_custom_code(self, export: bool = False) -> set[str]:
26662666
The custom code.
26672667
"""
26682668
if self.rendered_as_shared:
2669-
return set()
2670-
return self.component._get_all_custom_code().union(
2671-
{self._render_stateful_code(export=export)}
2669+
return {}
2670+
return self.component._get_all_custom_code() | (
2671+
{self._render_stateful_code(export=export): None}
26722672
)
26732673

2674-
def _get_all_refs(self) -> set[str]:
2674+
def _get_all_refs(self) -> dict[str, None]:
26752675
"""Get the refs for the children of the component.
26762676
26772677
Returns:
26782678
The refs for the children.
26792679
"""
26802680
if self.rendered_as_shared:
2681-
return set()
2681+
return {}
26822682
return self.component._get_all_refs()
26832683

26842684
def render(self) -> dict:

reflex/components/core/debounce.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def create(cls, *children: Component, **props: Any) -> Component:
132132
component.children = child.children
133133
component._rename_props = child._rename_props # pyright: ignore[reportAttributeAccessIssue]
134134
outer_get_all_custom_code = component._get_all_custom_code
135-
component._get_all_custom_code = lambda: outer_get_all_custom_code().union(
135+
component._get_all_custom_code = lambda: outer_get_all_custom_code() | (
136136
child._get_all_custom_code()
137137
)
138138
return component

reflex/components/core/foreach.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import functools
66
import inspect
77
from collections.abc import Callable, Iterable
8+
from hashlib import md5
89
from typing import Any
910

1011
from reflex.components.base.fragment import Fragment
@@ -141,25 +142,12 @@ def _render(self) -> IterTag:
141142
else:
142143
render_fn = self.render_fn
143144
# Otherwise, use a deterministic index, based on the render function bytecode.
144-
code_hash = (
145-
hash(
146-
getattr(
147-
render_fn,
148-
"__code__",
149-
(
150-
repr(self.render_fn)
151-
if not isinstance(render_fn, functools.partial)
152-
else render_fn.func.__code__
153-
),
154-
)
155-
)
156-
.to_bytes(
157-
length=8,
158-
byteorder="big",
159-
signed=True,
160-
)
161-
.hex()
162-
)
145+
if (render_fn_code := getattr(render_fn, "__code__", None)) is not None:
146+
code_hash = md5(render_fn_code.co_code).hexdigest()
147+
elif isinstance(render_fn, functools.partial):
148+
code_hash = md5(render_fn.func.__code__.co_code).hexdigest()
149+
else:
150+
code_hash = md5(repr(render_fn).encode()).hexdigest()
163151
props["index_var_name"] = f"index_{code_hash}"
164152

165153
return IterTag(

reflex/components/core/upload.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def create(cls, *children, **props) -> Component:
260260
props["class_name"] = ["rx-Upload", *given_class_name]
261261

262262
# get only upload component props
263-
supported_props = cls.get_props().union({"on_drop"})
263+
supported_props = set(cls.get_props()) | {"on_drop"}
264264
upload_props = {
265265
key: value for key, value in props.items() if key in supported_props
266266
}

reflex/components/dynamic.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ def make_component(component: Component) -> str:
8181
rendered_components.update(dict.fromkeys(dynamic_imports))
8282

8383
# Include custom code in the shared component.
84-
rendered_components.update(
85-
dict.fromkeys(component._get_all_custom_code()),
86-
)
84+
rendered_components.update(component._get_all_custom_code())
8785

8886
rendered_components[
8987
templates.stateful_component_template(

reflex/components/el/elements/forms.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -770,17 +770,17 @@ def _exclude_props(self) -> list[str]:
770770
"enter_key_submit",
771771
]
772772

773-
def _get_all_custom_code(self) -> set[str]:
773+
def _get_all_custom_code(self) -> dict[str, None]:
774774
"""Include the custom code for auto_height and enter_key_submit functionality.
775775
776776
Returns:
777777
The custom code for the component.
778778
"""
779779
custom_code = super()._get_all_custom_code()
780780
if self.auto_height is not None:
781-
custom_code.add(AUTO_HEIGHT_JS)
781+
custom_code[AUTO_HEIGHT_JS] = None
782782
if self.enter_key_submit is not None:
783-
custom_code.add(ENTER_KEY_SUBMIT_JS)
783+
custom_code[ENTER_KEY_SUBMIT_JS] = None
784784
return custom_code
785785

786786

reflex/vars/base.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import functools
1010
import inspect
1111
import json
12-
import random
1312
import re
1413
import string
1514
import uuid
@@ -50,6 +49,7 @@
5049
from reflex.constants.compiler import Hooks
5150
from reflex.constants.state import FIELD_MARKER
5251
from reflex.utils import console, exceptions, imports, serializers, types
52+
from reflex.utils.decorator import once
5353
from reflex.utils.exceptions import (
5454
ComputedVarSignatureError,
5555
UntypedComputedVarError,
@@ -3033,13 +3033,20 @@ def get_uuid_string_var() -> Var:
30333033
USED_VARIABLES = set()
30343034

30353035

3036+
@once
3037+
def _rng():
3038+
import random
3039+
3040+
return random.Random(42)
3041+
3042+
30363043
def get_unique_variable_name() -> str:
30373044
"""Get a unique variable name.
30383045
30393046
Returns:
30403047
The unique variable name.
30413048
"""
3042-
name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
3049+
name = "".join([_rng().choice(string.ascii_lowercase) for _ in range(8)])
30433050
if name not in USED_VARIABLES:
30443051
USED_VARIABLES.add(name)
30453052
return name

0 commit comments

Comments
 (0)