Skip to content

Commit

Permalink
X
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-to committed Dec 15, 2024
1 parent 04907c9 commit adb8dd3
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 4 deletions.
1 change: 1 addition & 0 deletions mesop/component_helpers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ py_test(
srcs = ["helper_test.py"],
deps = [
":component_helpers",
"//mesop/runtime",
"//mesop/server",
] + THIRD_PARTY_PY_PYTEST,
)
13 changes: 11 additions & 2 deletions mesop/component_helpers/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ def slot(name: str = ""):

@dataclass(kw_only=True)
class SlotMetadata:
"""Metadata for slot
"""Metadata for slot.
Attributes:
node_slot: Position of the slot
node_slot: Position of the slot.
node_tree_state: Detached node tree state for this slot.
"""

Expand Down Expand Up @@ -107,6 +107,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):


class DetachedNodeTreeStateContextFactory:
"""Creates `DetachedNodeTreeStateContext` instances with the given named slots."""

def __init__(self, named_slots: dict[str, SlotMetadata]):
self.named_slots_accessed = set()
self.named_slots = named_slots
Expand Down Expand Up @@ -157,6 +159,13 @@ def __init__(self, fn: Callable[[], T]):
"Must configure at least one child slot when defining a composite component."
)

if len(self.unnamed_slots) == 0 and len(self.named_slots) != len(
node_tree_state.node_slots()
):
raise MesopDeveloperException(
"Multiple slots of the same name encountered. The names must be unique within the composite component."
)

if (
len(self.named_slots) > 0
and len(self.named_slots) != len(node_tree_state.node_slots())
Expand Down
46 changes: 45 additions & 1 deletion mesop/component_helpers/helper_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
from unittest.mock import patch

import pytest
from flask import Flask

from mesop.component_helpers.helper import check_property_keys_is_safe
import mesop.protos.ui_pb2 as pb
from mesop.component_helpers.helper import (
DetachedNodeTreeStateContext,
check_property_keys_is_safe,
)
from mesop.exceptions import MesopDeveloperException
from mesop.runtime.context import NodeTreeState
from mesop.runtime.runtime import Runtime


def create_default_single_component():
return pb.Component(
key=pb.Key(key="key"),
style=pb.Style(color="red", columns="1"),
style_debug_json="debug json string",
type=pb.Type(
name=pb.ComponentName(core_module=True, fn_name="test"), value=b"value"
),
source_code_location=pb.SourceCodeLocation(module="x", line=1, col=2),
)


def test_check_property_keys_is_safe_raises_exception():
Expand Down Expand Up @@ -29,5 +50,28 @@ def test_check_property_keys_is_safe_passes():
check_property_keys_is_safe({"click-on": None}.keys())


@pytest.fixture
def app():
app = Flask(__name__)
return app


@patch("mesop.component_helpers.helper.runtime")
def test_detached_node_tree_state_context(mock_runtime, app):
with app.app_context():
runtime = Runtime()
mock_runtime.return_value = runtime
node_tree_state = NodeTreeState()
runtime.context().set_node_tree_state(node_tree_state)
detached_node_tree_state = NodeTreeState()
c1 = create_default_single_component()
detached_node_tree_state.set_current_node(c1)

with DetachedNodeTreeStateContext(detached_node_tree_state):
assert runtime.context().get_node_tree_state() == detached_node_tree_state

assert runtime.context().get_node_tree_state() == node_tree_state


if __name__ == "__main__":
raise SystemExit(pytest.main([__file__]))
8 changes: 7 additions & 1 deletion mesop/runtime/BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("//build_defs:defaults.bzl", "THIRD_PARTY_PY_FLASK", "py_library")
load("//build_defs:defaults.bzl", "THIRD_PARTY_PY_FLASK", "THIRD_PARTY_PY_PYTEST", "py_library", "py_test")

package(
default_visibility = ["//build_defs:mesop_internal"],
Expand All @@ -19,3 +19,9 @@ py_library(
"//mesop/warn",
] + THIRD_PARTY_PY_FLASK,
)

py_test(
name = "node_tree_state_test",
srcs = ["node_tree_state_test.py"],
deps = [":runtime"] + THIRD_PARTY_PY_PYTEST,
)
117 changes: 117 additions & 0 deletions mesop/runtime/node_tree_state_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import pytest

import mesop.protos.ui_pb2 as pb
from mesop.runtime.context import NodeTreeState


def create_default_single_component():
return pb.Component(
key=pb.Key(key="key"),
style=pb.Style(color="red", columns="1"),
style_debug_json="debug json string",
type=pb.Type(
name=pb.ComponentName(core_module=True, fn_name="test"), value=b"value"
),
source_code_location=pb.SourceCodeLocation(module="x", line=1, col=2),
)


def test_set_current_node():
node_tree_state = NodeTreeState()
current_component = create_default_single_component()
node_tree_state.set_current_node(current_component)

assert node_tree_state.current_node() == current_component


def test_set_previous_node_from_current_node():
node_tree_state = NodeTreeState()
current_component = create_default_single_component()
node_tree_state.set_current_node(current_component)
assert node_tree_state.previous_node() is None

node_tree_state.set_previous_node_from_current_node()

assert node_tree_state.previous_node() == current_component


def test_reset_current_node():
node_tree_state = NodeTreeState()
current_component = create_default_single_component()
node_tree_state.set_current_node(current_component)
assert node_tree_state.current_node() == current_component

node_tree_state.reset_current_node()

assert node_tree_state.current_node() == pb.Component()


def test_reset_previous_node():
node_tree_state = NodeTreeState()
node_tree_state.set_previous_node_from_current_node()
assert node_tree_state.previous_node() == node_tree_state.current_node()

node_tree_state.reset_previous_node()

assert node_tree_state.previous_node() is None


def test_save_current_node_as_slot():
node_tree_state = NodeTreeState()
c1 = create_default_single_component()
c1_c1 = create_default_single_component()
c1_c2 = create_default_single_component()
c1.children.append(c1_c1)
c1.children.append(c1_c2)
node_tree_state.set_current_node(c1)

node_tree_state.save_current_node_as_slot()

node_slots = node_tree_state.node_slots()
assert len(node_slots) == 1
assert node_slots[0].name == ""
assert node_slots[0].parent_node == c1
assert node_slots[0].insertion_index == 2


def test_save_current_node_as_slot_with_name():
node_tree_state = NodeTreeState()
current_node = create_default_single_component()
node_tree_state.set_current_node(current_node)

node_tree_state.save_current_node_as_slot("test")

node_slots = node_tree_state.node_slots()
assert len(node_slots) == 1
assert node_slots[0].name == "test"
assert node_slots[0].parent_node == current_node
assert node_slots[0].insertion_index == 0


def test_multiple_save_current_node_as_slot():
node_tree_state = NodeTreeState()
c1 = create_default_single_component()
c2 = create_default_single_component()
node_tree_state.set_current_node(c1)
node_tree_state.save_current_node_as_slot()
node_tree_state.set_current_node(c2)
node_tree_state.save_current_node_as_slot()

node_slots = node_tree_state.node_slots()
assert len(node_slots) == 2
assert node_slots[0].parent_node == c1
assert node_slots[1].parent_node == c2


def test_clear_node_slots():
node_tree_state = NodeTreeState()
current_node = create_default_single_component()
node_tree_state.set_current_node(current_node)

node_tree_state.save_current_node_as_slot()
node_tree_state.clear_node_slots()
assert len(node_tree_state.node_slots()) == 0


if __name__ == "__main__":
raise SystemExit(pytest.main([__file__]))

0 comments on commit adb8dd3

Please sign in to comment.