reflex 0.6.5a2__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 +99 -28
  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.5a2.dist-info → reflex-0.6.6.dist-info}/METADATA +3 -2
  49. {reflex-0.6.5a2.dist-info → reflex-0.6.6.dist-info}/RECORD +52 -51
  50. {reflex-0.6.5a2.dist-info → reflex-0.6.6.dist-info}/LICENSE +0 -0
  51. {reflex-0.6.5a2.dist-info → reflex-0.6.6.dist-info}/WHEEL +0 -0
  52. {reflex-0.6.5a2.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,9 +43,10 @@ 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
+ from reflex.model import Model
49
50
  from reflex.vars.base import (
50
51
  ComputedVar,
51
52
  DynamicRouteVar,
@@ -61,6 +62,13 @@ try:
61
62
  except ModuleNotFoundError:
62
63
  import pydantic
63
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
+
64
72
  import wrapt
65
73
  from redis.asyncio import Redis
66
74
  from redis.exceptions import ResponseError
@@ -86,8 +94,10 @@ from reflex.utils.exceptions import (
86
94
  ImmutableStateError,
87
95
  InvalidStateManagerMode,
88
96
  LockExpiredError,
97
+ ReflexRuntimeError,
89
98
  SetUndefinedStateVarError,
90
99
  StateSchemaMismatchError,
100
+ StateTooLargeError,
91
101
  )
92
102
  from reflex.utils.exec import is_testing_env
93
103
  from reflex.utils.serializers import serializer
@@ -108,10 +118,11 @@ Delta = Dict[str, Any]
108
118
  var = computed_var
109
119
 
110
120
 
111
- # If the state is this large, it's considered a performance issue.
112
- TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
113
- # Only warn about each state class size once.
114
- _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()
115
126
 
116
127
  # Errors caught during pickling of state
117
128
  HANDLED_PICKLE_ERRORS = (
@@ -386,6 +397,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
386
397
  "State classes should not be instantiated directly in a Reflex app. "
387
398
  "See https://reflex.dev/docs/state/ for further information."
388
399
  )
400
+ if type(self)._mixin:
401
+ raise ReflexRuntimeError(
402
+ f"{type(self).__name__} is a state mixin and cannot be instantiated directly."
403
+ )
389
404
  kwargs["parent_state"] = parent_state
390
405
  super().__init__()
391
406
  for name, value in kwargs.items():
@@ -1242,7 +1257,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1242
1257
  if parent_state is not None:
1243
1258
  return getattr(parent_state, name)
1244
1259
 
1245
- if isinstance(value, MutableProxy.__mutable_types__) and (
1260
+ if MutableProxy._is_mutable_type(value) and (
1246
1261
  name in super().__getattribute__("base_vars") or name in backend_vars
1247
1262
  ):
1248
1263
  # track changes in mutable containers (list, dict, set, etc)
@@ -1736,12 +1751,21 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1736
1751
  if (
1737
1752
  isinstance(value, dict)
1738
1753
  and inspect.isclass(hinted_args)
1739
- and (
1740
- dataclasses.is_dataclass(hinted_args)
1741
- or issubclass(hinted_args, Base)
1742
- )
1754
+ and not types.is_generic_alias(hinted_args) # py3.9-py3.10
1743
1755
  ):
1744
- payload[arg] = hinted_args(**value)
1756
+ if issubclass(hinted_args, Model):
1757
+ # Remove non-fields from the payload
1758
+ payload[arg] = hinted_args(
1759
+ **{
1760
+ key: value
1761
+ for key, value in value.items()
1762
+ if key in hinted_args.__fields__
1763
+ }
1764
+ )
1765
+ elif dataclasses.is_dataclass(hinted_args) or issubclass(
1766
+ hinted_args, (Base, BaseModelV1, BaseModelV2)
1767
+ ):
1768
+ payload[arg] = hinted_args(**value)
1745
1769
  if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
1746
1770
  payload[arg] = set(value)
1747
1771
  if isinstance(value, list) and (
@@ -1884,7 +1908,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1884
1908
  )
1885
1909
 
1886
1910
  subdelta: Dict[str, Any] = {
1887
- prop: self.get_value(getattr(self, prop))
1911
+ prop: self.get_value(prop)
1888
1912
  for prop in delta_vars
1889
1913
  if not types.is_backend_base_variable(prop, type(self))
1890
1914
  }
@@ -1935,6 +1959,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1935
1959
  if var in self.base_vars or var in self._backend_vars:
1936
1960
  self._was_touched = True
1937
1961
  break
1962
+ if var == constants.ROUTER_DATA and self.parent_state is None:
1963
+ self._was_touched = True
1964
+ break
1938
1965
 
1939
1966
  def _get_was_touched(self) -> bool:
1940
1967
  """Check current dirty_vars and flag to determine if state instance was modified.
@@ -1976,9 +2003,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1976
2003
  Returns:
1977
2004
  The value of the field.
1978
2005
  """
1979
- if isinstance(key, MutableProxy):
1980
- return super().get_value(key.__wrapped__)
1981
- return super().get_value(key)
2006
+ value = super().get_value(key)
2007
+ if isinstance(value, MutableProxy):
2008
+ return value.__wrapped__
2009
+ return value
1982
2010
 
1983
2011
  def dict(
1984
2012
  self, include_computed: bool = True, initial: bool = False, **kwargs
@@ -2000,8 +2028,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2000
2028
  self._mark_dirty()
2001
2029
 
2002
2030
  base_vars = {
2003
- prop_name: self.get_value(getattr(self, prop_name))
2004
- for prop_name in self.base_vars
2031
+ prop_name: self.get_value(prop_name) for prop_name in self.base_vars
2005
2032
  }
2006
2033
  if initial and include_computed:
2007
2034
  computed_vars = {
@@ -2010,7 +2037,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2010
2037
  cv._initial_value
2011
2038
  if is_computed_var(cv)
2012
2039
  and not isinstance(cv._initial_value, types.Unset)
2013
- else self.get_value(getattr(self, prop_name))
2040
+ else self.get_value(prop_name)
2014
2041
  )
2015
2042
  for prop_name, cv in self.computed_vars.items()
2016
2043
  if not cv._backend
@@ -2018,7 +2045,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2018
2045
  elif include_computed:
2019
2046
  computed_vars = {
2020
2047
  # Include the computed vars.
2021
- prop_name: self.get_value(getattr(self, prop_name))
2048
+ prop_name: self.get_value(prop_name)
2022
2049
  for prop_name, cv in self.computed_vars.items()
2023
2050
  if not cv._backend
2024
2051
  }
@@ -2086,7 +2113,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2086
2113
  state["__dict__"].pop(inherited_var_name, None)
2087
2114
  return state
2088
2115
 
2089
- def _warn_if_too_large(
2116
+ def _check_state_size(
2090
2117
  self,
2091
2118
  pickle_state_size: int,
2092
2119
  ):
@@ -2094,6 +2121,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2094
2121
 
2095
2122
  Args:
2096
2123
  pickle_state_size: The size of the pickled state.
2124
+
2125
+ Raises:
2126
+ StateTooLargeError: If the state is too large.
2097
2127
  """
2098
2128
  state_full_name = self.get_full_name()
2099
2129
  if (
@@ -2101,10 +2131,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2101
2131
  and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
2102
2132
  and self.substates
2103
2133
  ):
2104
- console.warn(
2134
+ msg = (
2105
2135
  f"State {state_full_name} serializes to {pickle_state_size} bytes "
2106
- "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."
2107
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)
2108
2142
  _WARNED_ABOUT_STATE_SIZE.add(state_full_name)
2109
2143
 
2110
2144
  @classmethod
@@ -2146,7 +2180,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2146
2180
  """
2147
2181
  try:
2148
2182
  pickle_state = pickle.dumps((self._to_schema(), self))
2149
- 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))
2150
2185
  return pickle_state
2151
2186
  except HANDLED_PICKLE_ERRORS as og_pickle_error:
2152
2187
  error = (
@@ -2361,6 +2396,23 @@ class ComponentState(State, mixin=True):
2361
2396
  # The number of components created from this class.
2362
2397
  _per_component_state_instance_count: ClassVar[int] = 0
2363
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
+
2364
2416
  @classmethod
2365
2417
  def __init_subclass__(cls, mixin: bool = True, **kwargs):
2366
2418
  """Overwrite mixin default to True.
@@ -3520,7 +3572,16 @@ class MutableProxy(wrapt.ObjectProxy):
3520
3572
  pydantic.BaseModel.__dict__
3521
3573
  )
3522
3574
 
3523
- __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
+ )
3524
3585
 
3525
3586
  def __init__(self, wrapped: Any, state: BaseState, field_name: str):
3526
3587
  """Create a proxy for a mutable object that tracks changes.
@@ -3560,6 +3621,18 @@ class MutableProxy(wrapt.ObjectProxy):
3560
3621
  if wrapped is not None:
3561
3622
  return wrapped(*args, **(kwargs or {}))
3562
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
+
3563
3636
  def _wrap_recursive(self, value: Any) -> Any:
3564
3637
  """Wrap a value recursively if it is mutable.
3565
3638
 
@@ -3570,9 +3643,7 @@ class MutableProxy(wrapt.ObjectProxy):
3570
3643
  The wrapped value.
3571
3644
  """
3572
3645
  # Recursively wrap mutable types, but do not re-wrap MutableProxy instances.
3573
- if isinstance(value, self.__mutable_types__) and not isinstance(
3574
- value, MutableProxy
3575
- ):
3646
+ if self._is_mutable_type(value) and not isinstance(value, MutableProxy):
3576
3647
  return type(self)(
3577
3648
  wrapped=value,
3578
3649
  state=self._self_state,
@@ -3630,7 +3701,7 @@ class MutableProxy(wrapt.ObjectProxy):
3630
3701
  self._wrap_recursive_decorator,
3631
3702
  )
3632
3703
 
3633
- if isinstance(value, self.__mutable_types__) and __name not in (
3704
+ if self._is_mutable_type(value) and __name not in (
3634
3705
  "__wrapped__",
3635
3706
  "_self_state",
3636
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