Skip to content

Commit 39a07b6

Browse files
adhami3310masenf
andauthored
deprecate state_auto_setters=True (#5739)
* deprecate state_auto_setters=True * make the examples use the `set_` * deprecate state auto settings at compile time (#5746) * move state_auth_setters deprecation warning to compile time * Also warn for setvar --------- Co-authored-by: Masen Furer <[email protected]>
1 parent 8d8e236 commit 39a07b6

File tree

7 files changed

+84
-15
lines changed

7 files changed

+84
-15
lines changed

reflex/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class BaseConfig:
237237
env_file: str | None = None
238238

239239
# Whether to automatically create setters for state base vars
240-
state_auto_setters: bool = True
240+
state_auto_setters: bool | None = None
241241

242242
# Whether to display the sticky "Built with Reflex" badge on all pages.
243243
show_built_with_reflex: bool | None = None

reflex/state.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,20 @@ def __call__(self, *args: Any) -> EventSpec:
225225
EventHandlerValueError: If the given Var name is not a str
226226
NotImplementedError: If the setter for the given Var is async
227227
"""
228+
from reflex.config import get_config
228229
from reflex.utils.exceptions import EventHandlerValueError
229230

231+
config = get_config()
232+
if config.state_auto_setters is None:
233+
console.deprecate(
234+
feature_name="state_auto_setters defaulting to True",
235+
reason="The default value will be changed to False in a future release. Set state_auto_setters explicitly or define setters explicitly. "
236+
f"Used {self.state_cls.__name__}.setvar without defining it.",
237+
deprecation_version="0.8.9",
238+
removal_version="0.9.0",
239+
dedupe=True,
240+
)
241+
230242
if args:
231243
if not isinstance(args[0], str):
232244
msg = f"Var name must be passed as a string, got {args[0]!r}"
@@ -1036,7 +1048,7 @@ def _init_var(cls, name: str, prop: Var):
10361048
)
10371049
raise VarTypeError(msg)
10381050
cls._set_var(name, prop)
1039-
if cls.is_user_defined() and get_config().state_auto_setters:
1051+
if cls.is_user_defined() and get_config().state_auto_setters is not False:
10401052
cls._create_setter(name, prop)
10411053
cls._set_default_value(name, prop)
10421054

@@ -1096,19 +1108,22 @@ def _set_var(cls, name: str, prop: Var):
10961108
setattr(cls, name, prop)
10971109

10981110
@classmethod
1099-
def _create_event_handler(cls, fn: Any):
1111+
def _create_event_handler(
1112+
cls, fn: Any, event_handler_cls: type[EventHandler] = EventHandler
1113+
):
11001114
"""Create an event handler for the given function.
11011115
11021116
Args:
11031117
fn: The function to create an event handler for.
1118+
event_handler_cls: The event handler class to use.
11041119
11051120
Returns:
11061121
The event handler.
11071122
"""
11081123
# Check if function has stored event_actions from decorator
11091124
event_actions = getattr(fn, "_rx_event_actions", {})
11101125

1111-
return EventHandler(
1126+
return event_handler_cls(
11121127
fn=fn, state_full_name=cls.get_full_name(), event_actions=event_actions
11131128
)
11141129

@@ -1125,9 +1140,34 @@ def _create_setter(cls, name: str, prop: Var):
11251140
name: The name of the var.
11261141
prop: The var to create a setter for.
11271142
"""
1143+
from reflex.config import get_config
1144+
1145+
config = get_config()
1146+
_create_event_handler_kwargs = {}
1147+
1148+
if config.state_auto_setters is None:
1149+
1150+
class EventHandlerDeprecatedSetter(EventHandler):
1151+
def __call__(self, *args, **kwargs):
1152+
console.deprecate(
1153+
feature_name="state_auto_setters defaulting to True",
1154+
reason="The default value will be changed to False in a future release. Set state_auto_setters explicitly or define setters explicitly. "
1155+
f"Used {setter_name} in {cls.__name__} without defining it.",
1156+
deprecation_version="0.8.9",
1157+
removal_version="0.9.0",
1158+
dedupe=True,
1159+
)
1160+
return super().__call__(*args, **kwargs)
1161+
1162+
_create_event_handler_kwargs["event_handler_cls"] = (
1163+
EventHandlerDeprecatedSetter
1164+
)
1165+
11281166
setter_name = Var._get_setter_name_for_name(name)
11291167
if setter_name not in cls.__dict__:
1130-
event_handler = cls._create_event_handler(prop._get_setter(name))
1168+
event_handler = cls._create_event_handler(
1169+
prop._get_setter(name), **_create_event_handler_kwargs
1170+
)
11311171
cls.event_handlers[setter_name] = event_handler
11321172
setattr(cls, setter_name, event_handler)
11331173

reflex/utils/console.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import inspect
88
import os
99
import shutil
10+
import sys
1011
import time
1112
from pathlib import Path
1213
from types import FrameType
@@ -244,23 +245,38 @@ def warn(msg: str, *, dedupe: bool = False, **kwargs):
244245
print_to_log_file(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
245246

246247

247-
def _get_first_non_framework_frame() -> FrameType | None:
248+
@once
249+
def _exclude_paths_from_frame_info() -> list[Path]:
250+
import importlib.util
251+
248252
import click
253+
import granian
254+
import socketio
249255
import typing_extensions
250256

251257
import reflex as rx
252258

253259
# Exclude utility modules that should never be the source of deprecated reflex usage.
254-
exclude_modules = [click, rx, typing_extensions]
260+
exclude_modules = [click, rx, typing_extensions, socketio, granian]
261+
modules_paths = [file for m in exclude_modules if (file := m.__file__)] + [
262+
spec.origin
263+
for m in [*sys.builtin_module_names, *sys.stdlib_module_names]
264+
if (spec := importlib.util.find_spec(m)) and spec.origin
265+
]
255266
exclude_roots = [
256267
p.parent.resolve() if (p := Path(file)).name == "__init__.py" else p.resolve()
257-
for m in exclude_modules
258-
if (file := m.__file__)
268+
for file in modules_paths
259269
]
260270
# Specifically exclude the reflex cli module.
261271
if reflex_bin := shutil.which(b"reflex"):
262272
exclude_roots.append(Path(reflex_bin.decode()))
263273

274+
return exclude_roots
275+
276+
277+
def _get_first_non_framework_frame() -> FrameType | None:
278+
exclude_roots = _exclude_paths_from_frame_info()
279+
264280
frame = inspect.currentframe()
265281
while frame := frame and frame.f_back:
266282
frame_path = Path(inspect.getfile(frame)).resolve()
@@ -297,13 +313,13 @@ def deprecate(
297313
filename = Path(origin_frame.f_code.co_filename)
298314
if filename.is_relative_to(Path.cwd()):
299315
filename = filename.relative_to(Path.cwd())
300-
loc = f"{filename}:{origin_frame.f_lineno}"
316+
loc = f" ({filename}:{origin_frame.f_lineno})"
301317
dedupe_key = f"{dedupe_key} {loc}"
302318

303319
if dedupe_key not in _EMITTED_DEPRECATION_WARNINGS:
304320
msg = (
305321
f"{feature_name} has been deprecated in version {deprecation_version}. {reason.rstrip('.').lstrip('. ')}. It will be completely "
306-
f"removed in {removal_version}. ({loc})"
322+
f"removed in {removal_version}.{loc}"
307323
)
308324
if _LOG_LEVEL <= LogLevel.WARNING:
309325
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)

reflex/vars/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ def _get_setter(self, name: str) -> Callable[[BaseState, Any], None]:
941941
Returns:
942942
A function that that creates a setter for the var.
943943
"""
944+
setter_name = Var._get_setter_name_for_name(name)
944945

945946
def setter(state: Any, value: Any):
946947
"""Get the setter for the var.
@@ -962,7 +963,7 @@ def setter(state: Any, value: Any):
962963

963964
setter.__annotations__["value"] = self._var_type
964965

965-
setter.__qualname__ = Var._get_setter_name_for_name(name)
966+
setter.__qualname__ = setter_name
966967

967968
return setter
968969

tests/integration/test_connection_banner.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ def ConnectionBanner():
2222
class State(rx.State):
2323
foo: int = 0
2424

25+
@rx.event
26+
def set_foo(self, foo: int):
27+
self.foo = foo
28+
2529
@rx.event
2630
async def delay(self):
2731
await asyncio.sleep(5)
@@ -33,7 +37,7 @@ def index():
3337
rx.button(
3438
"Increment",
3539
id="increment",
36-
on_click=State.set_foo(State.foo + 1), # pyright: ignore [reportAttributeAccessIssue]
40+
on_click=State.set_foo(State.foo + 1),
3741
),
3842
rx.button("Delay", id="delay", on_click=State.delay),
3943
)

tests/integration/test_exception_handlers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class TestAppState(rx.State):
2828

2929
react_error: bool = False
3030

31+
@rx.event
32+
def set_react_error(self, value: bool):
33+
self.react_error = value
34+
3135
def divide_by_number(self, number: int):
3236
"""Divide by number and print the result.
3337
@@ -54,7 +58,7 @@ def index():
5458
),
5559
rx.button(
5660
"induce_react_error",
57-
on_click=TestAppState.set_react_error(True), # pyright: ignore [reportAttributeAccessIssue]
61+
on_click=TestAppState.set_react_error(True),
5862
id="induce-react-error-btn",
5963
),
6064
rx.box(

tests/integration/test_lifespan.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ async def lifespan_task(inc: int = 1):
5353
class LifespanState(rx.State):
5454
interval: int = 100
5555

56+
@rx.event
57+
def set_interval(self, interval: int):
58+
self.interval = interval
59+
5660
@rx.var(cache=False)
5761
def task_global(self) -> int:
5862
return lifespan_task_global
@@ -73,7 +77,7 @@ def index():
7377
rx.moment(
7478
interval=LifespanState.interval, on_change=LifespanState.tick
7579
),
76-
on_click=LifespanState.set_interval( # pyright: ignore [reportAttributeAccessIssue]
80+
on_click=LifespanState.set_interval(
7781
rx.cond(LifespanState.interval, 0, 100)
7882
),
7983
id="toggle-tick",

0 commit comments

Comments
 (0)