Skip to content

Commit e5f741c

Browse files
Mike ProsserMike Prosser
authored andcommitted
add notification boolean
1 parent f5af8a9 commit e5f741c

File tree

9 files changed

+66
-14
lines changed

9 files changed

+66
-14
lines changed

protos/ni/pythonpanel/v1/python_panel_service.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ message SetValueRequest {
108108

109109
// The value
110110
google.protobuf.Any value = 3;
111+
112+
// Notify other clients of this new value
113+
bool notify = 4;
111114
}
112115

113116
message SetValueResponse {

src/ni/pythonpanel/v1/python_panel_service_pb2.py

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ni/pythonpanel/v1/python_panel_service_pb2.pyi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,13 @@ class SetValueRequest(google.protobuf.message.Message):
179179
PANEL_ID_FIELD_NUMBER: builtins.int
180180
VALUE_ID_FIELD_NUMBER: builtins.int
181181
VALUE_FIELD_NUMBER: builtins.int
182+
NOTIFY_FIELD_NUMBER: builtins.int
182183
panel_id: builtins.str
183184
"""Unique ID of the panel"""
184185
value_id: builtins.str
185186
"""Unique ID of the value"""
187+
notify: builtins.bool
188+
"""Notify other clients of this new value"""
186189
@property
187190
def value(self) -> google.protobuf.any_pb2.Any:
188191
"""The value"""
@@ -193,9 +196,10 @@ class SetValueRequest(google.protobuf.message.Message):
193196
panel_id: builtins.str = ...,
194197
value_id: builtins.str = ...,
195198
value: google.protobuf.any_pb2.Any | None = ...,
199+
notify: builtins.bool = ...,
196200
) -> None: ...
197201
def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ...
198-
def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "value", b"value", "value_id", b"value_id"]) -> None: ...
202+
def ClearField(self, field_name: typing.Literal["notify", b"notify", "panel_id", b"panel_id", "value", b"value", "value_id", b"value_id"]) -> None: ...
199203

200204
global___SetValueRequest = SetValueRequest
201205

src/nipanel/_panel_client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,19 @@ def enumerate_panels(self) -> dict[str, tuple[str, list[str]]]:
100100
panel.panel_id: (panel.panel_url, list(panel.value_ids)) for panel in response.panels
101101
}
102102

103-
def set_value(self, panel_id: str, value_id: str, value: object) -> None:
103+
def set_value(self, panel_id: str, value_id: str, value: object, notify: bool) -> None:
104104
"""Set the value for the control with value_id.
105105
106106
Args:
107107
panel_id: The ID of the panel.
108108
value_id: The ID of the control.
109109
value: The value to set.
110+
notify: Whether to notify other clients of the new value.
110111
"""
111112
new_any = to_any(value)
112-
set_value_request = SetValueRequest(panel_id=panel_id, value_id=value_id, value=new_any)
113+
set_value_request = SetValueRequest(
114+
panel_id=panel_id, value_id=value_id, value=new_any, notify=notify
115+
)
113116
self._invoke_with_retry(self._get_stub().SetValue, set_value_request)
114117

115118
def get_value(self, panel_id: str, value_id: str) -> object:

src/nipanel/_panel_value_accessor.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
class PanelValueAccessor(ABC):
1313
"""This class allows you to access values for a panel's controls."""
1414

15-
__slots__ = ["_panel_client", "_panel_id", "__weakref__"]
15+
__slots__ = ["_panel_client", "_panel_id", "_notify_on_set_value", "__weakref__"]
1616

