diff --git a/pynecone/state.py b/pynecone/state.py index f356f9723..2cc51782e 100644 --- a/pynecone/state.py +++ b/pynecone/state.py @@ -33,6 +33,9 @@ class State(Base, ABC): # Vars inherited by the parent state. inherited_vars: ClassVar[Dict[str, Var]] = {} + # Backend vars that are never sent to the client. + backend_vars: ClassVar[Dict[str, Any]] = {} + # The parent state. parent_state: Optional[State] = None @@ -117,6 +120,12 @@ class State(Base, ABC): if parent_state is not None: cls.inherited_vars = parent_state.vars + cls.backend_vars = { + name: value + for name, value in cls.__dict__.items() + if utils.is_backend_variable(name) + } + # Set the base and computed vars. skip_vars = set(cls.inherited_vars) | { "parent_state", @@ -409,6 +418,8 @@ class State(Base, ABC): # Get the var from the parent state. if name in super().__getattribute__("inherited_vars"): return getattr(super().__getattribute__("parent_state"), name) + elif name in super().__getattribute__("backend_vars"): + return super().__getattribute__("backend_vars").__getitem__(name) return super().__getattribute__(name) def __setattr__(self, name: str, value: Any): @@ -425,6 +436,10 @@ class State(Base, ABC): setattr(self.parent_state, name, value) return + if utils.is_backend_variable(name): + self.backend_vars.__setitem__(name, value) + return + # Set the attribute. super().__setattr__(name, value) diff --git a/pynecone/utils.py b/pynecone/utils.py index 54b40a97f..940a4a7fa 100644 --- a/pynecone/utils.py +++ b/pynecone/utils.py @@ -1251,5 +1251,17 @@ def get_redis() -> Optional[Redis]: return Redis(host=redis_url, port=int(redis_port), db=0) +def is_backend_variable(name: str) -> bool: + """Check if this variable name correspond to a backend variable. + + Args: + name (str): The name of the variable to check + + Returns: + bool: The result of the check + """ + return name.startswith("_") and not name.startswith("__") + + # Store this here for performance. StateBases = get_base_class(StateVar) diff --git a/tests/test_utils.py b/tests/test_utils.py index 538e31122..11cdfb998 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -245,3 +245,15 @@ def test_setup_frontend(tmp_path, mocker): assert web_folder.exists() assert web_public_folder.exists() assert (web_public_folder / "favicon.ico").exists() + + +@pytest.mark.parametrize( + "input, output", + [ + ("_hidden", True), + ("not_hidden", False), + ("__dundermethod__", False), + ], +) +def test_is_backend_variable(input, output): + assert utils.is_backend_variable(input) == output