reflex 0.6.5a3__py3-none-any.whl → 0.6.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (52) hide show
  1. reflex/.templates/web/utils/state.js +39 -14
  2. reflex/__init__.py +3 -1
  3. reflex/__init__.pyi +3 -0
  4. reflex/app.py +24 -4
  5. reflex/assets.py +95 -0
  6. reflex/base.py +2 -2
  7. reflex/components/base/error_boundary.py +99 -36
  8. reflex/components/base/error_boundary.pyi +3 -4
  9. reflex/components/component.py +29 -7
  10. reflex/components/core/cond.py +8 -0
  11. reflex/components/core/upload.py +8 -5
  12. reflex/components/datadisplay/code.py +1 -1
  13. reflex/components/datadisplay/logo.py +26 -13
  14. reflex/components/el/__init__.pyi +2 -0
  15. reflex/components/el/elements/__init__.py +1 -0
  16. reflex/components/el/elements/__init__.pyi +3 -0
  17. reflex/components/el/elements/forms.py +1 -0
  18. reflex/components/el/elements/forms.pyi +1 -0
  19. reflex/components/moment/moment.py +4 -3
  20. reflex/components/moment/moment.pyi +12 -2
  21. reflex/components/plotly/plotly.py +1 -1
  22. reflex/components/radix/primitives/drawer.py +5 -23
  23. reflex/components/radix/themes/base.py +3 -0
  24. reflex/components/radix/themes/components/segmented_control.py +3 -1
  25. reflex/components/radix/themes/components/segmented_control.pyi +7 -2
  26. reflex/components/radix/themes/components/text_field.py +3 -0
  27. reflex/components/radix/themes/components/text_field.pyi +4 -0
  28. reflex/components/recharts/recharts.py +2 -14
  29. reflex/components/recharts/recharts.pyi +0 -1
  30. reflex/components/sonner/toast.py +23 -12
  31. reflex/components/sonner/toast.pyi +6 -6
  32. reflex/config.py +60 -9
  33. reflex/constants/base.py +12 -0
  34. reflex/constants/installer.py +3 -3
  35. reflex/constants/style.py +1 -1
  36. reflex/event.py +22 -5
  37. reflex/experimental/assets.py +14 -36
  38. reflex/reflex.py +16 -35
  39. reflex/state.py +90 -25
  40. reflex/utils/exceptions.py +4 -0
  41. reflex/utils/prerequisites.py +174 -40
  42. reflex/utils/redir.py +13 -4
  43. reflex/utils/serializers.py +52 -1
  44. reflex/utils/telemetry.py +2 -1
  45. reflex/utils/types.py +52 -1
  46. reflex/vars/base.py +18 -4
  47. reflex/vars/function.py +283 -37
  48. {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/METADATA +3 -2
  49. {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/RECORD +52 -51
  50. {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/LICENSE +0 -0
  51. {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/WHEEL +0 -0
  52. {reflex-0.6.5a3.dist-info → reflex-0.6.6.dist-info}/entry_points.txt +0 -0
reflex/event.py CHANGED
@@ -45,6 +45,8 @@ from reflex.vars import VarData
45
45
  from reflex.vars.base import LiteralVar, Var
46
46
  from reflex.vars.function import (
47
47
  ArgsFunctionOperation,
48
+ ArgsFunctionOperationBuilder,
49
+ BuilderFunctionVar,
48
50
  FunctionArgs,
49
51
  FunctionStringVar,
50
52
  FunctionVar,
@@ -179,6 +181,18 @@ class EventActionsMixin:
179
181
  event_actions={"debounce": delay_ms, **self.event_actions},
180
182
  )
181
183
 
184
+ @property
185
+ def temporal(self):
186
+ """Do not queue the event if the backend is down.
187
+
188
+ Returns:
189
+ New EventHandler-like with temporal set to True.
190
+ """
191
+ return dataclasses.replace(
192
+ self,
193
+ event_actions={"temporal": True, **self.event_actions},
194
+ )
195
+
182
196
 
183
197
  @dataclasses.dataclass(
184
198
  init=True,
@@ -797,8 +811,7 @@ def scroll_to(elem_id: str, align_to_top: bool | Var[bool] = True) -> EventSpec:
797
811
  get_element_by_id = FunctionStringVar.create("document.getElementById")
798
812
 
799
813
  return run_script(
800
- get_element_by_id(elem_id)
801
- .call(elem_id)
814
+ get_element_by_id.call(elem_id)
802
815
  .to(ObjectVar)
803
816
  .scrollIntoView.to(FunctionVar)
804
817
  .call(align_to_top),
@@ -899,7 +912,7 @@ def remove_session_storage(key: str) -> EventSpec:
899
912
  )
900
913
 
901
914
 
902
- def set_clipboard(content: str) -> EventSpec:
915
+ def set_clipboard(content: Union[str, Var[str]]) -> EventSpec:
903
916
  """Set the text in content in the clipboard.
904
917
 
905
918
  Args:
@@ -1345,6 +1358,10 @@ def check_fn_match_arg_spec(
1345
1358
  EventFnArgMismatch: Raised if the number of mandatory arguments do not match
1346
1359
  """
1347
1360
  user_args = inspect.getfullargspec(user_func).args
1361
+ # Drop the first argument if it's a bound method
1362
+ if inspect.ismethod(user_func) and user_func.__self__ is not None:
1363
+ user_args = user_args[1:]
1364
+
1348
1365
  user_default_args = inspect.getfullargspec(user_func).defaults
1349
1366
  number_of_user_args = len(user_args) - number_of_bound_args
1350
1367
  number_of_user_default_args = len(user_default_args) if user_default_args else 0
@@ -1580,7 +1597,7 @@ class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
1580
1597
  )
1581
1598
 
1582
1599
 
1583
- class EventChainVar(FunctionVar, python_types=EventChain):
1600
+ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
1584
1601
  """Base class for event chain vars."""
1585
1602
 
1586
1603
 
@@ -1592,7 +1609,7 @@ class EventChainVar(FunctionVar, python_types=EventChain):
1592
1609
  # Note: LiteralVar is second in the inheritance list allowing it act like a
1593
1610
  # CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the
1594
1611
  # _cached_var_name property.
1595
- class LiteralEventChainVar(ArgsFunctionOperation, LiteralVar, EventChainVar):
1612
+ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainVar):
1596
1613
  """A literal event chain var."""
1597
1614
 
1598
1615
  _var_value: EventChain = dataclasses.field(default=None) # type: ignore
@@ -1,14 +1,15 @@
1
1
  """Helper functions for adding assets to the app."""
2
2
 
3
- import inspect
4
- from pathlib import Path
5
3
  from typing import Optional
6
4
 
7
- from reflex import constants
5
+ from reflex import assets
6
+ from reflex.utils import console
8
7
 
9
8
 
10
9
  def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
11
- """Add an asset to the app.
10
+ """DEPRECATED: use `rx.asset` with `shared=True` instead.
11
+
12
+ Add an asset to the app.
12
13
  Place the file next to your including python file.
13
14
  Copies the file to the app's external assets directory.
14
15
 
@@ -22,38 +23,15 @@ def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
22
23
  relative_filename: The relative filename of the asset.
23
24
  subfolder: The directory to place the asset in.
24
25
 
25
- Raises:
26
- FileNotFoundError: If the file does not exist.
27
- ValueError: If the module is None.
28
-
29
26
  Returns:
30
27
  The relative URL to the copied asset.
31
28
  """
32
- # Determine the file by which the asset is exposed.
33
- calling_file = inspect.stack()[1].filename
34
- module = inspect.getmodule(inspect.stack()[1][0])
35
- if module is None:
36
- raise ValueError("Module is None")
37
- caller_module_path = module.__name__.replace(".", "/")
38
-
39
- subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
40
-
41
- src_file = Path(calling_file).parent / relative_filename
42
-
43
- assets = constants.Dirs.APP_ASSETS
44
- external = constants.Dirs.EXTERNAL_APP_ASSETS
45
-
46
- if not src_file.exists():
47
- raise FileNotFoundError(f"File not found: {src_file}")
48
-
49
- # Create the asset folder in the currently compiling app.
50
- asset_folder = Path.cwd() / assets / external / subfolder
51
- asset_folder.mkdir(parents=True, exist_ok=True)
52
-
53
- dst_file = asset_folder / relative_filename
54
-
55
- if not dst_file.exists():
56
- dst_file.symlink_to(src_file)
57
-
58
- asset_url = f"/{external}/{subfolder}/{relative_filename}"
59
- return asset_url
29
+ console.deprecate(
30
+ feature_name="rx._x.asset",
31
+ reason="Use `rx.asset` with `shared=True` instead of `rx._x.asset`.",
32
+ deprecation_version="0.6.6",
33
+ removal_version="0.7.0",
34
+ )
35
+ return assets.asset(
36
+ relative_filename, shared=True, subfolder=subfolder, _stack_level=2
37
+ )
reflex/reflex.py CHANGED
@@ -11,16 +11,16 @@ import typer
11
11
  import typer.core
12
12
  from reflex_cli.deployments import deployments_cli
13
13
  from reflex_cli.utils import dependency
14
- from reflex_cli.v2.deployments import hosting_cli
14
+ from reflex_cli.v2.deployments import check_version, hosting_cli
15
15
 
16
16
  from reflex import constants
17
17
  from reflex.config import environment, get_config
18
18
  from reflex.custom_components.custom_components import custom_components_cli
19
19
  from reflex.state import reset_disk_state_manager
20
- from reflex.utils import console, redir, telemetry
20
+ from reflex.utils import console, telemetry
21
21
 
22
22
  # Disable typer+rich integration for help panels
23
- typer.core.rich = False # type: ignore
23
+ typer.core.rich = None # type: ignore
24
24
 
25
25
  # Create the app.
26
26
  try:
@@ -89,30 +89,8 @@ def _init(
89
89
  # Set up the web project.
90
90
  prerequisites.initialize_frontend_dependencies()
91
91
 
92
- # Integrate with reflex.build.
93
- generation_hash = None
94
- if ai:
95
- if template is None:
96
- # If AI is requested and no template specified, redirect the user to reflex.build.
97
- generation_hash = redir.reflex_build_redirect()
98
- elif prerequisites.is_generation_hash(template):
99
- # Otherwise treat the template as a generation hash.
100
- generation_hash = template
101
- else:
102
- console.error(
103
- "Cannot use `--template` option with `--ai` option. Please remove `--template` option."
104
- )
105
- raise typer.Exit(2)
106
- template = constants.Templates.DEFAULT
107
-
108
92
  # Initialize the app.
109
- prerequisites.initialize_app(app_name, template)
110
-
111
- # If a reflex.build generation hash is available, download the code and apply it to the main module.
112
- if generation_hash:
113
- prerequisites.initialize_main_module_index_from_generation(
114
- app_name, generation_hash=generation_hash
115
- )
93
+ template = prerequisites.initialize_app(app_name, template, ai)
116
94
 
117
95
  # Initialize the .gitignore.
118
96
  prerequisites.initialize_gitignore()
@@ -120,8 +98,9 @@ def _init(
120
98
  # Initialize the requirements.txt.
121
99
  prerequisites.initialize_requirements_txt()
122
100
 
101
+ template_msg = f" using the {template} template" if template else ""
123
102
  # Finish initializing the app.
124
- console.success(f"Initialized {app_name}")
103
+ console.success(f"Initialized {app_name}{template_msg}")
125
104
 
126
105
 
127
106
  @cli.command()
@@ -389,6 +368,8 @@ def loginv2(loglevel: constants.LogLevel = typer.Option(config.loglevel)):
389
368
  """Authenicate with experimental Reflex hosting service."""
390
369
  from reflex_cli.v2 import cli as hosting_cli
391
370
 
371
+ check_version()
372
+
392
373
  hosting_cli.login()
393
374
 
394
375
 
@@ -417,11 +398,13 @@ def logoutv2(
417
398
  """Log out of access to Reflex hosting service."""
418
399
  from reflex_cli.v2.utils import hosting
419
400
 
401
+ check_version()
402
+
420
403
  console.set_log_level(loglevel)
421
404
 
422
405
  hosting.log_out_on_browser()
423
406
  console.debug("Deleting access token from config locally")
424
- hosting.delete_token_from_config(include_invitation_code=True)
407
+ hosting.delete_token_from_config()
425
408
 
426
409
 
427
410
  db_cli = typer.Typer()
@@ -636,7 +619,7 @@ def deployv2(
636
619
  list(),
637
620
  "-r",
638
621
  "--region",
639
- help="The regions to deploy to. For multiple envs, repeat this option, e.g. --region sjc --region iad",
622
+ help="The regions to deploy to. `reflex apps regions` For multiple envs, repeat this option, e.g. --region sjc --region iad",
640
623
  ),
641
624
  envs: List[str] = typer.Option(
642
625
  list(),
@@ -646,13 +629,12 @@ def deployv2(
646
629
  vmtype: Optional[str] = typer.Option(
647
630
  None,
648
631
  "--vmtype",
649
- help="Vm type id. Run reflex apps vmtypes list to get options.",
632
+ help="Vm type id. Run `reflex apps vmtypes` to get options.",
650
633
  ),
651
634
  hostname: Optional[str] = typer.Option(
652
635
  None,
653
636
  "--hostname",
654
637
  help="The hostname of the frontend.",
655
- hidden=True,
656
638
  ),
657
639
  interactive: bool = typer.Option(
658
640
  True,
@@ -662,7 +644,6 @@ def deployv2(
662
644
  None,
663
645
  "--envfile",
664
646
  help="The path to an env file to use. Will override any envs set manually.",
665
- hidden=True,
666
647
  ),
667
648
  loglevel: constants.LogLevel = typer.Option(
668
649
  config.loglevel, help="The log level to use."
@@ -670,14 +651,12 @@ def deployv2(
670
651
  project: Optional[str] = typer.Option(
671
652
  None,
672
653
  "--project",
673
- help="project to deploy to",
674
- hidden=True,
654
+ help="project id to deploy to",
675
655
  ),
676
656
  token: Optional[str] = typer.Option(
677
657
  None,
678
658
  "--token",
679
659
  help="token to use for auth",
680
- hidden=True,
681
660
  ),
682
661
  ):
683
662
  """Deploy the app to the Reflex hosting service."""
@@ -687,6 +666,8 @@ def deployv2(
687
666
  from reflex.utils import export as export_utils
688
667
  from reflex.utils import prerequisites
689
668
 
669
+ check_version()
670
+
690
671
  # Set the log level.
691
672
  console.set_log_level(loglevel)
692
673
 
reflex/state.py CHANGED
@@ -43,7 +43,7 @@ from sqlalchemy.orm import DeclarativeBase
43
43
  from typing_extensions import Self
44
44
 
45
45
  from reflex import event
46
- from reflex.config import get_config
46
+ from reflex.config import PerformanceMode, get_config
47
47
  from reflex.istate.data import RouterData
48
48
  from reflex.istate.storage import ClientStorageBase
49
49
  from reflex.model import Model
@@ -62,6 +62,13 @@ try:
62
62
  except ModuleNotFoundError:
63
63
  import pydantic
64
64
 
65
+ from pydantic import BaseModel as BaseModelV2
66
+
67
+ try:
68
+ from pydantic.v1 import BaseModel as BaseModelV1
69
+ except ModuleNotFoundError:
70
+ BaseModelV1 = BaseModelV2
71
+
65
72
  import wrapt
66
73
  from redis.asyncio import Redis
67
74
  from redis.exceptions import ResponseError
@@ -87,8 +94,10 @@ from reflex.utils.exceptions import (
87
94
  ImmutableStateError,
88
95
  InvalidStateManagerMode,
89
96
  LockExpiredError,
97
+ ReflexRuntimeError,
90
98
  SetUndefinedStateVarError,
91
99
  StateSchemaMismatchError,
100
+ StateTooLargeError,
92
101
  )
93
102
  from reflex.utils.exec import is_testing_env
94
103
  from reflex.utils.serializers import serializer
@@ -109,10 +118,11 @@ Delta = Dict[str, Any]
109
118
  var = computed_var
110
119
 
111
120
 
112
- # If the state is this large, it's considered a performance issue.
113
- TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
114
- # Only warn about each state class size once.
115
- _WARNED_ABOUT_STATE_SIZE: Set[str] = set()
121
+ if environment.REFLEX_PERF_MODE.get() != PerformanceMode.OFF:
122
+ # If the state is this large, it's considered a performance issue.
123
+ TOO_LARGE_SERIALIZED_STATE = environment.REFLEX_STATE_SIZE_LIMIT.get() * 1024
124
+ # Only warn about each state class size once.
125
+ _WARNED_ABOUT_STATE_SIZE: Set[str] = set()
116
126
 
117
127
  # Errors caught during pickling of state
118
128
  HANDLED_PICKLE_ERRORS = (
@@ -387,6 +397,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
387
397
  "State classes should not be instantiated directly in a Reflex app. "
388
398
  "See https://reflex.dev/docs/state/ for further information."
389
399
  )
400
+ if type(self)._mixin:
401
+ raise ReflexRuntimeError(
402
+ f"{type(self).__name__} is a state mixin and cannot be instantiated directly."
403
+ )
390
404
  kwargs["parent_state"] = parent_state
391
405
  super().__init__()
392
406
  for name, value in kwargs.items():
@@ -1243,7 +1257,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1243
1257
  if parent_state is not None:
1244
1258
  return getattr(parent_state, name)
1245
1259
 
1246
- if isinstance(value, MutableProxy.__mutable_types__) and (
1260
+ if MutableProxy._is_mutable_type(value) and (
1247
1261
  name in super().__getattribute__("base_vars") or name in backend_vars
1248
1262
  ):
1249
1263
  # track changes in mutable containers (list, dict, set, etc)
@@ -1734,7 +1748,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1734
1748
  if value is None:
1735
1749
  continue
1736
1750
  hinted_args = value_inside_optional(hinted_args)
1737
- if isinstance(value, dict) and inspect.isclass(hinted_args):
1751
+ if (
1752
+ isinstance(value, dict)
1753
+ and inspect.isclass(hinted_args)
1754
+ and not types.is_generic_alias(hinted_args) # py3.9-py3.10
1755
+ ):
1738
1756
  if issubclass(hinted_args, Model):
1739
1757
  # Remove non-fields from the payload
1740
1758
  payload[arg] = hinted_args(
@@ -1745,7 +1763,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1745
1763
  }
1746
1764
  )
1747
1765
  elif dataclasses.is_dataclass(hinted_args) or issubclass(
1748
- hinted_args, Base
1766
+ hinted_args, (Base, BaseModelV1, BaseModelV2)
1749
1767
  ):
1750
1768
  payload[arg] = hinted_args(**value)
1751
1769
  if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
@@ -1890,7 +1908,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1890
1908
  )
1891
1909
 
1892
1910
  subdelta: Dict[str, Any] = {
1893
- prop: self.get_value(getattr(self, prop))
1911
+ prop: self.get_value(prop)
1894
1912
  for prop in delta_vars
1895
1913
  if not types.is_backend_base_variable(prop, type(self))
1896
1914
  }
@@ -1941,6 +1959,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1941
1959
  if var in self.base_vars or var in self._backend_vars:
1942
1960
  self._was_touched = True
1943
1961
  break
1962
+ if var == constants.ROUTER_DATA and self.parent_state is None:
1963
+ self._was_touched = True
1964
+ break
1944
1965
 
1945
1966
  def _get_was_touched(self) -> bool:
1946
1967
  """Check current dirty_vars and flag to determine if state instance was modified.
@@ -1982,9 +2003,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1982
2003
  Returns:
1983
2004
  The value of the field.
1984
2005
  """
1985
- if isinstance(key, MutableProxy):
1986
- return super().get_value(key.__wrapped__)
1987
- return super().get_value(key)
2006
+ value = super().get_value(key)
2007
+ if isinstance(value, MutableProxy):
2008
+ return value.__wrapped__
2009
+ return value
1988
2010
 
1989
2011
  def dict(
1990
2012
  self, include_computed: bool = True, initial: bool = False, **kwargs
@@ -2006,8 +2028,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2006
2028
  self._mark_dirty()
2007
2029
 
2008
2030
  base_vars = {
2009
- prop_name: self.get_value(getattr(self, prop_name))
2010
- for prop_name in self.base_vars
2031
+ prop_name: self.get_value(prop_name) for prop_name in self.base_vars
2011
2032
  }
2012
2033
  if initial and include_computed:
2013
2034
  computed_vars = {
@@ -2016,7 +2037,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2016
2037
  cv._initial_value
2017
2038
  if is_computed_var(cv)
2018
2039
  and not isinstance(cv._initial_value, types.Unset)
2019
- else self.get_value(getattr(self, prop_name))
2040
+ else self.get_value(prop_name)
2020
2041
  )
2021
2042
  for prop_name, cv in self.computed_vars.items()
2022
2043
  if not cv._backend
@@ -2024,7 +2045,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2024
2045
  elif include_computed:
2025
2046
  computed_vars = {
2026
2047
  # Include the computed vars.
2027
- prop_name: self.get_value(getattr(self, prop_name))
2048
+ prop_name: self.get_value(prop_name)
2028
2049
  for prop_name, cv in self.computed_vars.items()
2029
2050
  if not cv._backend
2030
2051
  }
@@ -2092,7 +2113,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2092
2113
  state["__dict__"].pop(inherited_var_name, None)
2093
2114
  return state
2094
2115
 
2095
- def _warn_if_too_large(
2116
+ def _check_state_size(
2096
2117
  self,
2097
2118
  pickle_state_size: int,
2098
2119
  ):
@@ -2100,6 +2121,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2100
2121
 
2101
2122
  Args:
2102
2123
  pickle_state_size: The size of the pickled state.
2124
+
2125
+ Raises:
2126
+ StateTooLargeError: If the state is too large.
2103
2127
  """
2104
2128
  state_full_name = self.get_full_name()
2105
2129
  if (
@@ -2107,10 +2131,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2107
2131
  and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
2108
2132
  and self.substates
2109
2133
  ):
2110
- console.warn(
2134
+ msg = (
2111
2135
  f"State {state_full_name} serializes to {pickle_state_size} bytes "
2112
- "which may present performance issues. Consider reducing the size of this state."
2136
+ + "which may present performance issues. Consider reducing the size of this state."
2113
2137
  )
2138
+ if environment.REFLEX_PERF_MODE.get() == PerformanceMode.WARN:
2139
+ console.warn(msg)
2140
+ elif environment.REFLEX_PERF_MODE.get() == PerformanceMode.RAISE:
2141
+ raise StateTooLargeError(msg)
2114
2142
  _WARNED_ABOUT_STATE_SIZE.add(state_full_name)
2115
2143
 
2116
2144
  @classmethod
@@ -2152,7 +2180,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2152
2180
  """
2153
2181
  try:
2154
2182
  pickle_state = pickle.dumps((self._to_schema(), self))
2155
- self._warn_if_too_large(len(pickle_state))
2183
+ if environment.REFLEX_PERF_MODE.get() != PerformanceMode.OFF:
2184
+ self._check_state_size(len(pickle_state))
2156
2185
  return pickle_state
2157
2186
  except HANDLED_PICKLE_ERRORS as og_pickle_error:
2158
2187
  error = (
@@ -2367,6 +2396,23 @@ class ComponentState(State, mixin=True):
2367
2396
  # The number of components created from this class.
2368
2397
  _per_component_state_instance_count: ClassVar[int] = 0
2369
2398
 
2399
+ def __init__(self, *args, **kwargs):
2400
+ """Do not allow direct initialization of the ComponentState.
2401
+
2402
+ Args:
2403
+ *args: The args to pass to the State init method.
2404
+ **kwargs: The kwargs to pass to the State init method.
2405
+
2406
+ Raises:
2407
+ ReflexRuntimeError: If the ComponentState is initialized directly.
2408
+ """
2409
+ if type(self)._mixin:
2410
+ raise ReflexRuntimeError(
2411
+ f"{ComponentState.__name__} {type(self).__name__} is not meant to be initialized directly. "
2412
+ + "Use the `create` method to create a new instance and access the state via the `State` attribute."
2413
+ )
2414
+ super().__init__(*args, **kwargs)
2415
+
2370
2416
  @classmethod
2371
2417
  def __init_subclass__(cls, mixin: bool = True, **kwargs):
2372
2418
  """Overwrite mixin default to True.
@@ -3526,7 +3572,16 @@ class MutableProxy(wrapt.ObjectProxy):
3526
3572
  pydantic.BaseModel.__dict__
3527
3573
  )
3528
3574
 
3529
- __mutable_types__ = (list, dict, set, Base, DeclarativeBase)
3575
+ # These types will be wrapped in MutableProxy
3576
+ __mutable_types__ = (
3577
+ list,
3578
+ dict,
3579
+ set,
3580
+ Base,
3581
+ DeclarativeBase,
3582
+ BaseModelV2,
3583
+ BaseModelV1,
3584
+ )
3530
3585
 
3531
3586
  def __init__(self, wrapped: Any, state: BaseState, field_name: str):
3532
3587
  """Create a proxy for a mutable object that tracks changes.
@@ -3566,6 +3621,18 @@ class MutableProxy(wrapt.ObjectProxy):
3566
3621
  if wrapped is not None:
3567
3622
  return wrapped(*args, **(kwargs or {}))
3568
3623
 
3624
+ @classmethod
3625
+ def _is_mutable_type(cls, value: Any) -> bool:
3626
+ """Check if a value is of a mutable type and should be wrapped.
3627
+
3628
+ Args:
3629
+ value: The value to check.
3630
+
3631
+ Returns:
3632
+ Whether the value is of a mutable type.
3633
+ """
3634
+ return isinstance(value, cls.__mutable_types__)
3635
+
3569
3636
  def _wrap_recursive(self, value: Any) -> Any:
3570
3637
  """Wrap a value recursively if it is mutable.
3571
3638
 
@@ -3576,9 +3643,7 @@ class MutableProxy(wrapt.ObjectProxy):
3576
3643
  The wrapped value.
3577
3644
  """
3578
3645
  # Recursively wrap mutable types, but do not re-wrap MutableProxy instances.
3579
- if isinstance(value, self.__mutable_types__) and not isinstance(
3580
- value, MutableProxy
3581
- ):
3646
+ if self._is_mutable_type(value) and not isinstance(value, MutableProxy):
3582
3647
  return type(self)(
3583
3648
  wrapped=value,
3584
3649
  state=self._self_state,
@@ -3636,7 +3701,7 @@ class MutableProxy(wrapt.ObjectProxy):
3636
3701
  self._wrap_recursive_decorator,
3637
3702
  )
3638
3703
 
3639
- if isinstance(value, self.__mutable_types__) and __name not in (
3704
+ if self._is_mutable_type(value) and __name not in (
3640
3705
  "__wrapped__",
3641
3706
  "_self_state",
3642
3707
  ):
@@ -151,6 +151,10 @@ class InvalidPropValueError(ReflexError):
151
151
  """Raised when a prop value is invalid."""
152
152
 
153
153
 
154
+ class StateTooLargeError(ReflexError):
155
+ """Raised when the state is too large to be serialized."""
156
+
157
+
154
158
  class SystemPackageMissingError(ReflexError):
155
159
  """Raised when a system package is missing."""
156
160