1717
def __init__(
1818
self,
1919
*,
2020
panel_id: str,
2121
provided_interface: str,
2222
service_class: str,
23+
notify_on_set_value: bool = True,
2324
discovery_client: DiscoveryClient | None = None,
2425
grpc_channel_pool: GrpcChannelPool | None = None,
2526
grpc_channel: grpc.Channel | None = None,
@@ -33,6 +34,7 @@ def __init__(
3334
grpc_channel=grpc_channel,
3435
)
3536
self._panel_id = panel_id
37+
self._notify_on_set_value = notify_on_set_value
3638

3739
@property
3840
def panel_id(self) -> str:
@@ -57,4 +59,6 @@ def set_value(self, value_id: str, value: object) -> None:
5759
value_id: The id of the value
5860
value: The value
5961
"""
60-
self._panel_client.set_value(self._panel_id, value_id, value)
62+
self._panel_client.set_value(
63+
self._panel_id, value_id, value, notify=self._notify_on_set_value
64+
)

src/nipanel/_streamlit_panel_value_accessor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212

1313
@final
1414
class StreamlitPanelValueAccessor(PanelValueAccessor):
15-
"""This class provides access to values for a Streamlit panel's controls."""
15+
"""This class provides access to values for a Streamlit panel's controls.
16+
17+
This class should only be used within a Streamlit script.
18+
"""
1619

1720
def __init__(
1821
self,
@@ -35,6 +38,7 @@ def __init__(
3538
panel_id=panel_id,
3639
provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE,
3740
service_class=STREAMLIT_PYTHON_PANEL_SERVICE,
41+
notify_on_set_value=False,
3842
discovery_client=discovery_client,
3943
grpc_channel_pool=grpc_channel_pool,
4044
grpc_channel=grpc_channel,

tests/unit/test_panel_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ def test___set_value___enumerate_panels_shows_value(
6363
) -> None:
6464
client = create_panel_client(fake_panel_channel)
6565

66-
client.set_value("panel1", "val1", "value1")
66+
client.set_value("panel1", "val1", "value1", notify=False)
6767

6868
assert client.enumerate_panels() == {"panel1": ("", ["val1"])}
6969

7070

7171
def test___set_value___gets_value(fake_panel_channel: grpc.Channel) -> None:
7272
client = create_panel_client(fake_panel_channel)
7373

74-
client.set_value("panel1", "val1", "value1")
74+
client.set_value("panel1", "val1", "value1", notify=False)
7575

7676
assert client.get_value("panel1", "val1") == "value1"
7777

tests/unit/test_streamlit_panel.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@ def test___panel___accessor_set_value___panel_gets_same_value(
6363
assert panel.get_value(value_id) == string_value
6464

6565

66+
def test___panel___set_value___notifies(
67+
fake_python_panel_service: FakePythonPanelService,
68+
fake_panel_channel: grpc.Channel,
69+
) -> None:
70+
service = fake_python_panel_service
71+
panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel)
72+
assert service.servicer.notification_count == 0
73+
74+
panel.set_value("value_id", "string_value")
75+
76+
assert service.servicer.notification_count == 1
77+
78+
79+
def test___accessor___set_value___does_not_notify(
80+
fake_python_panel_service: FakePythonPanelService,
81+
fake_panel_channel: grpc.Channel,
82+
) -> None:
83+
service = fake_python_panel_service
84+
accessor = StreamlitPanelValueAccessor("my_panel", grpc_channel=fake_panel_channel)
85+
assert service.servicer.notification_count == 0
86+
87+
accessor.set_value("value_id", "string_value")
88+
89+
assert service.servicer.notification_count == 0
90+
91+
6692
def test___first_start_will_fail___start_panel___panel_is_functional(
6793
fake_python_panel_service: FakePythonPanelService,
6894
fake_panel_channel: grpc.Channel,

tests/utils/_fake_python_panel_servicer.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(self) -> None:
2626
self._panel_is_running: dict[str, bool] = {}
2727
self._panel_value_ids: dict[str, dict[str, Any]] = {}
2828
self._fail_next_start_panel = False
29+
self._notification_count: int = 0
2930

3031
def StartPanel( # noqa: N802
3132
self, request: StartPanelRequest, context: Any
@@ -65,12 +66,19 @@ def SetValue(self, request: SetValueRequest, context: Any) -> SetValueResponse:
6566
"""Trivial implementation for testing."""
6667
self._init_panel(request.panel_id)
6768
self._panel_value_ids[request.panel_id][request.value_id] = request.value
69+
if request.notify:
70+
self._notification_count += 1
6871
return SetValueResponse()
6972

7073
def fail_next_start_panel(self) -> None:
7174
"""Set whether the StartPanel method should fail the next time it is called."""
7275
self._fail_next_start_panel = True
7376

77+
@property
78+
def notification_count(self) -> int:
79+
"""Get the number of notifications sent from SetValue."""
80+
return self._notification_count
81+
7482
def _init_panel(self, panel_id: str) -> None:
7583
if panel_id not in self._panel_ids:
7684
self._panel_ids.append(panel_id)

0 commit comments

Comments
 (0)