Skip to content

Commit

Permalink
Support backend-only state vars (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lendemor authored Jan 30, 2023
1 parent 2b7e2ec commit 4971f6d
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 0 deletions.
15 changes: 15 additions & 0 deletions pynecone/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -117,6 +120,12 @@ def __init_subclass__(cls, **kwargs):
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",
Expand Down Expand Up @@ -409,6 +418,8 @@ def __getattribute__(self, name: str) -> Any:
# 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):
Expand All @@ -425,6 +436,10 @@ def __setattr__(self, name: str, value: Any):
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)

Expand Down
12 changes: 12 additions & 0 deletions pynecone/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
12 changes: 12 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 4971f6d

Please sign in to comment.