From c541c348859563573c67be7e241d205553fc3068 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 16 Jul 2025 09:57:12 -0500 Subject: [PATCH 1/9] api cleanup --- examples/all_types/all_types.py | 2 +- examples/all_types/all_types_panel.py | 2 +- examples/hello/hello.py | 2 +- examples/hello/hello_panel.py | 2 +- .../nidaqmx_continuous_analog_input.py | 2 +- .../nidaqmx_continuous_analog_input_panel.py | 2 +- examples/niscope/niscope_ex_fetch_forever.py | 2 +- .../niscope/niscope_ex_fetch_forever_panel.py | 2 +- .../performance_checker.py | 2 +- .../performance_checker_panel.py | 2 +- examples/simple_graph/simple_graph.py | 2 +- examples/simple_graph/simple_graph_panel.py | 2 +- .../pythonpanel/v1/python_panel_service.proto | 12 +-- .../v1/python_panel_service_pb2.py | 58 ++++++------- .../v1/python_panel_service_pb2.pyi | 12 +-- .../v1/python_panel_service_pb2_grpc.py | 28 +++--- .../v1/python_panel_service_pb2_grpc.pyi | 24 +++--- src/nipanel/__init__.py | 17 ++-- src/nipanel/_panel.py | 86 ------------------- src/nipanel/_panel_client.py | 20 +++-- src/nipanel/_panel_value_accessor.py | 7 +- src/nipanel/_streamlit_panel.py | 65 ++++++++++++-- src/nipanel/_streamlit_panel_initializer.py | 22 +++-- .../_streamlit_panel_value_accessor.py | 45 ---------- src/nipanel/controls/_enum_selectbox.py | 4 +- src/nipanel/controls/_flag_checkboxes.py | 4 +- tests/unit/test_panel_client.py | 36 +++----- ...cessor.py => test_panel_value_accessor.py} | 18 ++-- tests/unit/test_python_panel_service_stub.py | 12 +-- tests/unit/test_streamlit_panel.py | 10 ++- tests/utils/_fake_python_panel_servicer.py | 12 +-- 31 files changed, 226 insertions(+), 290 deletions(-) delete mode 100644 src/nipanel/_panel.py delete mode 100644 src/nipanel/_streamlit_panel_value_accessor.py rename tests/unit/{test_streamlit_panel_value_accessor.py => test_panel_value_accessor.py} (87%) diff --git a/examples/all_types/all_types.py b/examples/all_types/all_types.py index 8162b6e6..3a73c743 100644 --- a/examples/all_types/all_types.py +++ b/examples/all_types/all_types.py @@ -7,7 +7,7 @@ import nipanel panel_script_path = Path(__file__).with_name("all_types_panel.py") -panel = nipanel.create_panel(panel_script_path) +panel = nipanel.create_streamlit_panel(panel_script_path) print("Setting values") for name, value in all_types_with_values.items(): diff --git a/examples/all_types/all_types_panel.py b/examples/all_types/all_types_panel.py index b2e046b3..529d39b2 100644 --- a/examples/all_types/all_types_panel.py +++ b/examples/all_types/all_types_panel.py @@ -12,7 +12,7 @@ st.set_page_config(page_title="All Types Example", page_icon="📊", layout="wide") st.title("All Types Example") -panel = nipanel.get_panel_accessor() +panel = nipanel.get_streamlit_panel_accessor() for name in all_types_with_values.keys(): st.markdown("---") diff --git a/examples/hello/hello.py b/examples/hello/hello.py index ad2d97a6..9c2bcea2 100644 --- a/examples/hello/hello.py +++ b/examples/hello/hello.py @@ -5,7 +5,7 @@ import nipanel panel_script_path = Path(__file__).with_name("hello_panel.py") -panel = nipanel.create_panel(panel_script_path) +panel = nipanel.create_streamlit_panel(panel_script_path) panel.set_value("hello_string", "Hello, World!") diff --git a/examples/hello/hello_panel.py b/examples/hello/hello_panel.py index 4242e2ad..9f96538a 100644 --- a/examples/hello/hello_panel.py +++ b/examples/hello/hello_panel.py @@ -8,5 +8,5 @@ st.set_page_config(page_title="Hello World Example", page_icon="📊", layout="wide") st.title("Hello World Example") -panel = nipanel.get_panel_accessor() +panel = nipanel.get_streamlit_panel_accessor() st.write(panel.get_value("hello_string", "")) diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input.py b/examples/nidaqmx/nidaqmx_continuous_analog_input.py index bed43eb7..f8210a8a 100644 --- a/examples/nidaqmx/nidaqmx_continuous_analog_input.py +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input.py @@ -17,7 +17,7 @@ import nipanel panel_script_path = Path(__file__).with_name("nidaqmx_continuous_analog_input_panel.py") -panel = nipanel.create_panel(panel_script_path) +panel = nipanel.create_streamlit_panel(panel_script_path) try: print(f"Panel URL: {panel.panel_url}") diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py b/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py index 59e108e3..89a22316 100644 --- a/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py @@ -34,7 +34,7 @@ unsafe_allow_html=True, ) -panel = nipanel.get_panel_accessor() +panel = nipanel.get_streamlit_panel_accessor() is_running = panel.get_value("is_running", False) if is_running: diff --git a/examples/niscope/niscope_ex_fetch_forever.py b/examples/niscope/niscope_ex_fetch_forever.py index 68d73161..78b43a45 100644 --- a/examples/niscope/niscope_ex_fetch_forever.py +++ b/examples/niscope/niscope_ex_fetch_forever.py @@ -11,7 +11,7 @@ import nipanel panel_script_path = Path(__file__).with_name("niscope_ex_fetch_forever_panel.py") -panel = nipanel.create_panel(panel_script_path) +panel = nipanel.create_streamlit_panel(panel_script_path) print(f"Panel URL: {panel.panel_url}") diff --git a/examples/niscope/niscope_ex_fetch_forever_panel.py b/examples/niscope/niscope_ex_fetch_forever_panel.py index 3c2d1b0e..a2355fed 100644 --- a/examples/niscope/niscope_ex_fetch_forever_panel.py +++ b/examples/niscope/niscope_ex_fetch_forever_panel.py @@ -8,7 +8,7 @@ st.set_page_config(page_title="NI-SCOPE Example", page_icon="📈", layout="wide") st.title("NIScope EX Fetch Forever") -panel = nipanel.get_panel_accessor() +panel = nipanel.get_streamlit_panel_accessor() waveform = panel.get_value("Waveform", [0]) diff --git a/examples/performance_checker/performance_checker.py b/examples/performance_checker/performance_checker.py index 3bbc8851..26f3067a 100644 --- a/examples/performance_checker/performance_checker.py +++ b/examples/performance_checker/performance_checker.py @@ -11,7 +11,7 @@ panel_script_path = Path(__file__).with_name("performance_checker_panel.py") -panel = nipanel.create_panel(panel_script_path) +panel = nipanel.create_streamlit_panel(panel_script_path) amplitude = 1.0 frequency = 1.0 diff --git a/examples/performance_checker/performance_checker_panel.py b/examples/performance_checker/performance_checker_panel.py index e29f58c4..56b6f826 100644 --- a/examples/performance_checker/performance_checker_panel.py +++ b/examples/performance_checker/performance_checker_panel.py @@ -69,7 +69,7 @@ def profile_get_value( max_refresh_time = max(refresh_history) if refresh_history else 0 avg_refresh_time = statistics.mean(refresh_history) if refresh_history else 0 -panel = nipanel.get_panel_accessor() +panel = nipanel.get_streamlit_panel_accessor() num_timing_runs = 5 time_points, time_points_ms = profile_get_value(panel, "time_points", [0.0], num_timing_runs) diff --git a/examples/simple_graph/simple_graph.py b/examples/simple_graph/simple_graph.py index 4b63530f..bebdf32e 100644 --- a/examples/simple_graph/simple_graph.py +++ b/examples/simple_graph/simple_graph.py @@ -10,7 +10,7 @@ panel_script_path = Path(__file__).with_name("simple_graph_panel.py") -panel = nipanel.create_panel(panel_script_path) +panel = nipanel.create_streamlit_panel(panel_script_path) amplitude = 1.0 frequency = 1.0 diff --git a/examples/simple_graph/simple_graph_panel.py b/examples/simple_graph/simple_graph_panel.py index 6550627a..f3200c90 100644 --- a/examples/simple_graph/simple_graph_panel.py +++ b/examples/simple_graph/simple_graph_panel.py @@ -9,7 +9,7 @@ st.set_page_config(page_title="Simple Graph Example", page_icon="📈", layout="wide") st.title("Simple Graph Example") -panel = nipanel.get_panel_accessor() +panel = nipanel.get_streamlit_panel_accessor() time_points = panel.get_value("time_points", [0.0]) sine_values = panel.get_value("sine_values", [0.0]) amplitude = panel.get_value("amplitude", 1.0) diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/pythonpanel/v1/python_panel_service.proto index ab833acc..6f9c56da 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/pythonpanel/v1/python_panel_service.proto @@ -16,7 +16,7 @@ option ruby_package = "NI::PythonPanel::V1"; // Service interface for interacting with python panels service PythonPanelService { - // Start a panel (or connect to if it has already been started) + // Start a panel using a streamlit script (or connect to if it has already been started) // Status Codes for errors: // - INVALID_ARGUMENT: // - The panel script filename doesn't end in .py. @@ -24,7 +24,7 @@ service PythonPanelService { // - NOT_FOUND: // - The panel script file was not found. // - The python executable file was not found. - rpc StartPanel(StartPanelRequest) returns (StartPanelResponse); + rpc StartStreamlitPanel(StartStreamlitPanelRequest) returns (StartStreamlitPanelResponse); // Stop a panel // Status Codes for errors: @@ -60,18 +60,18 @@ service PythonPanelService { rpc SetValue(SetValueRequest) returns (SetValueResponse); } -message StartPanelRequest { +message StartStreamlitPanelRequest { // Unique ID of the panel string panel_id = 1; - // Absolute path of the panel script's file on disk, or network path to the file + // Absolute path of the streamlit script file on disk, or network path to the file string panel_script_path = 2; - // Path to the python interpreter to use for the panel script. + // Path to the python interpreter to use for running the streamlit script. string python_path = 3; } -message StartPanelResponse { +message StartStreamlitPanelResponse { // Location of the panel's webpage string panel_url = 1; } diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.py b/src/ni/pythonpanel/v1/python_panel_service_pb2.py index b8b36dd3..6806b2bc 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.py @@ -14,7 +14,7 @@ from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"U\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x19\n\x11panel_script_path\x18\x02 \x01(\t\x12\x13\n\x0bpython_path\x18\x03 \x01(\t\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_url\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_url\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xb9\x04\n\x12PythonPanelService\x12Y\n\nStartPanel\x12$.ni.pythonpanel.v1.StartPanelRequest\x1a%.ni.pythonpanel.v1.StartPanelResponse\x12V\n\tStopPanel\x12#.ni.pythonpanel.v1.StopPanelRequest\x1a$.ni.pythonpanel.v1.StopPanelResponse\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12\\\n\x0bTryGetValue\x12%.ni.pythonpanel.v1.TryGetValueRequest\x1a&.ni.pythonpanel.v1.TryGetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponseB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"^\n\x1aStartStreamlitPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x19\n\x11panel_script_path\x18\x02 \x01(\t\x12\x13\n\x0bpython_path\x18\x03 \x01(\t\"0\n\x1bStartStreamlitPanelResponse\x12\x11\n\tpanel_url\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_url\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xd4\x04\n\x12PythonPanelService\x12t\n\x13StartStreamlitPanel\x12-.ni.pythonpanel.v1.StartStreamlitPanelRequest\x1a..ni.pythonpanel.v1.StartStreamlitPanelResponse\x12V\n\tStopPanel\x12#.ni.pythonpanel.v1.StopPanelRequest\x1a$.ni.pythonpanel.v1.StopPanelResponse\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12\\\n\x0bTryGetValue\x12%.ni.pythonpanel.v1.TryGetValueRequest\x1a&.ni.pythonpanel.v1.TryGetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponseB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.pythonpanel.v1.python_panel_service_pb2', globals()) @@ -22,32 +22,32 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\025com.ni.pythonpanel.v1B\027PythonPanelServiceProtoP\001Z\rpythonpanelv1\370\001\001\242\002\004NIPP\252\002\"NationalInstruments.PythonPanel.V1\312\002\021NI\\PythonPanel\\V1\352\002\023NI::PythonPanel::V1' - _STARTPANELREQUEST._serialized_start=94 - _STARTPANELREQUEST._serialized_end=179 - _STARTPANELRESPONSE._serialized_start=181 - _STARTPANELRESPONSE._serialized_end=220 - _STOPPANELREQUEST._serialized_start=222 - _STOPPANELREQUEST._serialized_end=273 - _STOPPANELRESPONSE._serialized_start=275 - _STOPPANELRESPONSE._serialized_end=294 - _ENUMERATEPANELSREQUEST._serialized_start=296 - _ENUMERATEPANELSREQUEST._serialized_end=320 - _PANELINFORMATION._serialized_start=322 - _PANELINFORMATION._serialized_end=396 - _ENUMERATEPANELSRESPONSE._serialized_start=398 - _ENUMERATEPANELSRESPONSE._serialized_end=476 - _GETVALUEREQUEST._serialized_start=478 - _GETVALUEREQUEST._serialized_end=531 - _GETVALUERESPONSE._serialized_start=533 - _GETVALUERESPONSE._serialized_end=588 - _TRYGETVALUEREQUEST._serialized_start=590 - _TRYGETVALUEREQUEST._serialized_end=646 - _TRYGETVALUERESPONSE._serialized_start=648 - _TRYGETVALUERESPONSE._serialized_end=721 - _SETVALUEREQUEST._serialized_start=723 - _SETVALUEREQUEST._serialized_end=829 - _SETVALUERESPONSE._serialized_start=831 - _SETVALUERESPONSE._serialized_end=849 - _PYTHONPANELSERVICE._serialized_start=852 - _PYTHONPANELSERVICE._serialized_end=1421 + _STARTSTREAMLITPANELREQUEST._serialized_start=94 + _STARTSTREAMLITPANELREQUEST._serialized_end=188 + _STARTSTREAMLITPANELRESPONSE._serialized_start=190 + _STARTSTREAMLITPANELRESPONSE._serialized_end=238 + _STOPPANELREQUEST._serialized_start=240 + _STOPPANELREQUEST._serialized_end=291 + _STOPPANELRESPONSE._serialized_start=293 + _STOPPANELRESPONSE._serialized_end=312 + _ENUMERATEPANELSREQUEST._serialized_start=314 + _ENUMERATEPANELSREQUEST._serialized_end=338 + _PANELINFORMATION._serialized_start=340 + _PANELINFORMATION._serialized_end=414 + _ENUMERATEPANELSRESPONSE._serialized_start=416 + _ENUMERATEPANELSRESPONSE._serialized_end=494 + _GETVALUEREQUEST._serialized_start=496 + _GETVALUEREQUEST._serialized_end=549 + _GETVALUERESPONSE._serialized_start=551 + _GETVALUERESPONSE._serialized_end=606 + _TRYGETVALUEREQUEST._serialized_start=608 + _TRYGETVALUEREQUEST._serialized_end=664 + _TRYGETVALUERESPONSE._serialized_start=666 + _TRYGETVALUERESPONSE._serialized_end=739 + _SETVALUEREQUEST._serialized_start=741 + _SETVALUEREQUEST._serialized_end=847 + _SETVALUERESPONSE._serialized_start=849 + _SETVALUERESPONSE._serialized_end=867 + _PYTHONPANELSERVICE._serialized_start=870 + _PYTHONPANELSERVICE._serialized_end=1466 # @@protoc_insertion_point(module_scope) diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi index f55eb02f..88a117c5 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi @@ -14,7 +14,7 @@ import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final -class StartPanelRequest(google.protobuf.message.Message): +class StartStreamlitPanelRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PANEL_ID_FIELD_NUMBER: builtins.int @@ -23,9 +23,9 @@ class StartPanelRequest(google.protobuf.message.Message): panel_id: builtins.str """Unique ID of the panel""" panel_script_path: builtins.str - """Absolute path of the panel script's file on disk, or network path to the file""" + """Absolute path of the streamlit script file on disk, or network path to the file""" python_path: builtins.str - """Path to the python interpreter to use for the panel script.""" + """Path to the python interpreter to use for running the streamlit script.""" def __init__( self, *, @@ -35,10 +35,10 @@ class StartPanelRequest(google.protobuf.message.Message): ) -> None: ... def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_script_path", b"panel_script_path", "python_path", b"python_path"]) -> None: ... -global___StartPanelRequest = StartPanelRequest +global___StartStreamlitPanelRequest = StartStreamlitPanelRequest @typing.final -class StartPanelResponse(google.protobuf.message.Message): +class StartStreamlitPanelResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PANEL_URL_FIELD_NUMBER: builtins.int @@ -51,7 +51,7 @@ class StartPanelResponse(google.protobuf.message.Message): ) -> None: ... def ClearField(self, field_name: typing.Literal["panel_url", b"panel_url"]) -> None: ... -global___StartPanelResponse = StartPanelResponse +global___StartStreamlitPanelResponse = StartStreamlitPanelResponse @typing.final class StopPanelRequest(google.protobuf.message.Message): diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py index f2a3f727..88301f37 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py @@ -15,10 +15,10 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.StartPanel = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/StartPanel', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.FromString, + self.StartStreamlitPanel = channel.unary_unary( + '/ni.pythonpanel.v1.PythonPanelService/StartStreamlitPanel', + request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelRequest.SerializeToString, + response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelResponse.FromString, ) self.StopPanel = channel.unary_unary( '/ni.pythonpanel.v1.PythonPanelService/StopPanel', @@ -51,8 +51,8 @@ class PythonPanelServiceServicer(object): """Service interface for interacting with python panels """ - def StartPanel(self, request, context): - """Start a panel (or connect to if it has already been started) + def StartStreamlitPanel(self, request, context): + """Start a panel using a streamlit script (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - The panel script filename doesn't end in .py. @@ -121,10 +121,10 @@ def SetValue(self, request, context): def add_PythonPanelServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'StartPanel': grpc.unary_unary_rpc_method_handler( - servicer.StartPanel, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.SerializeToString, + 'StartStreamlitPanel': grpc.unary_unary_rpc_method_handler( + servicer.StartStreamlitPanel, + request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelRequest.FromString, + response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelResponse.SerializeToString, ), 'StopPanel': grpc.unary_unary_rpc_method_handler( servicer.StopPanel, @@ -163,7 +163,7 @@ class PythonPanelService(object): """ @staticmethod - def StartPanel(request, + def StartStreamlitPanel(request, target, options=(), channel_credentials=None, @@ -173,9 +173,9 @@ def StartPanel(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/StartPanel', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/StartStreamlitPanel', + ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelRequest.SerializeToString, + ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi index a38cae8c..03a99abf 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi @@ -21,11 +21,11 @@ class PythonPanelServiceStub: """Service interface for interacting with python panels""" def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ... - StartPanel: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, + StartStreamlitPanel: grpc.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse, ] - """Start a panel (or connect to if it has already been started) + """Start a panel using a streamlit script (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - The panel script filename doesn't end in .py. @@ -91,11 +91,11 @@ class PythonPanelServiceStub: class PythonPanelServiceAsyncStub: """Service interface for interacting with python panels""" - StartPanel: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, + StartStreamlitPanel: grpc.aio.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse, ] - """Start a panel (or connect to if it has already been started) + """Start a panel using a streamlit script (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - The panel script filename doesn't end in .py. @@ -162,12 +162,12 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): """Service interface for interacting with python panels""" @abc.abstractmethod - def StartPanel( + def StartStreamlitPanel( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, + request: ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse]]: - """Start a panel (or connect to if it has already been started) + ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse]]: + """Start a panel using a streamlit script (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - The panel script filename doesn't end in .py. diff --git a/src/nipanel/__init__.py b/src/nipanel/__init__.py index 05d5a737..65262b5c 100644 --- a/src/nipanel/__init__.py +++ b/src/nipanel/__init__.py @@ -2,21 +2,22 @@ from importlib.metadata import version -from nipanel._panel import Panel +from nipanel._panel_value_accessor import PanelValueAccessor from nipanel._streamlit_panel import StreamlitPanel -from nipanel._streamlit_panel_initializer import create_panel, get_panel_accessor -from nipanel._streamlit_panel_value_accessor import StreamlitPanelValueAccessor +from nipanel._streamlit_panel_initializer import ( + create_streamlit_panel, + get_streamlit_panel_accessor, +) __all__ = [ - "create_panel", - "get_panel_accessor", - "Panel", + "create_streamlit_panel", + "get_streamlit_panel_accessor", + "PanelValueAccessor", "StreamlitPanel", - "StreamlitPanelValueAccessor", ] # Hide that it was defined in a helper file -Panel.__module__ = __name__ +PanelValueAccessor.__module__ = __name__ StreamlitPanel.__module__ = __name__ __version__ = version(__name__) diff --git a/src/nipanel/_panel.py b/src/nipanel/_panel.py deleted file mode 100644 index 25f5bed6..00000000 --- a/src/nipanel/_panel.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -import sys -from abc import ABC -from pathlib import Path - -import grpc -from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient -from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool - -from nipanel._panel_value_accessor import PanelValueAccessor - - -class Panel(PanelValueAccessor, ABC): - """This class allows you to open a panel and specify values for its controls.""" - - __slots__ = ["_panel_script_path", "_panel_url"] - - def __init__( - self, - *, - panel_id: str, - panel_script_path: str, - provided_interface: str, - service_class: str, - discovery_client: DiscoveryClient | None = None, - grpc_channel_pool: GrpcChannelPool | None = None, - grpc_channel: grpc.Channel | None = None, - ) -> None: - """Initialize the panel.""" - super().__init__( - panel_id=panel_id, - provided_interface=provided_interface, - service_class=service_class, - discovery_client=discovery_client, - grpc_channel_pool=grpc_channel_pool, - grpc_channel=grpc_channel, - ) - self._panel_script_path = panel_script_path - python_path = self._get_python_path() - self._panel_url = self._panel_client.start_panel(panel_id, panel_script_path, python_path) - - @property - def panel_script_path(self) -> str: - """Read-only accessor for the panel script path.""" - return self._panel_script_path - - @property - def panel_url(self) -> str: - """Read-only accessor for the panel URL.""" - return self._panel_url - - def _get_python_path(self) -> str: - """Get the Python interpreter path for the panel that ensures the same environment.""" - if sys.executable is None or sys.executable == "": - raise RuntimeError("Python environment not found") - if getattr(sys, "frozen", False): - raise RuntimeError("Panel cannot be used in a frozen application (e.g., PyInstaller).") - - if sys.prefix != sys.base_prefix: - # If we're in a virtual environment, build the path to the Python executable - # On Linux: .venv/bin/python, On Windows: .venv\Scripts\python.exe - if sys.platform.startswith("win"): - python_executable = "python.exe" - bin_dir = "Scripts" - else: - python_executable = "python" - bin_dir = "bin" - - # Construct path to the Python in the virtual environment based on sys.prefix - python_path = str(Path(sys.prefix) / bin_dir / python_executable) - - # Fall back to sys.executable if the constructed path doesn't exist - if not Path(python_path).exists(): - python_path = str(Path(sys.executable).resolve()) - else: - # If not in a .venv environment, use sys.executable - python_path = str(Path(sys.executable).resolve()) - - if sys.prefix not in python_path: - # Ensure the Python path is within the current environment - raise RuntimeError( - f"Python path '{python_path}' does not match the current environment prefix '{sys.prefix}'." - ) - - return python_path diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 9de42d3a..ed060926 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -6,7 +6,7 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - StartPanelRequest, + StartStreamlitPanelRequest, StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, @@ -33,8 +33,8 @@ class _PanelClient: def __init__( self, *, - provided_interface: str, - service_class: str, + provided_interface: str | None = None, + service_class: str | None = None, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, grpc_channel: grpc.Channel | None = None, @@ -47,11 +47,13 @@ def __init__( self._grpc_channel = grpc_channel self._stub: PythonPanelServiceStub | None = None - def start_panel(self, panel_id: str, panel_script_path: str, python_path: str) -> str: - start_panel_request = StartPanelRequest( + def start_streamlit_panel(self, panel_id: str, panel_script_path: str, python_path: str) -> str: + start_panel_request = StartStreamlitPanelRequest( panel_id=panel_id, panel_script_path=panel_script_path, python_path=python_path ) - response = self._invoke_with_retry(self._get_stub().StartPanel, start_panel_request) + response = self._invoke_with_retry( + self._get_stub().StartStreamlitPanel, start_panel_request + ) return response.panel_url def stop_panel(self, panel_id: str, reset: bool) -> None: @@ -91,7 +93,7 @@ def _get_stub(self) -> PythonPanelServiceStub: if self._stub is None: if self._grpc_channel is not None: self._stub = PythonPanelServiceStub(self._grpc_channel) - else: + elif self._provided_interface is not None and self._service_class is not None: with self._initialization_lock: if self._grpc_channel_pool is None: _logger.debug("Creating unshared GrpcChannelPool.") @@ -108,6 +110,10 @@ def _get_stub(self) -> PythonPanelServiceStub: ) channel = self._grpc_channel_pool.get_channel(service_location.insecure_address) self._stub = PythonPanelServiceStub(channel) + else: + raise TypeError( + "Either grpc_channel, or both provided_interface and service_class, must be provided." + ) return self._stub def _invoke_with_retry( diff --git a/src/nipanel/_panel_value_accessor.py b/src/nipanel/_panel_value_accessor.py index c123bd0f..bfce4009 100644 --- a/src/nipanel/_panel_value_accessor.py +++ b/src/nipanel/_panel_value_accessor.py @@ -29,8 +29,8 @@ def __init__( self, *, panel_id: str, - provided_interface: str, - service_class: str, + provided_interface: str | None = None, + service_class: str | None = None, notify_on_set_value: bool = True, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, @@ -70,6 +70,9 @@ def get_value(self, value_id: str, default_value: _T | None = None) -> _T | obje Returns: The value, or the default value if not set + + Raises: + KeyError: If the value is not set and no default value is provided """ value = self._panel_client.try_get_value(self._panel_id, value_id) if value is None: diff --git a/src/nipanel/_streamlit_panel.py b/src/nipanel/_streamlit_panel.py index 16607cb6..90042d6f 100644 --- a/src/nipanel/_streamlit_panel.py +++ b/src/nipanel/_streamlit_panel.py @@ -1,23 +1,27 @@ from __future__ import annotations +import sys +from pathlib import Path from typing import final import grpc from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool -from nipanel._panel import Panel +from nipanel._panel_value_accessor import PanelValueAccessor from nipanel._streamlit_constants import STREAMLIT_PYTHON_PANEL_SERVICE @final -class StreamlitPanel(Panel): +class StreamlitPanel(PanelValueAccessor): """This class allows you to open a Streamlit panel and specify values for its controls.""" + __slots__ = ["_panel_script_path", "_panel_url"] + def __init__( self, panel_id: str, - streamlit_script_path: str, + panel_script_path: str, *, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, @@ -27,7 +31,9 @@ def __init__( Args: panel_id: A unique identifier for the panel. - streamlit_script_path: The file path of the Streamlit script. + panel_script_path: The file path of the Streamlit script. + discovery_client: An optional DiscoveryClient for service discovery. + grpc_channel_pool: An optional GrpcChannelPool for managing gRPC channels. grpc_channel: An optional gRPC channel to use for communication with the panel service. Returns: @@ -35,10 +41,59 @@ def __init__( """ super().__init__( panel_id=panel_id, - panel_script_path=streamlit_script_path, provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE, service_class=STREAMLIT_PYTHON_PANEL_SERVICE, discovery_client=discovery_client, grpc_channel_pool=grpc_channel_pool, grpc_channel=grpc_channel, ) + self._panel_script_path = panel_script_path + python_path = self._get_python_path() + self._panel_url = self._panel_client.start_streamlit_panel( + panel_id, panel_script_path, python_path + ) + + @property + def panel_script_path(self) -> str: + """Read-only accessor for the panel file path.""" + return self._panel_script_path + + @property + def panel_url(self) -> str: + """Read-only accessor for the panel URL.""" + return self._panel_url + + def _get_python_path(self) -> str: + """Get the Python interpreter path for the panel that ensures the same environment.""" + if sys.executable is None or sys.executable == "": + raise RuntimeError("Python environment not found") + if getattr(sys, "frozen", False): + raise RuntimeError("Panel cannot be used in a frozen application (e.g., PyInstaller).") + + if sys.prefix != sys.base_prefix: + # If we're in a virtual environment, build the path to the Python executable + # On Linux: .venv/bin/python, On Windows: .venv\Scripts\python.exe + if sys.platform.startswith("win"): + python_executable = "python.exe" + bin_dir = "Scripts" + else: + python_executable = "python" + bin_dir = "bin" + + # Construct path to the Python in the virtual environment based on sys.prefix + python_path = str(Path(sys.prefix) / bin_dir / python_executable) + + # Fall back to sys.executable if the constructed path doesn't exist + if not Path(python_path).exists(): + python_path = str(Path(sys.executable).resolve()) + else: + # If not in a .venv environment, use sys.executable + python_path = str(Path(sys.executable).resolve()) + + if sys.prefix not in python_path: + # Ensure the Python path is within the current environment + raise RuntimeError( + f"Python path '{python_path}' does not match the current environment prefix '{sys.prefix}'." + ) + + return python_path diff --git a/src/nipanel/_streamlit_panel_initializer.py b/src/nipanel/_streamlit_panel_initializer.py index a05822d0..569676a4 100644 --- a/src/nipanel/_streamlit_panel_initializer.py +++ b/src/nipanel/_streamlit_panel_initializer.py @@ -4,14 +4,15 @@ import streamlit as st from nipanel._convert import is_supported_type +from nipanel._panel_value_accessor import PanelValueAccessor +from nipanel._streamlit_constants import STREAMLIT_PYTHON_PANEL_SERVICE from nipanel._streamlit_panel import StreamlitPanel -from nipanel._streamlit_panel_value_accessor import StreamlitPanelValueAccessor from nipanel.streamlit_refresh import initialize_refresh_component PANEL_ACCESSOR_KEY = "StreamlitPanelValueAccessor" -def create_panel(streamlit_script_path: Path) -> StreamlitPanel: +def create_streamlit_panel(streamlit_script_path: Path) -> StreamlitPanel: """Create a Streamlit panel with the specified script path. This function initializes a Streamlit panel using the provided script path. It derives the panel @@ -43,7 +44,7 @@ def create_panel(streamlit_script_path: Path) -> StreamlitPanel: return StreamlitPanel(panel_id, path_str) -def get_panel_accessor() -> StreamlitPanelValueAccessor: +def get_streamlit_panel_accessor() -> PanelValueAccessor: """Initialize and return the Streamlit panel value accessor. This function retrieves the Streamlit panel value accessor for the current Streamlit script. @@ -61,25 +62,30 @@ def get_panel_accessor() -> StreamlitPanelValueAccessor: if PANEL_ACCESSOR_KEY not in st.session_state: st.session_state[PANEL_ACCESSOR_KEY] = _initialize_panel_from_base_path() - panel = cast(StreamlitPanelValueAccessor, st.session_state[PANEL_ACCESSOR_KEY]) + panel = cast(PanelValueAccessor, st.session_state[PANEL_ACCESSOR_KEY]) _sync_session_state(panel) refresh_component = initialize_refresh_component(panel.panel_id) refresh_component() return panel -def _initialize_panel_from_base_path() -> StreamlitPanelValueAccessor: - """Validate and parse the Streamlit base URL path and return a StreamlitPanelValueAccessor.""" +def _initialize_panel_from_base_path() -> PanelValueAccessor: + """Validate and parse the Streamlit base URL path and return a PanelValueAccessor.""" base_url_path = st.get_option("server.baseUrlPath") if not base_url_path.startswith("/"): raise ValueError("Invalid or missing Streamlit server.baseUrlPath option.") panel_id = base_url_path.split("/")[-1] if not panel_id: raise ValueError(f"Panel ID is empty in baseUrlPath: '{base_url_path}'") - return StreamlitPanelValueAccessor(panel_id) + return PanelValueAccessor( + panel_id=panel_id, + notify_on_set_value=False, + provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE, + service_class=STREAMLIT_PYTHON_PANEL_SERVICE, + ) -def _sync_session_state(panel: StreamlitPanelValueAccessor) -> None: +def _sync_session_state(panel: PanelValueAccessor) -> None: """Automatically read keyed control values from the session state.""" for key in st.session_state.keys(): value = st.session_state[key] diff --git a/src/nipanel/_streamlit_panel_value_accessor.py b/src/nipanel/_streamlit_panel_value_accessor.py deleted file mode 100644 index dc9e1d29..00000000 --- a/src/nipanel/_streamlit_panel_value_accessor.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import final - -import grpc -from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient -from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool - -from nipanel._panel_value_accessor import PanelValueAccessor -from nipanel._streamlit_constants import STREAMLIT_PYTHON_PANEL_SERVICE - - -@final -class StreamlitPanelValueAccessor(PanelValueAccessor): - """This class provides access to values for a Streamlit panel's controls. - - This class should only be used within a Streamlit script. - """ - - def __init__( - self, - panel_id: str, - *, - discovery_client: DiscoveryClient | None = None, - grpc_channel_pool: GrpcChannelPool | None = None, - grpc_channel: grpc.Channel | None = None, - ) -> None: - """Create an accessor for a Streamlit panel. - - Args: - panel_id: A unique identifier for the panel. - grpc_channel: An optional gRPC channel to use for communication with the panel service. - - Returns: - A new StreamlitPanelAccessor instance. - """ - super().__init__( - panel_id=panel_id, - provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE, - service_class=STREAMLIT_PYTHON_PANEL_SERVICE, - notify_on_set_value=False, - discovery_client=discovery_client, - grpc_channel_pool=grpc_channel_pool, - grpc_channel=grpc_channel, - ) diff --git a/src/nipanel/controls/_enum_selectbox.py b/src/nipanel/controls/_enum_selectbox.py index a75277c3..6b90b363 100644 --- a/src/nipanel/controls/_enum_selectbox.py +++ b/src/nipanel/controls/_enum_selectbox.py @@ -5,13 +5,13 @@ import streamlit as st -from nipanel._streamlit_panel_value_accessor import StreamlitPanelValueAccessor +from nipanel._panel_value_accessor import PanelValueAccessor TEnumType = TypeVar("TEnumType", bound=Enum) def enum_selectbox( - panel: StreamlitPanelValueAccessor, + panel: PanelValueAccessor, label: str, value: TEnumType, key: str, diff --git a/src/nipanel/controls/_flag_checkboxes.py b/src/nipanel/controls/_flag_checkboxes.py index 7887a534..2d12cfa3 100644 --- a/src/nipanel/controls/_flag_checkboxes.py +++ b/src/nipanel/controls/_flag_checkboxes.py @@ -5,13 +5,13 @@ import streamlit as st -from nipanel._streamlit_panel_value_accessor import StreamlitPanelValueAccessor +from nipanel._panel_value_accessor import PanelValueAccessor TFlagType = TypeVar("TFlagType", bound=Flag) def flag_checkboxes( - panel: StreamlitPanelValueAccessor, + panel: PanelValueAccessor, label: str, value: TFlagType, key: str, diff --git a/tests/unit/test_panel_client.py b/tests/unit/test_panel_client.py index 6f422dac..be71f2ef 100644 --- a/tests/unit/test_panel_client.py +++ b/tests/unit/test_panel_client.py @@ -5,16 +5,16 @@ def test___enumerate_is_empty(fake_panel_channel: grpc.Channel) -> None: - client = create_panel_client(fake_panel_channel) + client = _PanelClient(grpc_channel=fake_panel_channel) assert client.enumerate_panels() == {} def test___start_panels___enumerate_has_panels(fake_panel_channel: grpc.Channel) -> None: - client = create_panel_client(fake_panel_channel) + client = _PanelClient(grpc_channel=fake_panel_channel) - client.start_panel("panel1", "uri1", "python.exe") - client.start_panel("panel2", "uri2", "python.exe") + client.start_streamlit_panel("panel1", "uri1", "python.exe") + client.start_streamlit_panel("panel2", "uri2", "python.exe") assert client.enumerate_panels() == { "panel1": ("http://localhost:50051/panel1", []), @@ -25,9 +25,9 @@ def test___start_panels___enumerate_has_panels(fake_panel_channel: grpc.Channel) def test___start_panels___stop_panel_1_with_reset___enumerate_has_panel_2( fake_panel_channel: grpc.Channel, ) -> None: - client = create_panel_client(fake_panel_channel) - client.start_panel("panel1", "uri1", "python.exe") - client.start_panel("panel2", "uri2", "python.exe") + client = _PanelClient(grpc_channel=fake_panel_channel) + client.start_streamlit_panel("panel1", "uri1", "python.exe") + client.start_streamlit_panel("panel2", "uri2", "python.exe") client.stop_panel("panel1", reset=True) @@ -39,9 +39,9 @@ def test___start_panels___stop_panel_1_with_reset___enumerate_has_panel_2( def test___start_panels___stop_panel_1_without_reset___enumerate_has_both_panels( fake_panel_channel: grpc.Channel, ) -> None: - client = create_panel_client(fake_panel_channel) - client.start_panel("panel1", "uri1", "python.exe") - client.start_panel("panel2", "uri2", "python.exe") + client = _PanelClient(grpc_channel=fake_panel_channel) + client.start_streamlit_panel("panel1", "uri1", "python.exe") + client.start_streamlit_panel("panel2", "uri2", "python.exe") client.stop_panel("panel1", reset=False) @@ -52,7 +52,7 @@ def test___start_panels___stop_panel_1_without_reset___enumerate_has_both_panels def test___get_unset_value___raises_exception(fake_panel_channel: grpc.Channel) -> None: - client = create_panel_client(fake_panel_channel) + client = _PanelClient(grpc_channel=fake_panel_channel) with pytest.raises(grpc.RpcError) as exc_info: client.get_value("panel1", "unset_id") @@ -61,7 +61,7 @@ def test___get_unset_value___raises_exception(fake_panel_channel: grpc.Channel) def test___try_get_unset_value___returns_none(fake_panel_channel: grpc.Channel) -> None: - client = create_panel_client(fake_panel_channel) + client = _PanelClient(grpc_channel=fake_panel_channel) response = client.try_get_value("panel1", "unset_id") @@ -71,7 +71,7 @@ def test___try_get_unset_value___returns_none(fake_panel_channel: grpc.Channel) def test___set_value___enumerate_panels_shows_value( fake_panel_channel: grpc.Channel, ) -> None: - client = create_panel_client(fake_panel_channel) + client = _PanelClient(grpc_channel=fake_panel_channel) client.set_value("panel1", "val1", "value1", notify=False) @@ -79,16 +79,8 @@ def test___set_value___enumerate_panels_shows_value( def test___set_value___gets_value(fake_panel_channel: grpc.Channel) -> None: - client = create_panel_client(fake_panel_channel) + client = _PanelClient(grpc_channel=fake_panel_channel) client.set_value("panel1", "val1", "value1", notify=False) assert client.try_get_value("panel1", "val1") == "value1" - - -def create_panel_client(fake_panel_channel: grpc.Channel) -> _PanelClient: - return _PanelClient( - provided_interface="iface", - service_class="svc", - grpc_channel=fake_panel_channel, - ) diff --git a/tests/unit/test_streamlit_panel_value_accessor.py b/tests/unit/test_panel_value_accessor.py similarity index 87% rename from tests/unit/test_streamlit_panel_value_accessor.py rename to tests/unit/test_panel_value_accessor.py index 4a845b6b..e75c13f3 100644 --- a/tests/unit/test_streamlit_panel_value_accessor.py +++ b/tests/unit/test_panel_value_accessor.py @@ -1,6 +1,6 @@ import grpc -from nipanel import StreamlitPanelValueAccessor +from nipanel import PanelValueAccessor from tests.types import MyIntEnum from tests.utils._fake_python_panel_service import FakePythonPanelService @@ -8,7 +8,7 @@ def test___no_previous_value___set_value_if_changed___sets_value( fake_panel_channel: grpc.Channel, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", "test_value") @@ -19,7 +19,7 @@ def test___set_value_if_changed___set_same_value___does_not_set_value_again( fake_panel_channel: grpc.Channel, fake_python_panel_service: FakePythonPanelService, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", "test_value") initial_set_count = fake_python_panel_service.servicer.set_count @@ -32,7 +32,7 @@ def test___set_value_if_changed___set_same_value___does_not_set_value_again( def test___set_value_if_changed___set_different_value___sets_new_value( fake_panel_channel: grpc.Channel, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", "test_value") accessor.set_value_if_changed("test_id", "new_value") @@ -43,7 +43,7 @@ def test___set_value_if_changed___set_different_value___sets_new_value( def test___set_value_if_changed___different_value_ids___tracks_separately( fake_panel_channel: grpc.Channel, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("id1", "value1") accessor.set_value_if_changed("id2", "value2") @@ -58,7 +58,7 @@ def test___set_value_if_changed_with_list_value___set_same_value___does_not_set_ fake_panel_channel: grpc.Channel, fake_python_panel_service: FakePythonPanelService, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", [1, 2, 3]) initial_set_count = fake_python_panel_service.servicer.set_count @@ -72,7 +72,7 @@ def test___set_value_if_changed_with_list_value___set_different_value___sets_new fake_panel_channel: grpc.Channel, fake_python_panel_service: FakePythonPanelService, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", [1, 2, 3]) initial_set_count = fake_python_panel_service.servicer.set_count @@ -86,7 +86,7 @@ def test___set_value_if_changed_with_enum_value___set_same_value___does_not_set_ fake_panel_channel: grpc.Channel, fake_python_panel_service: FakePythonPanelService, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", MyIntEnum.VALUE20) initial_set_count = fake_python_panel_service.servicer.set_count @@ -100,7 +100,7 @@ def test___set_value_if_changed_with_enum_value___set_different_value___sets_new fake_panel_channel: grpc.Channel, fake_python_panel_service: FakePythonPanelService, ) -> None: - accessor = StreamlitPanelValueAccessor("panel_id", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="panel_id", grpc_channel=fake_panel_channel) accessor.set_value_if_changed("test_id", MyIntEnum.VALUE20) initial_set_count = fake_python_panel_service.servicer.set_count diff --git a/tests/unit/test_python_panel_service_stub.py b/tests/unit/test_python_panel_service_stub.py index a26f671c..371e75e3 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -2,7 +2,7 @@ from google.protobuf.any_pb2 import Any from google.protobuf.wrappers_pb2 import StringValue from ni.pythonpanel.v1.python_panel_service_pb2 import ( - StartPanelRequest, + StartStreamlitPanelRequest, StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, @@ -13,8 +13,8 @@ def test___start_panel___gets_response(python_panel_service_stub: PythonPanelServiceStub) -> None: - request = StartPanelRequest(panel_id="test_panel", panel_script_path="path/to/panel") - response = python_panel_service_stub.StartPanel(request) + request = StartStreamlitPanelRequest(panel_id="test_panel", panel_script_path="path/to/panel") + response = python_panel_service_stub.StartStreamlitPanel(request) assert response.panel_url == "http://localhost:50051/test_panel" @@ -22,8 +22,10 @@ def test___start_panel___gets_response(python_panel_service_stub: PythonPanelSer def test___start_panel___stop_panel___gets_response( python_panel_service_stub: PythonPanelServiceStub, ) -> None: - open_request = StartPanelRequest(panel_id="test_panel", panel_script_path="path/to/panel") - python_panel_service_stub.StartPanel(open_request) + open_request = StartStreamlitPanelRequest( + panel_id="test_panel", panel_script_path="path/to/panel" + ) + python_panel_service_stub.StartStreamlitPanel(open_request) request = StopPanelRequest(panel_id="test_panel", reset=False) response = python_panel_service_stub.StopPanel(request) diff --git a/tests/unit/test_streamlit_panel.py b/tests/unit/test_streamlit_panel.py index 9b67bf6d..027a7c40 100644 --- a/tests/unit/test_streamlit_panel.py +++ b/tests/unit/test_streamlit_panel.py @@ -6,7 +6,7 @@ from typing_extensions import assert_type import tests.types as test_types -from nipanel import StreamlitPanel, StreamlitPanelValueAccessor +from nipanel import StreamlitPanel, PanelValueAccessor from tests.utils._fake_python_panel_service import FakePythonPanelService @@ -45,7 +45,7 @@ def test___panel___panel_set_value___accessor_gets_same_value( fake_panel_channel: grpc.Channel, ) -> None: panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - accessor = StreamlitPanelValueAccessor("my_panel", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="my_panel", grpc_channel=fake_panel_channel) value_id = "test_id" string_value = "test_value" @@ -58,7 +58,7 @@ def test___panel___accessor_set_value___panel_gets_same_value( fake_panel_channel: grpc.Channel, ) -> None: panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - accessor = StreamlitPanelValueAccessor("my_panel", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor(panel_id="my_panel", grpc_channel=fake_panel_channel) value_id = "test_id" string_value = "test_value" @@ -85,7 +85,9 @@ def test___accessor___set_value___does_not_notify( fake_panel_channel: grpc.Channel, ) -> None: service = fake_python_panel_service - accessor = StreamlitPanelValueAccessor("my_panel", grpc_channel=fake_panel_channel) + accessor = PanelValueAccessor( + panel_id="my_panel", grpc_channel=fake_panel_channel, notify_on_set_value=False + ) assert service.servicer.notification_count == 0 accessor.set_value("value_id", "string_value") diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index 77d5824c..2ff16c49 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -2,8 +2,8 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - StartPanelRequest, - StartPanelResponse, + StartStreamlitPanelRequest, + StartStreamlitPanelResponse, StopPanelRequest, StopPanelResponse, EnumeratePanelsRequest, @@ -32,16 +32,16 @@ def __init__(self) -> None: self._notification_count: int = 0 self._python_path: str = "" - def StartPanel( # noqa: N802 - self, request: StartPanelRequest, context: Any - ) -> StartPanelResponse: + def StartStreamlitPanel( # noqa: N802 + self, request: StartStreamlitPanelRequest, context: Any + ) -> StartStreamlitPanelResponse: """Trivial implementation for testing.""" self._python_path = request.python_path if self._fail_next_start_panel: self._fail_next_start_panel = False context.abort(grpc.StatusCode.UNAVAILABLE, "Simulated failure") self._start_panel(request.panel_id) - return StartPanelResponse(panel_url=self._get_panel_url(request.panel_id)) + return StartStreamlitPanelResponse(panel_url=self._get_panel_url(request.panel_id)) def StopPanel(self, request: StopPanelRequest, context: Any) -> StopPanelResponse: # noqa: N802 """Trivial implementation for testing.""" From 0cb6e3f00c0fe71fb5459ece270c142b5eea6446 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 16 Jul 2025 10:43:18 -0500 Subject: [PATCH 2/9] add panel_id input to create_streamlit_panel() --- .../performance_checker_panel.py | 2 +- src/nipanel/_streamlit_panel_initializer.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/performance_checker/performance_checker_panel.py b/examples/performance_checker/performance_checker_panel.py index 56b6f826..23e56ea3 100644 --- a/examples/performance_checker/performance_checker_panel.py +++ b/examples/performance_checker/performance_checker_panel.py @@ -13,7 +13,7 @@ def profile_get_value( - panel: "nipanel.StreamlitPanelValueAccessor", + panel: "nipanel.PanelValueAccessor", value_id: str, default_value: Any = None, num_runs: int = 5, diff --git a/src/nipanel/_streamlit_panel_initializer.py b/src/nipanel/_streamlit_panel_initializer.py index 569676a4..403251d4 100644 --- a/src/nipanel/_streamlit_panel_initializer.py +++ b/src/nipanel/_streamlit_panel_initializer.py @@ -12,19 +12,21 @@ PANEL_ACCESSOR_KEY = "StreamlitPanelValueAccessor" -def create_streamlit_panel(streamlit_script_path: Path) -> StreamlitPanel: +def create_streamlit_panel(streamlit_script_path: Path, panel_id: str = "") -> StreamlitPanel: """Create a Streamlit panel with the specified script path. - This function initializes a Streamlit panel using the provided script path. It derives the panel - ID from the script's path, which it expects to be a valid Streamlit script. For example, if the - value for streamlit_script_path is "c:/example/some_example.py", then the panel's ID becomes - "some_example". + This function initializes a Streamlit panel using the provided script path. By default, it + derives the panel ID from the script's path, which it expects to be a valid Streamlit script. + For example, if the value for streamlit_script_path is "c:/example/some_example.py", then the + panel's ID becomes "some_example". Alternatively, you can specify a custom panel_id. Use this function when you want to create a new panel instance to use in a Streamlit application. Do not call this function from within a Streamlit script. Args: streamlit_script_path: The file path of the Streamlit script to be used for the panel. + panel_id: Optional custom panel ID. If not provided, it will be derived from the script + path. Returns: A StreamlitPanel instance initialized with the given panel ID. @@ -39,7 +41,9 @@ def create_streamlit_panel(streamlit_script_path: Path) -> StreamlitPanel: "The provided script path must be a valid Streamlit script ending with '.py'." ) - panel_id = streamlit_script_path.stem + if panel_id == "": + panel_id = streamlit_script_path.stem + path_str = str(streamlit_script_path) return StreamlitPanel(panel_id, path_str) From 4834bfe49078ef260a8cace012dbee01bcc852a7 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 16 Jul 2025 13:39:01 -0500 Subject: [PATCH 3/9] StreamlitPanelConfiguration --- .../pythonpanel/v1/python_panel_service.proto | 39 +++++++----- .../v1/python_panel_service_pb2.py | 60 ++++++++++--------- .../v1/python_panel_service_pb2.pyi | 58 +++++++++++------- .../v1/python_panel_service_pb2_grpc.py | 33 +++++----- .../v1/python_panel_service_pb2_grpc.pyi | 39 ++++++------ src/nipanel/_panel_client.py | 16 ++--- src/nipanel/_streamlit_panel_initializer.py | 2 +- tests/unit/test_python_panel_service_stub.py | 21 ++++--- tests/utils/_fake_python_panel_servicer.py | 18 +++--- 9 files changed, 157 insertions(+), 129 deletions(-) diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/pythonpanel/v1/python_panel_service.proto index 6f9c56da..e21acce3 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/pythonpanel/v1/python_panel_service.proto @@ -16,15 +16,14 @@ option ruby_package = "NI::PythonPanel::V1"; // Service interface for interacting with python panels service PythonPanelService { - // Start a panel using a streamlit script (or connect to if it has already been started) + // Start a panel using the provided configuration (or connect to if it has already been started) // Status Codes for errors: // - INVALID_ARGUMENT: - // - The panel script filename doesn't end in .py. // - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - The panel configuration has an invalid argument. // - NOT_FOUND: - // - The panel script file was not found. - // - The python executable file was not found. - rpc StartStreamlitPanel(StartStreamlitPanelRequest) returns (StartStreamlitPanelResponse); + // - The panel configuration includes a file that was not found. + rpc StartPanel(StartPanelRequest) returns (StartPanelResponse); // Stop a panel // Status Codes for errors: @@ -60,20 +59,28 @@ service PythonPanelService { rpc SetValue(SetValueRequest) returns (SetValueResponse); } -message StartStreamlitPanelRequest { +message StreamlitPanelConfiguration +{ + // Absolute path of the streamlit script file on disk, or network path to the file. This must end in ".py". + string panel_script_path = 1; + + // Path to the python interpreter to use for running the streamlit script. + string python_path = 2; +} + +message StartPanelRequest { // Unique ID of the panel string panel_id = 1; - // Absolute path of the streamlit script file on disk, or network path to the file - string panel_script_path = 2; - - // Path to the python interpreter to use for running the streamlit script. - string python_path = 3; + // Configuration for the panel + oneof panel_configuration { + StreamlitPanelConfiguration streamlit_panel_configuration = 2; + } } -message StartStreamlitPanelResponse { - // Location of the panel's webpage - string panel_url = 1; +message StartPanelResponse { + // Location of the panel + string panel_uri = 1; } message StopPanelRequest { @@ -94,8 +101,8 @@ message PanelInformation { // Unique ID of the panel string panel_id = 1; - // Location of the panel's webpage - string panel_url = 2; + // Location of the panel + string panel_uri = 2; // IDs of all of the values associated with the panel repeated string value_ids = 3; diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.py b/src/ni/pythonpanel/v1/python_panel_service_pb2.py index 6806b2bc..c48a404e 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.py @@ -14,7 +14,7 @@ from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"^\n\x1aStartStreamlitPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x19\n\x11panel_script_path\x18\x02 \x01(\t\x12\x13\n\x0bpython_path\x18\x03 \x01(\t\"0\n\x1bStartStreamlitPanelResponse\x12\x11\n\tpanel_url\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_url\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xd4\x04\n\x12PythonPanelService\x12t\n\x13StartStreamlitPanel\x12-.ni.pythonpanel.v1.StartStreamlitPanelRequest\x1a..ni.pythonpanel.v1.StartStreamlitPanelResponse\x12V\n\tStopPanel\x12#.ni.pythonpanel.v1.StopPanelRequest\x1a$.ni.pythonpanel.v1.StopPanelResponse\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12\\\n\x0bTryGetValue\x12%.ni.pythonpanel.v1.TryGetValueRequest\x1a&.ni.pythonpanel.v1.TryGetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponseB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"M\n\x1bStreamlitPanelConfiguration\x12\x19\n\x11panel_script_path\x18\x01 \x01(\t\x12\x13\n\x0bpython_path\x18\x02 \x01(\t\"\x95\x01\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12W\n\x1dstreamlit_panel_configuration\x18\x02 \x01(\x0b\x32..ni.pythonpanel.v1.StreamlitPanelConfigurationH\x00\x42\x15\n\x13panel_configuration\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_uri\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_uri\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xb9\x04\n\x12PythonPanelService\x12Y\n\nStartPanel\x12$.ni.pythonpanel.v1.StartPanelRequest\x1a%.ni.pythonpanel.v1.StartPanelResponse\x12V\n\tStopPanel\x12#.ni.pythonpanel.v1.StopPanelRequest\x1a$.ni.pythonpanel.v1.StopPanelResponse\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12\\\n\x0bTryGetValue\x12%.ni.pythonpanel.v1.TryGetValueRequest\x1a&.ni.pythonpanel.v1.TryGetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponseB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.pythonpanel.v1.python_panel_service_pb2', globals()) @@ -22,32 +22,34 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\025com.ni.pythonpanel.v1B\027PythonPanelServiceProtoP\001Z\rpythonpanelv1\370\001\001\242\002\004NIPP\252\002\"NationalInstruments.PythonPanel.V1\312\002\021NI\\PythonPanel\\V1\352\002\023NI::PythonPanel::V1' - _STARTSTREAMLITPANELREQUEST._serialized_start=94 - _STARTSTREAMLITPANELREQUEST._serialized_end=188 - _STARTSTREAMLITPANELRESPONSE._serialized_start=190 - _STARTSTREAMLITPANELRESPONSE._serialized_end=238 - _STOPPANELREQUEST._serialized_start=240 - _STOPPANELREQUEST._serialized_end=291 - _STOPPANELRESPONSE._serialized_start=293 - _STOPPANELRESPONSE._serialized_end=312 - _ENUMERATEPANELSREQUEST._serialized_start=314 - _ENUMERATEPANELSREQUEST._serialized_end=338 - _PANELINFORMATION._serialized_start=340 - _PANELINFORMATION._serialized_end=414 - _ENUMERATEPANELSRESPONSE._serialized_start=416 - _ENUMERATEPANELSRESPONSE._serialized_end=494 - _GETVALUEREQUEST._serialized_start=496 - _GETVALUEREQUEST._serialized_end=549 - _GETVALUERESPONSE._serialized_start=551 - _GETVALUERESPONSE._serialized_end=606 - _TRYGETVALUEREQUEST._serialized_start=608 - _TRYGETVALUEREQUEST._serialized_end=664 - _TRYGETVALUERESPONSE._serialized_start=666 - _TRYGETVALUERESPONSE._serialized_end=739 - _SETVALUEREQUEST._serialized_start=741 - _SETVALUEREQUEST._serialized_end=847 - _SETVALUERESPONSE._serialized_start=849 - _SETVALUERESPONSE._serialized_end=867 - _PYTHONPANELSERVICE._serialized_start=870 - _PYTHONPANELSERVICE._serialized_end=1466 + _STREAMLITPANELCONFIGURATION._serialized_start=94 + _STREAMLITPANELCONFIGURATION._serialized_end=171 + _STARTPANELREQUEST._serialized_start=174 + _STARTPANELREQUEST._serialized_end=323 + _STARTPANELRESPONSE._serialized_start=325 + _STARTPANELRESPONSE._serialized_end=364 + _STOPPANELREQUEST._serialized_start=366 + _STOPPANELREQUEST._serialized_end=417 + _STOPPANELRESPONSE._serialized_start=419 + _STOPPANELRESPONSE._serialized_end=438 + _ENUMERATEPANELSREQUEST._serialized_start=440 + _ENUMERATEPANELSREQUEST._serialized_end=464 + _PANELINFORMATION._serialized_start=466 + _PANELINFORMATION._serialized_end=540 + _ENUMERATEPANELSRESPONSE._serialized_start=542 + _ENUMERATEPANELSRESPONSE._serialized_end=620 + _GETVALUEREQUEST._serialized_start=622 + _GETVALUEREQUEST._serialized_end=675 + _GETVALUERESPONSE._serialized_start=677 + _GETVALUERESPONSE._serialized_end=732 + _TRYGETVALUEREQUEST._serialized_start=734 + _TRYGETVALUEREQUEST._serialized_end=790 + _TRYGETVALUERESPONSE._serialized_start=792 + _TRYGETVALUERESPONSE._serialized_end=865 + _SETVALUEREQUEST._serialized_start=867 + _SETVALUEREQUEST._serialized_end=973 + _SETVALUERESPONSE._serialized_start=975 + _SETVALUERESPONSE._serialized_end=993 + _PYTHONPANELSERVICE._serialized_start=996 + _PYTHONPANELSERVICE._serialized_end=1565 # @@protoc_insertion_point(module_scope) diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi index 88a117c5..5ab8f9c9 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi @@ -14,44 +14,62 @@ import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final -class StartStreamlitPanelRequest(google.protobuf.message.Message): +class StreamlitPanelConfiguration(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PANEL_ID_FIELD_NUMBER: builtins.int PANEL_SCRIPT_PATH_FIELD_NUMBER: builtins.int PYTHON_PATH_FIELD_NUMBER: builtins.int - panel_id: builtins.str - """Unique ID of the panel""" panel_script_path: builtins.str - """Absolute path of the streamlit script file on disk, or network path to the file""" + """Absolute path of the streamlit script file on disk, or network path to the file. This must end in ".py".""" python_path: builtins.str """Path to the python interpreter to use for running the streamlit script.""" def __init__( self, *, - panel_id: builtins.str = ..., panel_script_path: builtins.str = ..., python_path: builtins.str = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_script_path", b"panel_script_path", "python_path", b"python_path"]) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_script_path", b"panel_script_path", "python_path", b"python_path"]) -> None: ... + +global___StreamlitPanelConfiguration = StreamlitPanelConfiguration + +@typing.final +class StartPanelRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PANEL_ID_FIELD_NUMBER: builtins.int + STREAMLIT_PANEL_CONFIGURATION_FIELD_NUMBER: builtins.int + panel_id: builtins.str + """Unique ID of the panel""" + @property + def streamlit_panel_configuration(self) -> global___StreamlitPanelConfiguration: ... + def __init__( + self, + *, + panel_id: builtins.str = ..., + streamlit_panel_configuration: global___StreamlitPanelConfiguration | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["panel_configuration", b"panel_configuration", "streamlit_panel_configuration", b"streamlit_panel_configuration"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["panel_configuration", b"panel_configuration", "panel_id", b"panel_id", "streamlit_panel_configuration", b"streamlit_panel_configuration"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["panel_configuration", b"panel_configuration"]) -> typing.Literal["streamlit_panel_configuration"] | None: ... -global___StartStreamlitPanelRequest = StartStreamlitPanelRequest +global___StartPanelRequest = StartPanelRequest @typing.final -class StartStreamlitPanelResponse(google.protobuf.message.Message): +class StartPanelResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PANEL_URL_FIELD_NUMBER: builtins.int - panel_url: builtins.str - """Location of the panel's webpage""" + PANEL_URI_FIELD_NUMBER: builtins.int + panel_uri: builtins.str + """Location of the panel""" def __init__( self, *, - panel_url: builtins.str = ..., + panel_uri: builtins.str = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["panel_url", b"panel_url"]) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_uri", b"panel_uri"]) -> None: ... -global___StartStreamlitPanelResponse = StartStreamlitPanelResponse +global___StartPanelResponse = StartPanelResponse @typing.final class StopPanelRequest(google.protobuf.message.Message): @@ -98,12 +116,12 @@ class PanelInformation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PANEL_ID_FIELD_NUMBER: builtins.int - PANEL_URL_FIELD_NUMBER: builtins.int + PANEL_URI_FIELD_NUMBER: builtins.int VALUE_IDS_FIELD_NUMBER: builtins.int panel_id: builtins.str """Unique ID of the panel""" - panel_url: builtins.str - """Location of the panel's webpage""" + panel_uri: builtins.str + """Location of the panel""" @property def value_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: """IDs of all of the values associated with the panel""" @@ -112,10 +130,10 @@ class PanelInformation(google.protobuf.message.Message): self, *, panel_id: builtins.str = ..., - panel_url: builtins.str = ..., + panel_uri: builtins.str = ..., value_ids: collections.abc.Iterable[builtins.str] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_url", b"panel_url", "value_ids", b"value_ids"]) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_uri", b"panel_uri", "value_ids", b"value_ids"]) -> None: ... global___PanelInformation = PanelInformation diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py index 88301f37..2f5f9d20 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py @@ -15,10 +15,10 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.StartStreamlitPanel = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/StartStreamlitPanel', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelResponse.FromString, + self.StartPanel = channel.unary_unary( + '/ni.pythonpanel.v1.PythonPanelService/StartPanel', + request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.SerializeToString, + response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.FromString, ) self.StopPanel = channel.unary_unary( '/ni.pythonpanel.v1.PythonPanelService/StopPanel', @@ -51,15 +51,14 @@ class PythonPanelServiceServicer(object): """Service interface for interacting with python panels """ - def StartStreamlitPanel(self, request, context): - """Start a panel using a streamlit script (or connect to if it has already been started) + def StartPanel(self, request, context): + """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - - The panel script filename doesn't end in .py. - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The panel configuration has an invalid argument. - NOT_FOUND: - - The panel script file was not found. - - The python executable file was not found. + - The panel configuration includes a file that was not found. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -121,10 +120,10 @@ def SetValue(self, request, context): def add_PythonPanelServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'StartStreamlitPanel': grpc.unary_unary_rpc_method_handler( - servicer.StartStreamlitPanel, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelResponse.SerializeToString, + 'StartPanel': grpc.unary_unary_rpc_method_handler( + servicer.StartPanel, + request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.FromString, + response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.SerializeToString, ), 'StopPanel': grpc.unary_unary_rpc_method_handler( servicer.StopPanel, @@ -163,7 +162,7 @@ class PythonPanelService(object): """ @staticmethod - def StartStreamlitPanel(request, + def StartPanel(request, target, options=(), channel_credentials=None, @@ -173,9 +172,9 @@ def StartStreamlitPanel(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/StartStreamlitPanel', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartStreamlitPanelResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/StartPanel', + ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.SerializeToString, + ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi index 03a99abf..698260fc 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi @@ -21,18 +21,17 @@ class PythonPanelServiceStub: """Service interface for interacting with python panels""" def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ... - StartStreamlitPanel: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse, + StartPanel: grpc.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, ] - """Start a panel using a streamlit script (or connect to if it has already been started) + """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - - The panel script filename doesn't end in .py. - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The panel configuration has an invalid argument. - NOT_FOUND: - - The panel script file was not found. - - The python executable file was not found. + - The panel configuration includes a file that was not found. """ StopPanel: grpc.UnaryUnaryMultiCallable[ @@ -91,18 +90,17 @@ class PythonPanelServiceStub: class PythonPanelServiceAsyncStub: """Service interface for interacting with python panels""" - StartStreamlitPanel: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse, + StartPanel: grpc.aio.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, ] - """Start a panel using a streamlit script (or connect to if it has already been started) + """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - - The panel script filename doesn't end in .py. - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The panel configuration has an invalid argument. - NOT_FOUND: - - The panel script file was not found. - - The python executable file was not found. + - The panel configuration includes a file that was not found. """ StopPanel: grpc.aio.UnaryUnaryMultiCallable[ @@ -162,19 +160,18 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): """Service interface for interacting with python panels""" @abc.abstractmethod - def StartStreamlitPanel( + def StartPanel( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelRequest, + request: ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StartStreamlitPanelResponse]]: - """Start a panel using a streamlit script (or connect to if it has already been started) + ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse]]: + """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: - - The panel script filename doesn't end in .py. - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The panel configuration has an invalid argument. - NOT_FOUND: - - The panel script file was not found. - - The python executable file was not found. + - The panel configuration includes a file that was not found. """ @abc.abstractmethod diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index ed060926..58a105d1 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -6,7 +6,8 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - StartStreamlitPanelRequest, + StreamlitPanelConfiguration, + StartPanelRequest, StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, @@ -48,13 +49,14 @@ def __init__( self._stub: PythonPanelServiceStub | None = None def start_streamlit_panel(self, panel_id: str, panel_script_path: str, python_path: str) -> str: - start_panel_request = StartStreamlitPanelRequest( - panel_id=panel_id, panel_script_path=panel_script_path, python_path=python_path + streamlit_panel_configuration = StreamlitPanelConfiguration( + panel_script_path=panel_script_path, python_path=python_path ) - response = self._invoke_with_retry( - self._get_stub().StartStreamlitPanel, start_panel_request + start_panel_request = StartPanelRequest( + panel_id=panel_id, streamlit_panel_configuration=streamlit_panel_configuration ) - return response.panel_url + response = self._invoke_with_retry(self._get_stub().StartPanel, start_panel_request) + return response.panel_uri def stop_panel(self, panel_id: str, reset: bool) -> None: stop_panel_request = StopPanelRequest(panel_id=panel_id, reset=reset) @@ -66,7 +68,7 @@ def enumerate_panels(self) -> dict[str, tuple[str, list[str]]]: self._get_stub().EnumeratePanels, enumerate_panels_request ) return { - panel.panel_id: (panel.panel_url, list(panel.value_ids)) for panel in response.panels + panel.panel_id: (panel.panel_uri, list(panel.value_ids)) for panel in response.panels } def set_value(self, panel_id: str, value_id: str, value: object, notify: bool) -> None: diff --git a/src/nipanel/_streamlit_panel_initializer.py b/src/nipanel/_streamlit_panel_initializer.py index 403251d4..ce8fcbed 100644 --- a/src/nipanel/_streamlit_panel_initializer.py +++ b/src/nipanel/_streamlit_panel_initializer.py @@ -56,7 +56,7 @@ def get_streamlit_panel_accessor() -> PanelValueAccessor: in the Streamlit session state to ensure that it is reused across reruns of the script. Returns: - A StreamlitPanelValueAccessor instance for the current panel. + A PanelValueAccessor instance for the current panel. """ if st.get_option("server.baseUrlPath") == "": raise RuntimeError( diff --git a/tests/unit/test_python_panel_service_stub.py b/tests/unit/test_python_panel_service_stub.py index 371e75e3..455815af 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -2,7 +2,8 @@ from google.protobuf.any_pb2 import Any from google.protobuf.wrappers_pb2 import StringValue from ni.pythonpanel.v1.python_panel_service_pb2 import ( - StartStreamlitPanelRequest, + StreamlitPanelConfiguration, + StartPanelRequest, StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, @@ -13,22 +14,24 @@ def test___start_panel___gets_response(python_panel_service_stub: PythonPanelServiceStub) -> None: - request = StartStreamlitPanelRequest(panel_id="test_panel", panel_script_path="path/to/panel") - response = python_panel_service_stub.StartStreamlitPanel(request) + configuration = StreamlitPanelConfiguration(panel_script_path="path/to/panel.py") + request = StartPanelRequest(panel_id="test_panel", streamlit_panel_configuration=configuration) + response = python_panel_service_stub.StartPanel(request) - assert response.panel_url == "http://localhost:50051/test_panel" + assert response.panel_uri == "http://localhost:50051/test_panel" def test___start_panel___stop_panel___gets_response( python_panel_service_stub: PythonPanelServiceStub, ) -> None: - open_request = StartStreamlitPanelRequest( - panel_id="test_panel", panel_script_path="path/to/panel" + configuration = StreamlitPanelConfiguration(panel_script_path="path/to/panel.py") + start_request = StartPanelRequest( + panel_id="test_panel", streamlit_panel_configuration=configuration ) - python_panel_service_stub.StartStreamlitPanel(open_request) + python_panel_service_stub.StartPanel(start_request) - request = StopPanelRequest(panel_id="test_panel", reset=False) - response = python_panel_service_stub.StopPanel(request) + stop_request = StopPanelRequest(panel_id="test_panel", reset=False) + response = python_panel_service_stub.StopPanel(stop_request) assert response is not None # Ensure response is returned diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index 2ff16c49..346c05db 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -2,8 +2,8 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - StartStreamlitPanelRequest, - StartStreamlitPanelResponse, + StartPanelRequest, + StartPanelResponse, StopPanelRequest, StopPanelResponse, EnumeratePanelsRequest, @@ -32,16 +32,16 @@ def __init__(self) -> None: self._notification_count: int = 0 self._python_path: str = "" - def StartStreamlitPanel( # noqa: N802 - self, request: StartStreamlitPanelRequest, context: Any - ) -> StartStreamlitPanelResponse: + def StartPanel( # noqa: N802 + self, request: StartPanelRequest, context: Any + ) -> StartPanelResponse: """Trivial implementation for testing.""" - self._python_path = request.python_path + self._python_path = request.streamlit_panel_configuration.python_path if self._fail_next_start_panel: self._fail_next_start_panel = False context.abort(grpc.StatusCode.UNAVAILABLE, "Simulated failure") self._start_panel(request.panel_id) - return StartStreamlitPanelResponse(panel_url=self._get_panel_url(request.panel_id)) + return StartPanelResponse(panel_uri=self._get_panel_uri(request.panel_id)) def StopPanel(self, request: StopPanelRequest, context: Any) -> StopPanelResponse: # noqa: N802 """Trivial implementation for testing.""" @@ -56,7 +56,7 @@ def EnumeratePanels( # noqa: N802 for panel_id in self._panel_ids: panel = PanelInformation( panel_id=panel_id, - panel_url=self._get_panel_url(panel_id), + panel_uri=self._get_panel_uri(panel_id), value_ids=self._panel_value_ids[panel_id], ) response.panels.append(panel) @@ -128,7 +128,7 @@ def _stop_panel(self, reset: bool, panel_id: str) -> None: else: self._panel_is_running[panel_id] = False - def _get_panel_url(self, panel_id: str) -> str: + def _get_panel_uri(self, panel_id: str) -> str: if not self._panel_is_running.get(panel_id, False): return "" return f"http://localhost:50051/{panel_id}" From c743f5ac2b5a5987578955d6eee25e2859041b6b Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 16 Jul 2025 14:04:53 -0500 Subject: [PATCH 4/9] cleanup --- protos/ni/pythonpanel/v1/python_panel_service.proto | 2 +- src/ni/pythonpanel/v1/python_panel_service_pb2.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/pythonpanel/v1/python_panel_service.proto index e21acce3..e52c35a6 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/pythonpanel/v1/python_panel_service.proto @@ -135,7 +135,7 @@ message TryGetValueRequest { } message TryGetValueResponse { - // The value, if it was found + // The value, if it was found optional google.protobuf.Any value = 1; } diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi index 5ab8f9c9..8710881d 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi @@ -221,7 +221,7 @@ class TryGetValueResponse(google.protobuf.message.Message): VALUE_FIELD_NUMBER: builtins.int @property def value(self) -> google.protobuf.any_pb2.Any: - """The value, if it was found""" + """The value, if it was found""" def __init__( self, From 6f8f665bb16d280b773156198560ce14f42ce4e1 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Thu, 17 Jul 2025 13:15:01 -0500 Subject: [PATCH 5/9] rename the proto files --- CONTRIBUTING.md | 4 +- .../v1/panel_service.proto} | 22 ++-- .../v1/panel_types.proto} | 16 +-- pyproject.toml | 6 +- src/ni/panels/__init__.py | 1 + src/ni/panels/v1/__init__.py | 1 + src/ni/panels/v1/panel_service_pb2.py | 55 +++++++++ .../v1/panel_service_pb2.pyi} | 0 .../v1/panel_service_pb2_grpc.py} | 114 +++++++++--------- .../v1/panel_service_pb2_grpc.pyi} | 88 +++++++------- src/ni/panels/v1/panel_types_pb2.py | 34 ++++++ .../v1/panel_types_pb2.pyi} | 0 .../v1/panel_types_pb2_grpc.py} | 0 .../v1/panel_types_pb2_grpc.pyi} | 0 src/ni/{pythonpanel => panels}/v1/py.typed | 0 src/ni/pythonpanel/__init__.py | 1 - src/ni/pythonpanel/v1/__init__.py | 1 - .../v1/python_panel_service_pb2.py | 55 --------- .../pythonpanel/v1/python_panel_types_pb2.py | 34 ------ src/nipanel/_panel_client.py | 24 ++-- src/nipanel/_panel_value_accessor.py | 2 - src/nipanel/_streamlit_panel.py | 1 - src/nipanel/_streamlit_panel_initializer.py | 1 - src/nipanel/converters/builtin.py | 74 +++++------- tests/conftest.py | 8 +- tests/unit/test_convert.py | 32 ++--- tests/unit/test_python_panel_service_stub.py | 20 +-- tests/utils/_fake_python_panel_service.py | 6 +- tests/utils/_fake_python_panel_servicer.py | 8 +- 29 files changed, 295 insertions(+), 313 deletions(-) rename protos/ni/{pythonpanel/v1/python_panel_service.proto => panels/v1/panel_service.proto} (90%) rename protos/ni/{pythonpanel/v1/python_panel_types.proto => panels/v1/panel_types.proto} (51%) create mode 100644 src/ni/panels/__init__.py create mode 100644 src/ni/panels/v1/__init__.py create mode 100644 src/ni/panels/v1/panel_service_pb2.py rename src/ni/{pythonpanel/v1/python_panel_service_pb2.pyi => panels/v1/panel_service_pb2.pyi} (100%) rename src/ni/{pythonpanel/v1/python_panel_service_pb2_grpc.py => panels/v1/panel_service_pb2_grpc.py} (58%) rename src/ni/{pythonpanel/v1/python_panel_service_pb2_grpc.pyi => panels/v1/panel_service_pb2_grpc.pyi} (66%) create mode 100644 src/ni/panels/v1/panel_types_pb2.py rename src/ni/{pythonpanel/v1/python_panel_types_pb2.pyi => panels/v1/panel_types_pb2.pyi} (100%) rename src/ni/{pythonpanel/v1/python_panel_types_pb2_grpc.py => panels/v1/panel_types_pb2_grpc.py} (100%) rename src/ni/{pythonpanel/v1/python_panel_types_pb2_grpc.pyi => panels/v1/panel_types_pb2_grpc.pyi} (100%) rename src/ni/{pythonpanel => panels}/v1/py.typed (100%) delete mode 100644 src/ni/pythonpanel/__init__.py delete mode 100644 src/ni/pythonpanel/v1/__init__.py delete mode 100644 src/ni/pythonpanel/v1/python_panel_service_pb2.py delete mode 100644 src/ni/pythonpanel/v1/python_panel_types_pb2.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1e5e38e..ef4178a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,8 +21,8 @@ See [GitHub's official documentation](https://help.github.com/articles/using-pul # Getting Started -This is the command to generate the files in /src/ni/pythonpanel/v1/: -`poetry run python -m grpc_tools.protoc --proto_path=protos --python_out=src/ --grpc_python_out=src/ --mypy_out=src/ --mypy_grpc_out=src/ ni/pythonpanel/v1/python_panel_service.proto` +This is the command to generate the files in /src/ni/panels/v1/: +`poetry run python -m grpc_tools.protoc --proto_path=protos --python_out=src/ --grpc_python_out=src/ --mypy_out=src/ --mypy_grpc_out=src/ ni/panels/v1/panel_service.proto ni/panels/v1/panel_types.proto` # Testing diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/panels/v1/panel_service.proto similarity index 90% rename from protos/ni/pythonpanel/v1/python_panel_service.proto rename to protos/ni/panels/v1/panel_service.proto index e52c35a6..acc91fe0 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/panels/v1/panel_service.proto @@ -1,21 +1,21 @@ syntax = "proto3"; -package ni.pythonpanel.v1; +package ni.panels.v1; import "google/protobuf/any.proto"; option cc_enable_arenas = true; -option csharp_namespace = "NationalInstruments.PythonPanel.V1"; -option go_package = "pythonpanelv1"; +option csharp_namespace = "NationalInstruments.Panels.V1"; +option go_package = "panelsv1"; option java_multiple_files = true; -option java_outer_classname = "PythonPanelServiceProto"; -option java_package = "com.ni.pythonpanel.v1"; -option objc_class_prefix = "NIPP"; -option php_namespace = "NI\\PythonPanel\\V1"; -option ruby_package = "NI::PythonPanel::V1"; - -// Service interface for interacting with python panels -service PythonPanelService { +option java_outer_classname = "PanelsProto"; +option java_package = "com.ni.panels.v1"; +option objc_class_prefix = "NIPS"; +option php_namespace = "NI\\Panels\\V1"; +option ruby_package = "NI::Panels::V1"; + +// Service interface for interacting with NI panels +service PanelService { // Start a panel using the provided configuration (or connect to if it has already been started) // Status Codes for errors: // - INVALID_ARGUMENT: diff --git a/protos/ni/pythonpanel/v1/python_panel_types.proto b/protos/ni/panels/v1/panel_types.proto similarity index 51% rename from protos/ni/pythonpanel/v1/python_panel_types.proto rename to protos/ni/panels/v1/panel_types.proto index 8306bbfc..09995fe5 100644 --- a/protos/ni/pythonpanel/v1/python_panel_types.proto +++ b/protos/ni/panels/v1/panel_types.proto @@ -1,16 +1,16 @@ syntax = "proto3"; -package ni.pythonpanel.v1; +package ni.panels.v1; option cc_enable_arenas = true; -option csharp_namespace = "NationalInstruments.PythonPanel.V1"; -option go_package = "pythonpanelv1"; +option csharp_namespace = "NationalInstruments.Panels.V1"; +option go_package = "panelsv1"; option java_multiple_files = true; -option java_outer_classname = "PythonPanelServiceProto"; -option java_package = "com.ni.pythonpanel.v1"; -option objc_class_prefix = "NIPP"; -option php_namespace = "NI\\PythonPanel\\V1"; -option ruby_package = "NI::PythonPanel::V1"; +option java_outer_classname = "PanelsProto"; +option java_package = "com.ni.panels.v1"; +option objc_class_prefix = "NIPS"; +option php_namespace = "NI\\Panels\\V1"; +option ruby_package = "NI::Panels::V1"; message BoolCollection { repeated bool values = 1; diff --git a/pyproject.toml b/pyproject.toml index ba3985c9..1af789a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,10 +71,10 @@ requires = ["poetry-core>=1.8.0"] build-backend = "poetry.core.masonry.api" [tool.ni-python-styleguide] -extend_exclude = ".tox,docs,src/ni/pythonpanel/v1,src/ni/protobuf/types/" +extend_exclude = ".tox,docs,src/ni/panels/v1,src/ni/protobuf/types/" [tool.black] -extend-exclude = '\.tox/|docs/|src/ni/pythonpanel/v1/|src/ni/protobuf/types/' +extend-exclude = '\.tox/|docs/|src/ni/panels/v1/|src/ni/protobuf/types/' line-length = 100 [tool.mypy] @@ -104,4 +104,4 @@ testpaths = ["src/nipanel", "tests"] [tool.pyright] include = ["examples/", "src/", "tests/"] -exclude = ["src/ni/protobuf/types/", "src/ni/pythonpanel/v1/","examples/nidaqmx/nidaqmx_continuous_analog_input.py","examples/niscope/niscope_ex_fetch_forever.py"] \ No newline at end of file +exclude = ["src/ni/protobuf/types/", "src/ni/panels/v1/","examples/nidaqmx/nidaqmx_continuous_analog_input.py","examples/niscope/niscope_ex_fetch_forever.py"] \ No newline at end of file diff --git a/src/ni/panels/__init__.py b/src/ni/panels/__init__.py new file mode 100644 index 00000000..c744f82e --- /dev/null +++ b/src/ni/panels/__init__.py @@ -0,0 +1 @@ +"""gRPC stubs for NI Panels.""" diff --git a/src/ni/panels/v1/__init__.py b/src/ni/panels/v1/__init__.py new file mode 100644 index 00000000..6519cfc2 --- /dev/null +++ b/src/ni/panels/v1/__init__.py @@ -0,0 +1 @@ +"""gRPC client for NI Panels.""" diff --git a/src/ni/panels/v1/panel_service_pb2.py b/src/ni/panels/v1/panel_service_pb2.py new file mode 100644 index 00000000..56611f83 --- /dev/null +++ b/src/ni/panels/v1/panel_service_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ni/panels/v1/panel_service.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n ni/panels/v1/panel_service.proto\x12\x0cni.panels.v1\x1a\x19google/protobuf/any.proto\"M\n\x1bStreamlitPanelConfiguration\x12\x19\n\x11panel_script_path\x18\x01 \x01(\t\x12\x13\n\x0bpython_path\x18\x02 \x01(\t\"\x90\x01\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12R\n\x1dstreamlit_panel_configuration\x18\x02 \x01(\x0b\x32).ni.panels.v1.StreamlitPanelConfigurationH\x00\x42\x15\n\x13panel_configuration\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_uri\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_uri\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"I\n\x17\x45numeratePanelsResponse\x12.\n\x06panels\x18\x01 \x03(\x0b\x32\x1e.ni.panels.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xf7\x03\n\x0cPanelService\x12O\n\nStartPanel\x12\x1f.ni.panels.v1.StartPanelRequest\x1a .ni.panels.v1.StartPanelResponse\x12L\n\tStopPanel\x12\x1e.ni.panels.v1.StopPanelRequest\x1a\x1f.ni.panels.v1.StopPanelResponse\x12^\n\x0f\x45numeratePanels\x12$.ni.panels.v1.EnumeratePanelsRequest\x1a%.ni.panels.v1.EnumeratePanelsResponse\x12I\n\x08GetValue\x12\x1d.ni.panels.v1.GetValueRequest\x1a\x1e.ni.panels.v1.GetValueResponse\x12R\n\x0bTryGetValue\x12 .ni.panels.v1.TryGetValueRequest\x1a!.ni.panels.v1.TryGetValueResponse\x12I\n\x08SetValue\x12\x1d.ni.panels.v1.SetValueRequest\x1a\x1e.ni.panels.v1.SetValueResponseBu\n\x10\x63om.ni.panels.v1B\x0bPanelsProtoP\x01Z\x08panelsv1\xf8\x01\x01\xa2\x02\x04NIPS\xaa\x02\x1dNationalInstruments.Panels.V1\xca\x02\x0cNI\\Panels\\V1\xea\x02\x0eNI::Panels::V1b\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.panels.v1.panel_service_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\020com.ni.panels.v1B\013PanelsProtoP\001Z\010panelsv1\370\001\001\242\002\004NIPS\252\002\035NationalInstruments.Panels.V1\312\002\014NI\\Panels\\V1\352\002\016NI::Panels::V1' + _STREAMLITPANELCONFIGURATION._serialized_start=77 + _STREAMLITPANELCONFIGURATION._serialized_end=154 + _STARTPANELREQUEST._serialized_start=157 + _STARTPANELREQUEST._serialized_end=301 + _STARTPANELRESPONSE._serialized_start=303 + _STARTPANELRESPONSE._serialized_end=342 + _STOPPANELREQUEST._serialized_start=344 + _STOPPANELREQUEST._serialized_end=395 + _STOPPANELRESPONSE._serialized_start=397 + _STOPPANELRESPONSE._serialized_end=416 + _ENUMERATEPANELSREQUEST._serialized_start=418 + _ENUMERATEPANELSREQUEST._serialized_end=442 + _PANELINFORMATION._serialized_start=444 + _PANELINFORMATION._serialized_end=518 + _ENUMERATEPANELSRESPONSE._serialized_start=520 + _ENUMERATEPANELSRESPONSE._serialized_end=593 + _GETVALUEREQUEST._serialized_start=595 + _GETVALUEREQUEST._serialized_end=648 + _GETVALUERESPONSE._serialized_start=650 + _GETVALUERESPONSE._serialized_end=705 + _TRYGETVALUEREQUEST._serialized_start=707 + _TRYGETVALUEREQUEST._serialized_end=763 + _TRYGETVALUERESPONSE._serialized_start=765 + _TRYGETVALUERESPONSE._serialized_end=838 + _SETVALUEREQUEST._serialized_start=840 + _SETVALUEREQUEST._serialized_end=946 + _SETVALUERESPONSE._serialized_start=948 + _SETVALUERESPONSE._serialized_end=966 + _PANELSERVICE._serialized_start=969 + _PANELSERVICE._serialized_end=1472 +# @@protoc_insertion_point(module_scope) diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi b/src/ni/panels/v1/panel_service_pb2.pyi similarity index 100% rename from src/ni/pythonpanel/v1/python_panel_service_pb2.pyi rename to src/ni/panels/v1/panel_service_pb2.pyi diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py b/src/ni/panels/v1/panel_service_pb2_grpc.py similarity index 58% rename from src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py rename to src/ni/panels/v1/panel_service_pb2_grpc.py index 2f5f9d20..ef44db2a 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py +++ b/src/ni/panels/v1/panel_service_pb2_grpc.py @@ -2,11 +2,11 @@ """Client and server classes corresponding to protobuf-defined services.""" import grpc -from ni.pythonpanel.v1 import python_panel_service_pb2 as ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2 +from ni.panels.v1 import panel_service_pb2 as ni_dot_panels_dot_v1_dot_panel__service__pb2 -class PythonPanelServiceStub(object): - """Service interface for interacting with python panels +class PanelServiceStub(object): + """Service interface for interacting with NI panels """ def __init__(self, channel): @@ -16,39 +16,39 @@ def __init__(self, channel): channel: A grpc.Channel. """ self.StartPanel = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/StartPanel', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.FromString, + '/ni.panels.v1.PanelService/StartPanel', + request_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StartPanelRequest.SerializeToString, + response_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StartPanelResponse.FromString, ) self.StopPanel = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/StopPanel', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StopPanelRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StopPanelResponse.FromString, + '/ni.panels.v1.PanelService/StopPanel', + request_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StopPanelRequest.SerializeToString, + response_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StopPanelResponse.FromString, ) self.EnumeratePanels = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/EnumeratePanels', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.EnumeratePanelsRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.EnumeratePanelsResponse.FromString, + '/ni.panels.v1.PanelService/EnumeratePanels', + request_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.EnumeratePanelsRequest.SerializeToString, + response_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.EnumeratePanelsResponse.FromString, ) self.GetValue = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/GetValue', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueResponse.FromString, + '/ni.panels.v1.PanelService/GetValue', + request_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.GetValueRequest.SerializeToString, + response_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.GetValueResponse.FromString, ) self.TryGetValue = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/TryGetValue', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueResponse.FromString, + '/ni.panels.v1.PanelService/TryGetValue', + request_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.TryGetValueRequest.SerializeToString, + response_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.TryGetValueResponse.FromString, ) self.SetValue = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/SetValue', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueResponse.FromString, + '/ni.panels.v1.PanelService/SetValue', + request_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.SetValueRequest.SerializeToString, + response_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.SetValueResponse.FromString, ) -class PythonPanelServiceServicer(object): - """Service interface for interacting with python panels +class PanelServiceServicer(object): + """Service interface for interacting with NI panels """ def StartPanel(self, request, context): @@ -118,47 +118,47 @@ def SetValue(self, request, context): raise NotImplementedError('Method not implemented!') -def add_PythonPanelServiceServicer_to_server(servicer, server): +def add_PanelServiceServicer_to_server(servicer, server): rpc_method_handlers = { 'StartPanel': grpc.unary_unary_rpc_method_handler( servicer.StartPanel, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.SerializeToString, + request_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StartPanelRequest.FromString, + response_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StartPanelResponse.SerializeToString, ), 'StopPanel': grpc.unary_unary_rpc_method_handler( servicer.StopPanel, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StopPanelRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StopPanelResponse.SerializeToString, + request_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StopPanelRequest.FromString, + response_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.StopPanelResponse.SerializeToString, ), 'EnumeratePanels': grpc.unary_unary_rpc_method_handler( servicer.EnumeratePanels, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.EnumeratePanelsRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.EnumeratePanelsResponse.SerializeToString, + request_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.EnumeratePanelsRequest.FromString, + response_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.EnumeratePanelsResponse.SerializeToString, ), 'GetValue': grpc.unary_unary_rpc_method_handler( servicer.GetValue, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueResponse.SerializeToString, + request_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.GetValueRequest.FromString, + response_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.GetValueResponse.SerializeToString, ), 'TryGetValue': grpc.unary_unary_rpc_method_handler( servicer.TryGetValue, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueResponse.SerializeToString, + request_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.TryGetValueRequest.FromString, + response_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.TryGetValueResponse.SerializeToString, ), 'SetValue': grpc.unary_unary_rpc_method_handler( servicer.SetValue, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueResponse.SerializeToString, + request_deserializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.SetValueRequest.FromString, + response_serializer=ni_dot_panels_dot_v1_dot_panel__service__pb2.SetValueResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( - 'ni.pythonpanel.v1.PythonPanelService', rpc_method_handlers) + 'ni.panels.v1.PanelService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. -class PythonPanelService(object): - """Service interface for interacting with python panels +class PanelService(object): + """Service interface for interacting with NI panels """ @staticmethod @@ -172,9 +172,9 @@ def StartPanel(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/StartPanel', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StartPanelResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.panels.v1.PanelService/StartPanel', + ni_dot_panels_dot_v1_dot_panel__service__pb2.StartPanelRequest.SerializeToString, + ni_dot_panels_dot_v1_dot_panel__service__pb2.StartPanelResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -189,9 +189,9 @@ def StopPanel(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/StopPanel', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StopPanelRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.StopPanelResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.panels.v1.PanelService/StopPanel', + ni_dot_panels_dot_v1_dot_panel__service__pb2.StopPanelRequest.SerializeToString, + ni_dot_panels_dot_v1_dot_panel__service__pb2.StopPanelResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -206,9 +206,9 @@ def EnumeratePanels(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/EnumeratePanels', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.EnumeratePanelsRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.EnumeratePanelsResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.panels.v1.PanelService/EnumeratePanels', + ni_dot_panels_dot_v1_dot_panel__service__pb2.EnumeratePanelsRequest.SerializeToString, + ni_dot_panels_dot_v1_dot_panel__service__pb2.EnumeratePanelsResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -223,9 +223,9 @@ def GetValue(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/GetValue', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.panels.v1.PanelService/GetValue', + ni_dot_panels_dot_v1_dot_panel__service__pb2.GetValueRequest.SerializeToString, + ni_dot_panels_dot_v1_dot_panel__service__pb2.GetValueResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -240,9 +240,9 @@ def TryGetValue(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/TryGetValue', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.panels.v1.PanelService/TryGetValue', + ni_dot_panels_dot_v1_dot_panel__service__pb2.TryGetValueRequest.SerializeToString, + ni_dot_panels_dot_v1_dot_panel__service__pb2.TryGetValueResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -257,8 +257,8 @@ def SetValue(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/SetValue', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/ni.panels.v1.PanelService/SetValue', + ni_dot_panels_dot_v1_dot_panel__service__pb2.SetValueRequest.SerializeToString, + ni_dot_panels_dot_v1_dot_panel__service__pb2.SetValueResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi b/src/ni/panels/v1/panel_service_pb2_grpc.pyi similarity index 66% rename from src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi rename to src/ni/panels/v1/panel_service_pb2_grpc.pyi index 698260fc..030c5a37 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi +++ b/src/ni/panels/v1/panel_service_pb2_grpc.pyi @@ -7,7 +7,7 @@ import abc import collections.abc import grpc import grpc.aio -import ni.pythonpanel.v1.python_panel_service_pb2 +import ni.panels.v1.panel_service_pb2 import typing _T = typing.TypeVar("_T") @@ -17,13 +17,13 @@ class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Ite class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] ... -class PythonPanelServiceStub: - """Service interface for interacting with python panels""" +class PanelServiceStub: + """Service interface for interacting with NI panels""" def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ... StartPanel: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, + ni.panels.v1.panel_service_pb2.StartPanelRequest, + ni.panels.v1.panel_service_pb2.StartPanelResponse, ] """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: @@ -35,8 +35,8 @@ class PythonPanelServiceStub: """ StopPanel: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StopPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse, + ni.panels.v1.panel_service_pb2.StopPanelRequest, + ni.panels.v1.panel_service_pb2.StopPanelResponse, ] """Stop a panel Status Codes for errors: @@ -45,16 +45,16 @@ class PythonPanelServiceStub: """ EnumeratePanels: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, - ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse, + ni.panels.v1.panel_service_pb2.EnumeratePanelsRequest, + ni.panels.v1.panel_service_pb2.EnumeratePanelsResponse, ] """Enumerate the panels available in the system, including information about the state of the panels and what values they have. Status Codes for errors: """ GetValue: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest, - ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse, + ni.panels.v1.panel_service_pb2.GetValueRequest, + ni.panels.v1.panel_service_pb2.GetValueResponse, ] """Get a value for a control on the panel Status Codes for errors: @@ -66,8 +66,8 @@ class PythonPanelServiceStub: """ TryGetValue: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueRequest, - ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse, + ni.panels.v1.panel_service_pb2.TryGetValueRequest, + ni.panels.v1.panel_service_pb2.TryGetValueResponse, ] """Try to get a value for a control on the panel Status Codes for errors: @@ -77,8 +77,8 @@ class PythonPanelServiceStub: """ SetValue: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.SetValueRequest, - ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse, + ni.panels.v1.panel_service_pb2.SetValueRequest, + ni.panels.v1.panel_service_pb2.SetValueResponse, ] """Set a value for a control on the panel Status Codes for errors: @@ -87,12 +87,12 @@ class PythonPanelServiceStub: - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ -class PythonPanelServiceAsyncStub: - """Service interface for interacting with python panels""" +class PanelServiceAsyncStub: + """Service interface for interacting with NI panels""" StartPanel: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, + ni.panels.v1.panel_service_pb2.StartPanelRequest, + ni.panels.v1.panel_service_pb2.StartPanelResponse, ] """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: @@ -104,8 +104,8 @@ class PythonPanelServiceAsyncStub: """ StopPanel: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.StopPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse, + ni.panels.v1.panel_service_pb2.StopPanelRequest, + ni.panels.v1.panel_service_pb2.StopPanelResponse, ] """Stop a panel Status Codes for errors: @@ -114,16 +114,16 @@ class PythonPanelServiceAsyncStub: """ EnumeratePanels: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, - ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse, + ni.panels.v1.panel_service_pb2.EnumeratePanelsRequest, + ni.panels.v1.panel_service_pb2.EnumeratePanelsResponse, ] """Enumerate the panels available in the system, including information about the state of the panels and what values they have. Status Codes for errors: """ GetValue: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest, - ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse, + ni.panels.v1.panel_service_pb2.GetValueRequest, + ni.panels.v1.panel_service_pb2.GetValueResponse, ] """Get a value for a control on the panel Status Codes for errors: @@ -135,8 +135,8 @@ class PythonPanelServiceAsyncStub: """ TryGetValue: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueRequest, - ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse, + ni.panels.v1.panel_service_pb2.TryGetValueRequest, + ni.panels.v1.panel_service_pb2.TryGetValueResponse, ] """Try to get a value for a control on the panel Status Codes for errors: @@ -146,8 +146,8 @@ class PythonPanelServiceAsyncStub: """ SetValue: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.SetValueRequest, - ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse, + ni.panels.v1.panel_service_pb2.SetValueRequest, + ni.panels.v1.panel_service_pb2.SetValueResponse, ] """Set a value for a control on the panel Status Codes for errors: @@ -156,15 +156,15 @@ class PythonPanelServiceAsyncStub: - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ -class PythonPanelServiceServicer(metaclass=abc.ABCMeta): - """Service interface for interacting with python panels""" +class PanelServiceServicer(metaclass=abc.ABCMeta): + """Service interface for interacting with NI panels""" @abc.abstractmethod def StartPanel( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, + request: ni.panels.v1.panel_service_pb2.StartPanelRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse]]: + ) -> typing.Union[ni.panels.v1.panel_service_pb2.StartPanelResponse, collections.abc.Awaitable[ni.panels.v1.panel_service_pb2.StartPanelResponse]]: """Start a panel using the provided configuration (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: @@ -177,9 +177,9 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): @abc.abstractmethod def StopPanel( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.StopPanelRequest, + request: ni.panels.v1.panel_service_pb2.StopPanelRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse]]: + ) -> typing.Union[ni.panels.v1.panel_service_pb2.StopPanelResponse, collections.abc.Awaitable[ni.panels.v1.panel_service_pb2.StopPanelResponse]]: """Stop a panel Status Codes for errors: - INVALID_ARGUMENT: @@ -189,9 +189,9 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): @abc.abstractmethod def EnumeratePanels( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, + request: ni.panels.v1.panel_service_pb2.EnumeratePanelsRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse]]: + ) -> typing.Union[ni.panels.v1.panel_service_pb2.EnumeratePanelsResponse, collections.abc.Awaitable[ni.panels.v1.panel_service_pb2.EnumeratePanelsResponse]]: """Enumerate the panels available in the system, including information about the state of the panels and what values they have. Status Codes for errors: """ @@ -199,9 +199,9 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): @abc.abstractmethod def GetValue( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest, + request: ni.panels.v1.panel_service_pb2.GetValueRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse]]: + ) -> typing.Union[ni.panels.v1.panel_service_pb2.GetValueResponse, collections.abc.Awaitable[ni.panels.v1.panel_service_pb2.GetValueResponse]]: """Get a value for a control on the panel Status Codes for errors: - INVALID_ARGUMENT: @@ -214,9 +214,9 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): @abc.abstractmethod def TryGetValue( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueRequest, + request: ni.panels.v1.panel_service_pb2.TryGetValueRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse]]: + ) -> typing.Union[ni.panels.v1.panel_service_pb2.TryGetValueResponse, collections.abc.Awaitable[ni.panels.v1.panel_service_pb2.TryGetValueResponse]]: """Try to get a value for a control on the panel Status Codes for errors: - INVALID_ARGUMENT: @@ -227,9 +227,9 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): @abc.abstractmethod def SetValue( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.SetValueRequest, + request: ni.panels.v1.panel_service_pb2.SetValueRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse]]: + ) -> typing.Union[ni.panels.v1.panel_service_pb2.SetValueResponse, collections.abc.Awaitable[ni.panels.v1.panel_service_pb2.SetValueResponse]]: """Set a value for a control on the panel Status Codes for errors: - INVALID_ARGUMENT: @@ -237,4 +237,4 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ -def add_PythonPanelServiceServicer_to_server(servicer: PythonPanelServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ... +def add_PanelServiceServicer_to_server(servicer: PanelServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ... diff --git a/src/ni/panels/v1/panel_types_pb2.py b/src/ni/panels/v1/panel_types_pb2.py new file mode 100644 index 00000000..d637414f --- /dev/null +++ b/src/ni/panels/v1/panel_types_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ni/panels/v1/panel_types.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eni/panels/v1/panel_types.proto\x12\x0cni.panels.v1\" \n\x0e\x42oolCollection\x12\x0e\n\x06values\x18\x01 \x03(\x08\"&\n\x14\x42yteStringCollection\x12\x0e\n\x06values\x18\x01 \x03(\x0c\"!\n\x0f\x46loatCollection\x12\x0e\n\x06values\x18\x01 \x03(\x01\"\x1f\n\rIntCollection\x12\x0e\n\x06values\x18\x01 \x03(\x12\"\"\n\x10StringCollection\x12\x0e\n\x06values\x18\x01 \x03(\tBu\n\x10\x63om.ni.panels.v1B\x0bPanelsProtoP\x01Z\x08panelsv1\xf8\x01\x01\xa2\x02\x04NIPS\xaa\x02\x1dNationalInstruments.Panels.V1\xca\x02\x0cNI\\Panels\\V1\xea\x02\x0eNI::Panels::V1b\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.panels.v1.panel_types_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\020com.ni.panels.v1B\013PanelsProtoP\001Z\010panelsv1\370\001\001\242\002\004NIPS\252\002\035NationalInstruments.Panels.V1\312\002\014NI\\Panels\\V1\352\002\016NI::Panels::V1' + _BOOLCOLLECTION._serialized_start=48 + _BOOLCOLLECTION._serialized_end=80 + _BYTESTRINGCOLLECTION._serialized_start=82 + _BYTESTRINGCOLLECTION._serialized_end=120 + _FLOATCOLLECTION._serialized_start=122 + _FLOATCOLLECTION._serialized_end=155 + _INTCOLLECTION._serialized_start=157 + _INTCOLLECTION._serialized_end=188 + _STRINGCOLLECTION._serialized_start=190 + _STRINGCOLLECTION._serialized_end=224 +# @@protoc_insertion_point(module_scope) diff --git a/src/ni/pythonpanel/v1/python_panel_types_pb2.pyi b/src/ni/panels/v1/panel_types_pb2.pyi similarity index 100% rename from src/ni/pythonpanel/v1/python_panel_types_pb2.pyi rename to src/ni/panels/v1/panel_types_pb2.pyi diff --git a/src/ni/pythonpanel/v1/python_panel_types_pb2_grpc.py b/src/ni/panels/v1/panel_types_pb2_grpc.py similarity index 100% rename from src/ni/pythonpanel/v1/python_panel_types_pb2_grpc.py rename to src/ni/panels/v1/panel_types_pb2_grpc.py diff --git a/src/ni/pythonpanel/v1/python_panel_types_pb2_grpc.pyi b/src/ni/panels/v1/panel_types_pb2_grpc.pyi similarity index 100% rename from src/ni/pythonpanel/v1/python_panel_types_pb2_grpc.pyi rename to src/ni/panels/v1/panel_types_pb2_grpc.pyi diff --git a/src/ni/pythonpanel/v1/py.typed b/src/ni/panels/v1/py.typed similarity index 100% rename from src/ni/pythonpanel/v1/py.typed rename to src/ni/panels/v1/py.typed diff --git a/src/ni/pythonpanel/__init__.py b/src/ni/pythonpanel/__init__.py deleted file mode 100644 index 9dc74c4b..00000000 --- a/src/ni/pythonpanel/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""gRPC stubs for NI PythonPanel.""" diff --git a/src/ni/pythonpanel/v1/__init__.py b/src/ni/pythonpanel/v1/__init__.py deleted file mode 100644 index 7a90cada..00000000 --- a/src/ni/pythonpanel/v1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""gRPC client for PythonPanel.""" diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.py b/src/ni/pythonpanel/v1/python_panel_service_pb2.py deleted file mode 100644 index c48a404e..00000000 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: ni/pythonpanel/v1/python_panel_service.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"M\n\x1bStreamlitPanelConfiguration\x12\x19\n\x11panel_script_path\x18\x01 \x01(\t\x12\x13\n\x0bpython_path\x18\x02 \x01(\t\"\x95\x01\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12W\n\x1dstreamlit_panel_configuration\x18\x02 \x01(\x0b\x32..ni.pythonpanel.v1.StreamlitPanelConfigurationH\x00\x42\x15\n\x13panel_configuration\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_uri\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_uri\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xb9\x04\n\x12PythonPanelService\x12Y\n\nStartPanel\x12$.ni.pythonpanel.v1.StartPanelRequest\x1a%.ni.pythonpanel.v1.StartPanelResponse\x12V\n\tStopPanel\x12#.ni.pythonpanel.v1.StopPanelRequest\x1a$.ni.pythonpanel.v1.StopPanelResponse\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12\\\n\x0bTryGetValue\x12%.ni.pythonpanel.v1.TryGetValueRequest\x1a&.ni.pythonpanel.v1.TryGetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponseB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.pythonpanel.v1.python_panel_service_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\025com.ni.pythonpanel.v1B\027PythonPanelServiceProtoP\001Z\rpythonpanelv1\370\001\001\242\002\004NIPP\252\002\"NationalInstruments.PythonPanel.V1\312\002\021NI\\PythonPanel\\V1\352\002\023NI::PythonPanel::V1' - _STREAMLITPANELCONFIGURATION._serialized_start=94 - _STREAMLITPANELCONFIGURATION._serialized_end=171 - _STARTPANELREQUEST._serialized_start=174 - _STARTPANELREQUEST._serialized_end=323 - _STARTPANELRESPONSE._serialized_start=325 - _STARTPANELRESPONSE._serialized_end=364 - _STOPPANELREQUEST._serialized_start=366 - _STOPPANELREQUEST._serialized_end=417 - _STOPPANELRESPONSE._serialized_start=419 - _STOPPANELRESPONSE._serialized_end=438 - _ENUMERATEPANELSREQUEST._serialized_start=440 - _ENUMERATEPANELSREQUEST._serialized_end=464 - _PANELINFORMATION._serialized_start=466 - _PANELINFORMATION._serialized_end=540 - _ENUMERATEPANELSRESPONSE._serialized_start=542 - _ENUMERATEPANELSRESPONSE._serialized_end=620 - _GETVALUEREQUEST._serialized_start=622 - _GETVALUEREQUEST._serialized_end=675 - _GETVALUERESPONSE._serialized_start=677 - _GETVALUERESPONSE._serialized_end=732 - _TRYGETVALUEREQUEST._serialized_start=734 - _TRYGETVALUEREQUEST._serialized_end=790 - _TRYGETVALUERESPONSE._serialized_start=792 - _TRYGETVALUERESPONSE._serialized_end=865 - _SETVALUEREQUEST._serialized_start=867 - _SETVALUEREQUEST._serialized_end=973 - _SETVALUERESPONSE._serialized_start=975 - _SETVALUERESPONSE._serialized_end=993 - _PYTHONPANELSERVICE._serialized_start=996 - _PYTHONPANELSERVICE._serialized_end=1565 -# @@protoc_insertion_point(module_scope) diff --git a/src/ni/pythonpanel/v1/python_panel_types_pb2.py b/src/ni/pythonpanel/v1/python_panel_types_pb2.py deleted file mode 100644 index 684da292..00000000 --- a/src/ni/pythonpanel/v1/python_panel_types_pb2.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: ni/pythonpanel/v1/python_panel_types.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*ni/pythonpanel/v1/python_panel_types.proto\x12\x11ni.pythonpanel.v1\" \n\x0e\x42oolCollection\x12\x0e\n\x06values\x18\x01 \x03(\x08\"&\n\x14\x42yteStringCollection\x12\x0e\n\x06values\x18\x01 \x03(\x0c\"!\n\x0f\x46loatCollection\x12\x0e\n\x06values\x18\x01 \x03(\x01\"\x1f\n\rIntCollection\x12\x0e\n\x06values\x18\x01 \x03(\x12\"\"\n\x10StringCollection\x12\x0e\n\x06values\x18\x01 \x03(\tB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.pythonpanel.v1.python_panel_types_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\025com.ni.pythonpanel.v1B\027PythonPanelServiceProtoP\001Z\rpythonpanelv1\370\001\001\242\002\004NIPP\252\002\"NationalInstruments.PythonPanel.V1\312\002\021NI\\PythonPanel\\V1\352\002\023NI::PythonPanel::V1' - _BOOLCOLLECTION._serialized_start=65 - _BOOLCOLLECTION._serialized_end=97 - _BYTESTRINGCOLLECTION._serialized_start=99 - _BYTESTRINGCOLLECTION._serialized_end=137 - _FLOATCOLLECTION._serialized_start=139 - _FLOATCOLLECTION._serialized_end=172 - _INTCOLLECTION._serialized_start=174 - _INTCOLLECTION._serialized_end=205 - _STRINGCOLLECTION._serialized_start=207 - _STRINGCOLLECTION._serialized_end=241 -# @@protoc_insertion_point(module_scope) diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 58a105d1..1fc33bca 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -5,7 +5,7 @@ from typing import Callable, TypeVar import grpc -from ni.pythonpanel.v1.python_panel_service_pb2 import ( +from ni.panels.v1.panel_service_pb2 import ( StreamlitPanelConfiguration, StartPanelRequest, StopPanelRequest, @@ -14,7 +14,7 @@ TryGetValueRequest, SetValueRequest, ) -from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub +from ni.panels.v1.panel_service_pb2_grpc import PanelServiceStub from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool from typing_extensions import ParamSpec @@ -29,24 +29,24 @@ _logger = logging.getLogger(__name__) +PANEL_INTERFACE = "ni.panels.v1.PanelService" + class _PanelClient: def __init__( self, *, - provided_interface: str | None = None, service_class: str | None = None, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, grpc_channel: grpc.Channel | None = None, ) -> None: self._initialization_lock = threading.Lock() - self._provided_interface = provided_interface self._service_class = service_class self._discovery_client = discovery_client self._grpc_channel_pool = grpc_channel_pool self._grpc_channel = grpc_channel - self._stub: PythonPanelServiceStub | None = None + self._stub: PanelServiceStub | None = None def start_streamlit_panel(self, panel_id: str, panel_script_path: str, python_path: str) -> str: streamlit_panel_configuration = StreamlitPanelConfiguration( @@ -91,11 +91,11 @@ def try_get_value(self, panel_id: str, value_id: str) -> object | None: else: return None - def _get_stub(self) -> PythonPanelServiceStub: + def _get_stub(self) -> PanelServiceStub: if self._stub is None: if self._grpc_channel is not None: - self._stub = PythonPanelServiceStub(self._grpc_channel) - elif self._provided_interface is not None and self._service_class is not None: + self._stub = PanelServiceStub(self._grpc_channel) + elif self._service_class is not None: with self._initialization_lock: if self._grpc_channel_pool is None: _logger.debug("Creating unshared GrpcChannelPool.") @@ -107,15 +107,13 @@ def _get_stub(self) -> PythonPanelServiceStub: ) service_location = self._discovery_client.resolve_service( - provided_interface=self._provided_interface, + provided_interface=PANEL_INTERFACE, service_class=self._service_class, ) channel = self._grpc_channel_pool.get_channel(service_location.insecure_address) - self._stub = PythonPanelServiceStub(channel) + self._stub = PanelServiceStub(channel) else: - raise TypeError( - "Either grpc_channel, or both provided_interface and service_class, must be provided." - ) + raise TypeError("Either grpc_channel or service_class must be provided.") return self._stub def _invoke_with_retry( diff --git a/src/nipanel/_panel_value_accessor.py b/src/nipanel/_panel_value_accessor.py index bfce4009..42396cea 100644 --- a/src/nipanel/_panel_value_accessor.py +++ b/src/nipanel/_panel_value_accessor.py @@ -29,7 +29,6 @@ def __init__( self, *, panel_id: str, - provided_interface: str | None = None, service_class: str | None = None, notify_on_set_value: bool = True, discovery_client: DiscoveryClient | None = None, @@ -38,7 +37,6 @@ def __init__( ) -> None: """Initialize the accessor.""" self._panel_client = _PanelClient( - provided_interface=provided_interface, service_class=service_class, discovery_client=discovery_client, grpc_channel_pool=grpc_channel_pool, diff --git a/src/nipanel/_streamlit_panel.py b/src/nipanel/_streamlit_panel.py index 90042d6f..7076a36b 100644 --- a/src/nipanel/_streamlit_panel.py +++ b/src/nipanel/_streamlit_panel.py @@ -41,7 +41,6 @@ def __init__( """ super().__init__( panel_id=panel_id, - provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE, service_class=STREAMLIT_PYTHON_PANEL_SERVICE, discovery_client=discovery_client, grpc_channel_pool=grpc_channel_pool, diff --git a/src/nipanel/_streamlit_panel_initializer.py b/src/nipanel/_streamlit_panel_initializer.py index ce8fcbed..ff24bc5d 100644 --- a/src/nipanel/_streamlit_panel_initializer.py +++ b/src/nipanel/_streamlit_panel_initializer.py @@ -84,7 +84,6 @@ def _initialize_panel_from_base_path() -> PanelValueAccessor: return PanelValueAccessor( panel_id=panel_id, notify_on_set_value=False, - provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE, service_class=STREAMLIT_PYTHON_PANEL_SERVICE, ) diff --git a/src/nipanel/converters/builtin.py b/src/nipanel/converters/builtin.py index bcd1e449..c004315c 100644 --- a/src/nipanel/converters/builtin.py +++ b/src/nipanel/converters/builtin.py @@ -4,7 +4,7 @@ from typing import Type from google.protobuf import wrappers_pb2 -from ni.pythonpanel.v1 import python_panel_types_pb2 +from ni.panels.v1 import panel_types_pb2 from nipanel.converters import Converter @@ -119,7 +119,7 @@ def to_python_value(self, protobuf_message: wrappers_pb2.StringValue) -> str: return protobuf_message.value -class BoolCollectionConverter(Converter[Collection[bool], python_panel_types_pb2.BoolCollection]): +class BoolCollectionConverter(Converter[Collection[bool], panel_types_pb2.BoolCollection]): """A converter for a Collection of bools.""" @property @@ -128,26 +128,20 @@ def python_typename(self) -> str: return f"{Collection.__name__}.{bool.__name__}" @property - def protobuf_message(self) -> Type[python_panel_types_pb2.BoolCollection]: + def protobuf_message(self) -> Type[panel_types_pb2.BoolCollection]: """The type-specific protobuf message for the Python type.""" - return python_panel_types_pb2.BoolCollection + return panel_types_pb2.BoolCollection - def to_protobuf_message( - self, python_value: Collection[bool] - ) -> python_panel_types_pb2.BoolCollection: - """Convert the collection of bools to python_panel_types_pb2.BoolCollection.""" + def to_protobuf_message(self, python_value: Collection[bool]) -> panel_types_pb2.BoolCollection: + """Convert the collection of bools to panel_types_pb2.BoolCollection.""" return self.protobuf_message(values=python_value) - def to_python_value( - self, protobuf_message: python_panel_types_pb2.BoolCollection - ) -> Collection[bool]: + def to_python_value(self, protobuf_message: panel_types_pb2.BoolCollection) -> Collection[bool]: """Convert the protobuf message to a Python collection of bools.""" return list(protobuf_message.values) -class BytesCollectionConverter( - Converter[Collection[bytes], python_panel_types_pb2.ByteStringCollection] -): +class BytesCollectionConverter(Converter[Collection[bytes], panel_types_pb2.ByteStringCollection]): """A converter for a Collection of byte strings.""" @property @@ -156,26 +150,24 @@ def python_typename(self) -> str: return f"{Collection.__name__}.{bytes.__name__}" @property - def protobuf_message(self) -> Type[python_panel_types_pb2.ByteStringCollection]: + def protobuf_message(self) -> Type[panel_types_pb2.ByteStringCollection]: """The type-specific protobuf message for the Python type.""" - return python_panel_types_pb2.ByteStringCollection + return panel_types_pb2.ByteStringCollection def to_protobuf_message( self, python_value: Collection[bytes] - ) -> python_panel_types_pb2.ByteStringCollection: - """Convert the collection of byte strings to python_panel_types_pb2.ByteStringCollection.""" + ) -> panel_types_pb2.ByteStringCollection: + """Convert the collection of byte strings to panel_types_pb2.ByteStringCollection.""" return self.protobuf_message(values=python_value) def to_python_value( - self, protobuf_message: python_panel_types_pb2.ByteStringCollection + self, protobuf_message: panel_types_pb2.ByteStringCollection ) -> Collection[bytes]: """Convert the protobuf message to a Python collection of byte strings.""" return list(protobuf_message.values) -class FloatCollectionConverter( - Converter[Collection[float], python_panel_types_pb2.FloatCollection] -): +class FloatCollectionConverter(Converter[Collection[float], panel_types_pb2.FloatCollection]): """A converter for a Collection of floats.""" @property @@ -184,24 +176,24 @@ def python_typename(self) -> str: return f"{Collection.__name__}.{float.__name__}" @property - def protobuf_message(self) -> Type[python_panel_types_pb2.FloatCollection]: + def protobuf_message(self) -> Type[panel_types_pb2.FloatCollection]: """The type-specific protobuf message for the Python type.""" - return python_panel_types_pb2.FloatCollection + return panel_types_pb2.FloatCollection def to_protobuf_message( self, python_value: Collection[float] - ) -> python_panel_types_pb2.FloatCollection: - """Convert the collection of floats to python_panel_types_pb2.FloatCollection.""" + ) -> panel_types_pb2.FloatCollection: + """Convert the collection of floats to panel_types_pb2.FloatCollection.""" return self.protobuf_message(values=python_value) def to_python_value( - self, protobuf_message: python_panel_types_pb2.FloatCollection + self, protobuf_message: panel_types_pb2.FloatCollection ) -> Collection[float]: """Convert the protobuf message to a Python collection of floats.""" return list(protobuf_message.values) -class IntCollectionConverter(Converter[Collection[int], python_panel_types_pb2.IntCollection]): +class IntCollectionConverter(Converter[Collection[int], panel_types_pb2.IntCollection]): """A converter for a Collection of integers.""" @property @@ -210,24 +202,20 @@ def python_typename(self) -> str: return f"{Collection.__name__}.{int.__name__}" @property - def protobuf_message(self) -> Type[python_panel_types_pb2.IntCollection]: + def protobuf_message(self) -> Type[panel_types_pb2.IntCollection]: """The type-specific protobuf message for the Python type.""" - return python_panel_types_pb2.IntCollection + return panel_types_pb2.IntCollection - def to_protobuf_message( - self, python_value: Collection[int] - ) -> python_panel_types_pb2.IntCollection: - """Convert the collection of integers to python_panel_types_pb2.IntCollection.""" + def to_protobuf_message(self, python_value: Collection[int]) -> panel_types_pb2.IntCollection: + """Convert the collection of integers to panel_types_pb2.IntCollection.""" return self.protobuf_message(values=python_value) - def to_python_value( - self, protobuf_message: python_panel_types_pb2.IntCollection - ) -> Collection[int]: + def to_python_value(self, protobuf_message: panel_types_pb2.IntCollection) -> Collection[int]: """Convert the protobuf message to a Python collection of integers.""" return list(protobuf_message.values) -class StrCollectionConverter(Converter[Collection[str], python_panel_types_pb2.StringCollection]): +class StrCollectionConverter(Converter[Collection[str], panel_types_pb2.StringCollection]): """A converter for a Collection of strings.""" @property @@ -236,18 +224,18 @@ def python_typename(self) -> str: return f"{Collection.__name__}.{str.__name__}" @property - def protobuf_message(self) -> Type[python_panel_types_pb2.StringCollection]: + def protobuf_message(self) -> Type[panel_types_pb2.StringCollection]: """The type-specific protobuf message for the Python type.""" - return python_panel_types_pb2.StringCollection + return panel_types_pb2.StringCollection def to_protobuf_message( self, python_value: Collection[str] - ) -> python_panel_types_pb2.StringCollection: - """Convert the collection of strings to python_panel_types_pb2.StringCollection.""" + ) -> panel_types_pb2.StringCollection: + """Convert the collection of strings to panel_types_pb2.StringCollection.""" return self.protobuf_message(values=python_value) def to_python_value( - self, protobuf_message: python_panel_types_pb2.StringCollection + self, protobuf_message: panel_types_pb2.StringCollection ) -> Collection[str]: """Convert the protobuf message to a Python collection of strings.""" return list(protobuf_message.values) diff --git a/tests/conftest.py b/tests/conftest.py index 963fc3a9..ff9c2a01 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ import grpc import pytest from grpc.framework.foundation import logging_pool -from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub +from ni.panels.v1.panel_service_pb2_grpc import PanelServiceStub from tests.utils._fake_python_panel_service import FakePythonPanelService @@ -38,6 +38,6 @@ def fake_panel_channel( @pytest.fixture def python_panel_service_stub( fake_panel_channel: grpc.Channel, -) -> Generator[PythonPanelServiceStub]: - """Fixture to get a PythonPanelServiceStub, attached to a FakePythonPanelService.""" - yield PythonPanelServiceStub(fake_panel_channel) +) -> Generator[PanelServiceStub]: + """Fixture to get a PanelServiceStub, attached to a FakePythonPanelService.""" + yield PanelServiceStub(fake_panel_channel) diff --git a/tests/unit/test_convert.py b/tests/unit/test_convert.py index 7580fef8..a68131c0 100644 --- a/tests/unit/test_convert.py +++ b/tests/unit/test_convert.py @@ -4,8 +4,8 @@ import pytest from google.protobuf import any_pb2, wrappers_pb2 from google.protobuf.message import Message +from ni.panels.v1 import panel_types_pb2 from ni.protobuf.types.scalar_pb2 import ScalarData -from ni.pythonpanel.v1 import python_panel_types_pb2 from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types.array_pb2 import ( Double2DArray, ) @@ -29,11 +29,11 @@ ] _AnyPanelPbTypes: TypeAlias = Union[ - python_panel_types_pb2.BoolCollection, - python_panel_types_pb2.ByteStringCollection, - python_panel_types_pb2.FloatCollection, - python_panel_types_pb2.IntCollection, - python_panel_types_pb2.StringCollection, + panel_types_pb2.BoolCollection, + panel_types_pb2.ByteStringCollection, + panel_types_pb2.FloatCollection, + panel_types_pb2.IntCollection, + panel_types_pb2.StringCollection, ] @@ -126,11 +126,11 @@ def test___python_builtin_scalar___to_any___valid_wrapperpb2_value( @pytest.mark.parametrize( "proto_type, default_value, expected_value", [ - (python_panel_types_pb2.BoolCollection, [False, False, False], [True, True, True]), - (python_panel_types_pb2.ByteStringCollection, [b"", b"", b""], [b"a", b"b", b"c"]), - (python_panel_types_pb2.FloatCollection, [0.0, 0.0, 0.0], [1.0, 2.0, 3.0]), - (python_panel_types_pb2.IntCollection, [0, 0, 0], [1, 2, 3]), - (python_panel_types_pb2.StringCollection, ["", "", ""], ["a", "b", "c"]), + (panel_types_pb2.BoolCollection, [False, False, False], [True, True, True]), + (panel_types_pb2.ByteStringCollection, [b"", b"", b""], [b"a", b"b", b"c"]), + (panel_types_pb2.FloatCollection, [0.0, 0.0, 0.0], [1.0, 2.0, 3.0]), + (panel_types_pb2.IntCollection, [0, 0, 0], [1, 2, 3]), + (panel_types_pb2.StringCollection, ["", "", ""], ["a", "b", "c"]), ], ) def test___python_panel_collection___to_any___valid_paneltype_value( @@ -178,11 +178,11 @@ def test___wrapperpb2_value___from_any___valid_python_value( @pytest.mark.parametrize( "proto_type, expected_value", [ - (python_panel_types_pb2.BoolCollection, [True, True, True]), - (python_panel_types_pb2.ByteStringCollection, [b"a", b"b", b"c"]), - (python_panel_types_pb2.FloatCollection, [1.0, 2.0, 3.0]), - (python_panel_types_pb2.IntCollection, [1, 2, 3]), - (python_panel_types_pb2.StringCollection, ["a", "b", "c"]), + (panel_types_pb2.BoolCollection, [True, True, True]), + (panel_types_pb2.ByteStringCollection, [b"a", b"b", b"c"]), + (panel_types_pb2.FloatCollection, [1.0, 2.0, 3.0]), + (panel_types_pb2.IntCollection, [1, 2, 3]), + (panel_types_pb2.StringCollection, ["a", "b", "c"]), ], ) def test___paneltype_value___from_any___valid_python_value( diff --git a/tests/unit/test_python_panel_service_stub.py b/tests/unit/test_python_panel_service_stub.py index 455815af..760b2053 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -1,7 +1,7 @@ import pytest from google.protobuf.any_pb2 import Any from google.protobuf.wrappers_pb2 import StringValue -from ni.pythonpanel.v1.python_panel_service_pb2 import ( +from ni.panels.v1.panel_service_pb2 import ( StreamlitPanelConfiguration, StartPanelRequest, StopPanelRequest, @@ -10,10 +10,10 @@ TryGetValueRequest, SetValueRequest, ) -from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub +from ni.panels.v1.panel_service_pb2_grpc import PanelServiceStub -def test___start_panel___gets_response(python_panel_service_stub: PythonPanelServiceStub) -> None: +def test___start_panel___gets_response(python_panel_service_stub: PanelServiceStub) -> None: configuration = StreamlitPanelConfiguration(panel_script_path="path/to/panel.py") request = StartPanelRequest(panel_id="test_panel", streamlit_panel_configuration=configuration) response = python_panel_service_stub.StartPanel(request) @@ -22,7 +22,7 @@ def test___start_panel___gets_response(python_panel_service_stub: PythonPanelSer def test___start_panel___stop_panel___gets_response( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: configuration = StreamlitPanelConfiguration(panel_script_path="path/to/panel.py") start_request = StartPanelRequest( @@ -37,7 +37,7 @@ def test___start_panel___stop_panel___gets_response( def test___enumerate_panels___gets_response( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: request = EnumeratePanelsRequest() response = python_panel_service_stub.EnumeratePanels(request) @@ -46,7 +46,7 @@ def test___enumerate_panels___gets_response( def test___set_value___gets_response( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: test_value = Any() test_value.Pack(StringValue(value="test_value")) @@ -57,7 +57,7 @@ def test___set_value___gets_response( def test___set_value___get_value___gets_response( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: test_value = Any() test_value.Pack(StringValue(value="test_value")) @@ -72,7 +72,7 @@ def test___set_value___get_value___gets_response( def test___no_value___get_value___raises_exception( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: request = GetValueRequest(panel_id="test_panel", value_id="test_value") with pytest.raises(Exception): @@ -80,7 +80,7 @@ def test___no_value___get_value___raises_exception( def test___set_value___try_get_value___gets_response( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: test_value = Any() test_value.Pack(StringValue(value="test_value")) @@ -95,7 +95,7 @@ def test___set_value___try_get_value___gets_response( def test___no_value___try_get_value___gets_no_value( - python_panel_service_stub: PythonPanelServiceStub, + python_panel_service_stub: PanelServiceStub, ) -> None: request = TryGetValueRequest(panel_id="test_panel", value_id="test_value") response = python_panel_service_stub.TryGetValue(request) diff --git a/tests/utils/_fake_python_panel_service.py b/tests/utils/_fake_python_panel_service.py index 547f0db6..13eef73a 100644 --- a/tests/utils/_fake_python_panel_service.py +++ b/tests/utils/_fake_python_panel_service.py @@ -1,8 +1,8 @@ from concurrent import futures import grpc -from ni.pythonpanel.v1.python_panel_service_pb2_grpc import ( - add_PythonPanelServiceServicer_to_server, +from ni.panels.v1.panel_service_pb2_grpc import ( + add_PanelServiceServicer_to_server, ) from tests.utils._fake_python_panel_servicer import FakePythonPanelServicer @@ -20,7 +20,7 @@ def __init__(self) -> None: def start(self, thread_pool: futures.ThreadPoolExecutor) -> None: """Start the gRPC server and return the port it is bound to.""" self._server = grpc.server(thread_pool) - add_PythonPanelServiceServicer_to_server(self._servicer, self._server) + add_PanelServiceServicer_to_server(self._servicer, self._server) self._port = self._server.add_insecure_port("[::1]:0") self._server.start() diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index 346c05db..806a4d58 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -1,7 +1,7 @@ from typing import Any import grpc -from ni.pythonpanel.v1.python_panel_service_pb2 import ( +from ni.panels.v1.panel_service_pb2 import ( StartPanelRequest, StartPanelResponse, StopPanelRequest, @@ -16,11 +16,11 @@ SetValueRequest, SetValueResponse, ) -from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceServicer +from ni.panels.v1.panel_service_pb2_grpc import PanelServiceServicer -class FakePythonPanelServicer(PythonPanelServiceServicer): - """Fake implementation of the PythonPanelServicer for testing.""" +class FakePythonPanelServicer(PanelServiceServicer): + """Fake implementation of the PanelServiceServicer for testing.""" def __init__(self) -> None: """Initialize the fake PythonPanelServicer.""" From 36574631545047e8d4b683bed9953660bc4e84fd Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Fri, 18 Jul 2025 15:00:32 -0500 Subject: [PATCH 6/9] extract StreamlitPanelConfiguration to streamlit_panel_configuration.proto --- CONTRIBUTING.md | 2 +- protos/ni/panels/v1/panel_service.proto | 15 +---- .../v1/streamlit_panel_configuration.proto | 23 +++++++ src/ni/panels/v1/panel_service_pb2.py | 60 +++++++++---------- src/ni/panels/v1/panel_service_pb2.pyi | 33 +++------- .../v1/streamlit_panel_configuration_pb2.py | 26 ++++++++ .../v1/streamlit_panel_configuration_pb2.pyi | 33 ++++++++++ .../streamlit_panel_configuration_pb2_grpc.py | 4 ++ ...streamlit_panel_configuration_pb2_grpc.pyi | 17 ++++++ src/nipanel/_panel_client.py | 7 ++- src/nipanel/_streamlit_constants.py | 2 +- tests/unit/test_python_panel_service_stub.py | 12 ++-- tests/utils/_fake_python_panel_servicer.py | 5 +- 13 files changed, 159 insertions(+), 80 deletions(-) create mode 100644 protos/ni/panels/v1/streamlit_panel_configuration.proto create mode 100644 src/ni/panels/v1/streamlit_panel_configuration_pb2.py create mode 100644 src/ni/panels/v1/streamlit_panel_configuration_pb2.pyi create mode 100644 src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.py create mode 100644 src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.pyi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef4178a4..2f7ea86d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ See [GitHub's official documentation](https://help.github.com/articles/using-pul # Getting Started This is the command to generate the files in /src/ni/panels/v1/: -`poetry run python -m grpc_tools.protoc --proto_path=protos --python_out=src/ --grpc_python_out=src/ --mypy_out=src/ --mypy_grpc_out=src/ ni/panels/v1/panel_service.proto ni/panels/v1/panel_types.proto` +`poetry run python -m grpc_tools.protoc --proto_path=protos --python_out=src/ --grpc_python_out=src/ --mypy_out=src/ --mypy_grpc_out=src/ ni/panels/v1/panel_service.proto ni/panels/v1/panel_types.proto ni/panels/v1/streamlit_panel_configuration.proto` # Testing diff --git a/protos/ni/panels/v1/panel_service.proto b/protos/ni/panels/v1/panel_service.proto index acc91fe0..a96acc0d 100644 --- a/protos/ni/panels/v1/panel_service.proto +++ b/protos/ni/panels/v1/panel_service.proto @@ -59,23 +59,12 @@ service PanelService { rpc SetValue(SetValueRequest) returns (SetValueResponse); } -message StreamlitPanelConfiguration -{ - // Absolute path of the streamlit script file on disk, or network path to the file. This must end in ".py". - string panel_script_path = 1; - - // Path to the python interpreter to use for running the streamlit script. - string python_path = 2; -} - message StartPanelRequest { // Unique ID of the panel string panel_id = 1; - // Configuration for the panel - oneof panel_configuration { - StreamlitPanelConfiguration streamlit_panel_configuration = 2; - } + // Configuration for the panel, packed as Any + google.protobuf.Any panel_configuration = 2; } message StartPanelResponse { diff --git a/protos/ni/panels/v1/streamlit_panel_configuration.proto b/protos/ni/panels/v1/streamlit_panel_configuration.proto new file mode 100644 index 00000000..82e3abfb --- /dev/null +++ b/protos/ni/panels/v1/streamlit_panel_configuration.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package ni.panels.v1; + +option cc_enable_arenas = true; +option csharp_namespace = "NationalInstruments.Panels.V1"; +option go_package = "panelsv1"; +option java_multiple_files = true; +option java_outer_classname = "StreamlitPanelConfigurationProto"; +option java_package = "com.ni.panels.v1"; +option objc_class_prefix = "NIPS"; +option php_namespace = "NI\\Panels\\V1"; +option ruby_package = "NI::Panels::V1"; + +// Pack this into PanelService.StartPanelRequest.panel_configuration to start a Streamlit panel. +message StreamlitPanelConfiguration +{ + // Absolute path of the streamlit script file on disk, or network path to the file. This must end in ".py". + string panel_script_path = 1; + + // Path to the python interpreter to use for running the streamlit script. + string python_path = 2; +} \ No newline at end of file diff --git a/src/ni/panels/v1/panel_service_pb2.py b/src/ni/panels/v1/panel_service_pb2.py index 56611f83..fe0a80c8 100644 --- a/src/ni/panels/v1/panel_service_pb2.py +++ b/src/ni/panels/v1/panel_service_pb2.py @@ -14,7 +14,7 @@ from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n ni/panels/v1/panel_service.proto\x12\x0cni.panels.v1\x1a\x19google/protobuf/any.proto\"M\n\x1bStreamlitPanelConfiguration\x12\x19\n\x11panel_script_path\x18\x01 \x01(\t\x12\x13\n\x0bpython_path\x18\x02 \x01(\t\"\x90\x01\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12R\n\x1dstreamlit_panel_configuration\x18\x02 \x01(\x0b\x32).ni.panels.v1.StreamlitPanelConfigurationH\x00\x42\x15\n\x13panel_configuration\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_uri\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_uri\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"I\n\x17\x45numeratePanelsResponse\x12.\n\x06panels\x18\x01 \x03(\x0b\x32\x1e.ni.panels.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xf7\x03\n\x0cPanelService\x12O\n\nStartPanel\x12\x1f.ni.panels.v1.StartPanelRequest\x1a .ni.panels.v1.StartPanelResponse\x12L\n\tStopPanel\x12\x1e.ni.panels.v1.StopPanelRequest\x1a\x1f.ni.panels.v1.StopPanelResponse\x12^\n\x0f\x45numeratePanels\x12$.ni.panels.v1.EnumeratePanelsRequest\x1a%.ni.panels.v1.EnumeratePanelsResponse\x12I\n\x08GetValue\x12\x1d.ni.panels.v1.GetValueRequest\x1a\x1e.ni.panels.v1.GetValueResponse\x12R\n\x0bTryGetValue\x12 .ni.panels.v1.TryGetValueRequest\x1a!.ni.panels.v1.TryGetValueResponse\x12I\n\x08SetValue\x12\x1d.ni.panels.v1.SetValueRequest\x1a\x1e.ni.panels.v1.SetValueResponseBu\n\x10\x63om.ni.panels.v1B\x0bPanelsProtoP\x01Z\x08panelsv1\xf8\x01\x01\xa2\x02\x04NIPS\xaa\x02\x1dNationalInstruments.Panels.V1\xca\x02\x0cNI\\Panels\\V1\xea\x02\x0eNI::Panels::V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n ni/panels/v1/panel_service.proto\x12\x0cni.panels.v1\x1a\x19google/protobuf/any.proto\"X\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x31\n\x13panel_configuration\x18\x02 \x01(\x0b\x32\x14.google.protobuf.Any\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_uri\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_uri\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"I\n\x17\x45numeratePanelsResponse\x12.\n\x06panels\x18\x01 \x03(\x0b\x32\x1e.ni.panels.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xf7\x03\n\x0cPanelService\x12O\n\nStartPanel\x12\x1f.ni.panels.v1.StartPanelRequest\x1a .ni.panels.v1.StartPanelResponse\x12L\n\tStopPanel\x12\x1e.ni.panels.v1.StopPanelRequest\x1a\x1f.ni.panels.v1.StopPanelResponse\x12^\n\x0f\x45numeratePanels\x12$.ni.panels.v1.EnumeratePanelsRequest\x1a%.ni.panels.v1.EnumeratePanelsResponse\x12I\n\x08GetValue\x12\x1d.ni.panels.v1.GetValueRequest\x1a\x1e.ni.panels.v1.GetValueResponse\x12R\n\x0bTryGetValue\x12 .ni.panels.v1.TryGetValueRequest\x1a!.ni.panels.v1.TryGetValueResponse\x12I\n\x08SetValue\x12\x1d.ni.panels.v1.SetValueRequest\x1a\x1e.ni.panels.v1.SetValueResponseBu\n\x10\x63om.ni.panels.v1B\x0bPanelsProtoP\x01Z\x08panelsv1\xf8\x01\x01\xa2\x02\x04NIPS\xaa\x02\x1dNationalInstruments.Panels.V1\xca\x02\x0cNI\\Panels\\V1\xea\x02\x0eNI::Panels::V1b\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.panels.v1.panel_service_pb2', globals()) @@ -22,34 +22,32 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\020com.ni.panels.v1B\013PanelsProtoP\001Z\010panelsv1\370\001\001\242\002\004NIPS\252\002\035NationalInstruments.Panels.V1\312\002\014NI\\Panels\\V1\352\002\016NI::Panels::V1' - _STREAMLITPANELCONFIGURATION._serialized_start=77 - _STREAMLITPANELCONFIGURATION._serialized_end=154 - _STARTPANELREQUEST._serialized_start=157 - _STARTPANELREQUEST._serialized_end=301 - _STARTPANELRESPONSE._serialized_start=303 - _STARTPANELRESPONSE._serialized_end=342 - _STOPPANELREQUEST._serialized_start=344 - _STOPPANELREQUEST._serialized_end=395 - _STOPPANELRESPONSE._serialized_start=397 - _STOPPANELRESPONSE._serialized_end=416 - _ENUMERATEPANELSREQUEST._serialized_start=418 - _ENUMERATEPANELSREQUEST._serialized_end=442 - _PANELINFORMATION._serialized_start=444 - _PANELINFORMATION._serialized_end=518 - _ENUMERATEPANELSRESPONSE._serialized_start=520 - _ENUMERATEPANELSRESPONSE._serialized_end=593 - _GETVALUEREQUEST._serialized_start=595 - _GETVALUEREQUEST._serialized_end=648 - _GETVALUERESPONSE._serialized_start=650 - _GETVALUERESPONSE._serialized_end=705 - _TRYGETVALUEREQUEST._serialized_start=707 - _TRYGETVALUEREQUEST._serialized_end=763 - _TRYGETVALUERESPONSE._serialized_start=765 - _TRYGETVALUERESPONSE._serialized_end=838 - _SETVALUEREQUEST._serialized_start=840 - _SETVALUEREQUEST._serialized_end=946 - _SETVALUERESPONSE._serialized_start=948 - _SETVALUERESPONSE._serialized_end=966 - _PANELSERVICE._serialized_start=969 - _PANELSERVICE._serialized_end=1472 + _STARTPANELREQUEST._serialized_start=77 + _STARTPANELREQUEST._serialized_end=165 + _STARTPANELRESPONSE._serialized_start=167 + _STARTPANELRESPONSE._serialized_end=206 + _STOPPANELREQUEST._serialized_start=208 + _STOPPANELREQUEST._serialized_end=259 + _STOPPANELRESPONSE._serialized_start=261 + _STOPPANELRESPONSE._serialized_end=280 + _ENUMERATEPANELSREQUEST._serialized_start=282 + _ENUMERATEPANELSREQUEST._serialized_end=306 + _PANELINFORMATION._serialized_start=308 + _PANELINFORMATION._serialized_end=382 + _ENUMERATEPANELSRESPONSE._serialized_start=384 + _ENUMERATEPANELSRESPONSE._serialized_end=457 + _GETVALUEREQUEST._serialized_start=459 + _GETVALUEREQUEST._serialized_end=512 + _GETVALUERESPONSE._serialized_start=514 + _GETVALUERESPONSE._serialized_end=569 + _TRYGETVALUEREQUEST._serialized_start=571 + _TRYGETVALUEREQUEST._serialized_end=627 + _TRYGETVALUERESPONSE._serialized_start=629 + _TRYGETVALUERESPONSE._serialized_end=702 + _SETVALUEREQUEST._serialized_start=704 + _SETVALUEREQUEST._serialized_end=810 + _SETVALUERESPONSE._serialized_start=812 + _SETVALUERESPONSE._serialized_end=830 + _PANELSERVICE._serialized_start=833 + _PANELSERVICE._serialized_end=1336 # @@protoc_insertion_point(module_scope) diff --git a/src/ni/panels/v1/panel_service_pb2.pyi b/src/ni/panels/v1/panel_service_pb2.pyi index 8710881d..538636b4 100644 --- a/src/ni/panels/v1/panel_service_pb2.pyi +++ b/src/ni/panels/v1/panel_service_pb2.pyi @@ -13,45 +13,26 @@ import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -@typing.final -class StreamlitPanelConfiguration(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - PANEL_SCRIPT_PATH_FIELD_NUMBER: builtins.int - PYTHON_PATH_FIELD_NUMBER: builtins.int - panel_script_path: builtins.str - """Absolute path of the streamlit script file on disk, or network path to the file. This must end in ".py".""" - python_path: builtins.str - """Path to the python interpreter to use for running the streamlit script.""" - def __init__( - self, - *, - panel_script_path: builtins.str = ..., - python_path: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["panel_script_path", b"panel_script_path", "python_path", b"python_path"]) -> None: ... - -global___StreamlitPanelConfiguration = StreamlitPanelConfiguration - @typing.final class StartPanelRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PANEL_ID_FIELD_NUMBER: builtins.int - STREAMLIT_PANEL_CONFIGURATION_FIELD_NUMBER: builtins.int + PANEL_CONFIGURATION_FIELD_NUMBER: builtins.int panel_id: builtins.str """Unique ID of the panel""" @property - def streamlit_panel_configuration(self) -> global___StreamlitPanelConfiguration: ... + def panel_configuration(self) -> google.protobuf.any_pb2.Any: + """Configuration for the panel, packed as Any""" + def __init__( self, *, panel_id: builtins.str = ..., - streamlit_panel_configuration: global___StreamlitPanelConfiguration | None = ..., + panel_configuration: google.protobuf.any_pb2.Any | None = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["panel_configuration", b"panel_configuration", "streamlit_panel_configuration", b"streamlit_panel_configuration"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["panel_configuration", b"panel_configuration", "panel_id", b"panel_id", "streamlit_panel_configuration", b"streamlit_panel_configuration"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["panel_configuration", b"panel_configuration"]) -> typing.Literal["streamlit_panel_configuration"] | None: ... + def HasField(self, field_name: typing.Literal["panel_configuration", b"panel_configuration"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["panel_configuration", b"panel_configuration", "panel_id", b"panel_id"]) -> None: ... global___StartPanelRequest = StartPanelRequest diff --git a/src/ni/panels/v1/streamlit_panel_configuration_pb2.py b/src/ni/panels/v1/streamlit_panel_configuration_pb2.py new file mode 100644 index 00000000..6e4f6eb1 --- /dev/null +++ b/src/ni/panels/v1/streamlit_panel_configuration_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ni/panels/v1/streamlit_panel_configuration.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n0ni/panels/v1/streamlit_panel_configuration.proto\x12\x0cni.panels.v1\"M\n\x1bStreamlitPanelConfiguration\x12\x19\n\x11panel_script_path\x18\x01 \x01(\t\x12\x13\n\x0bpython_path\x18\x02 \x01(\tB\x8a\x01\n\x10\x63om.ni.panels.v1B StreamlitPanelConfigurationProtoP\x01Z\x08panelsv1\xf8\x01\x01\xa2\x02\x04NIPS\xaa\x02\x1dNationalInstruments.Panels.V1\xca\x02\x0cNI\\Panels\\V1\xea\x02\x0eNI::Panels::V1b\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.panels.v1.streamlit_panel_configuration_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\020com.ni.panels.v1B StreamlitPanelConfigurationProtoP\001Z\010panelsv1\370\001\001\242\002\004NIPS\252\002\035NationalInstruments.Panels.V1\312\002\014NI\\Panels\\V1\352\002\016NI::Panels::V1' + _STREAMLITPANELCONFIGURATION._serialized_start=66 + _STREAMLITPANELCONFIGURATION._serialized_end=143 +# @@protoc_insertion_point(module_scope) diff --git a/src/ni/panels/v1/streamlit_panel_configuration_pb2.pyi b/src/ni/panels/v1/streamlit_panel_configuration_pb2.pyi new file mode 100644 index 00000000..d62d0af0 --- /dev/null +++ b/src/ni/panels/v1/streamlit_panel_configuration_pb2.pyi @@ -0,0 +1,33 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class StreamlitPanelConfiguration(google.protobuf.message.Message): + """Pack this into PanelService.StartPanelRequest.panel_configuration to start a Streamlit panel.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PANEL_SCRIPT_PATH_FIELD_NUMBER: builtins.int + PYTHON_PATH_FIELD_NUMBER: builtins.int + panel_script_path: builtins.str + """Absolute path of the streamlit script file on disk, or network path to the file. This must end in ".py".""" + python_path: builtins.str + """Path to the python interpreter to use for running the streamlit script.""" + def __init__( + self, + *, + panel_script_path: builtins.str = ..., + python_path: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_script_path", b"panel_script_path", "python_path", b"python_path"]) -> None: ... + +global___StreamlitPanelConfiguration = StreamlitPanelConfiguration diff --git a/src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.py b/src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.py new file mode 100644 index 00000000..2daafffe --- /dev/null +++ b/src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.pyi b/src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.pyi new file mode 100644 index 00000000..a6a9cff9 --- /dev/null +++ b/src/ni/panels/v1/streamlit_panel_configuration_pb2_grpc.pyi @@ -0,0 +1,17 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import abc +import collections.abc +import grpc +import grpc.aio +import typing + +_T = typing.TypeVar("_T") + +class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ... + +class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] + ... diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 1fc33bca..928ab8fc 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -5,8 +5,8 @@ from typing import Callable, TypeVar import grpc +from google.protobuf.any_pb2 import Any from ni.panels.v1.panel_service_pb2 import ( - StreamlitPanelConfiguration, StartPanelRequest, StopPanelRequest, EnumeratePanelsRequest, @@ -15,6 +15,7 @@ SetValueRequest, ) from ni.panels.v1.panel_service_pb2_grpc import PanelServiceStub +from ni.panels.v1.streamlit_panel_configuration_pb2 import StreamlitPanelConfiguration from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool from typing_extensions import ParamSpec @@ -52,8 +53,10 @@ def start_streamlit_panel(self, panel_id: str, panel_script_path: str, python_pa streamlit_panel_configuration = StreamlitPanelConfiguration( panel_script_path=panel_script_path, python_path=python_path ) + panel_configuration_any = Any() + panel_configuration_any.Pack(streamlit_panel_configuration) start_panel_request = StartPanelRequest( - panel_id=panel_id, streamlit_panel_configuration=streamlit_panel_configuration + panel_id=panel_id, panel_configuration=panel_configuration_any ) response = self._invoke_with_retry(self._get_stub().StartPanel, start_panel_request) return response.panel_uri diff --git a/src/nipanel/_streamlit_constants.py b/src/nipanel/_streamlit_constants.py index f1350c11..cefeea35 100644 --- a/src/nipanel/_streamlit_constants.py +++ b/src/nipanel/_streamlit_constants.py @@ -1,2 +1,2 @@ -STREAMLIT_PYTHON_PANEL_SERVICE = "ni.pythonpanel.v1.PythonPanelService" +STREAMLIT_PYTHON_PANEL_SERVICE = "ni.panels.v1.PanelService" STREAMLIT_REFRESH_COMPONENT_URL = "http://localhost:42001/panel-service/refresh" diff --git a/tests/unit/test_python_panel_service_stub.py b/tests/unit/test_python_panel_service_stub.py index 760b2053..e2521e26 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -2,7 +2,6 @@ from google.protobuf.any_pb2 import Any from google.protobuf.wrappers_pb2 import StringValue from ni.panels.v1.panel_service_pb2 import ( - StreamlitPanelConfiguration, StartPanelRequest, StopPanelRequest, EnumeratePanelsRequest, @@ -11,11 +10,14 @@ SetValueRequest, ) from ni.panels.v1.panel_service_pb2_grpc import PanelServiceStub +from ni.panels.v1.streamlit_panel_configuration_pb2 import StreamlitPanelConfiguration def test___start_panel___gets_response(python_panel_service_stub: PanelServiceStub) -> None: configuration = StreamlitPanelConfiguration(panel_script_path="path/to/panel.py") - request = StartPanelRequest(panel_id="test_panel", streamlit_panel_configuration=configuration) + configuration_any = Any() + configuration_any.Pack(configuration) + request = StartPanelRequest(panel_id="test_panel", panel_configuration=configuration_any) response = python_panel_service_stub.StartPanel(request) assert response.panel_uri == "http://localhost:50051/test_panel" @@ -25,9 +27,9 @@ def test___start_panel___stop_panel___gets_response( python_panel_service_stub: PanelServiceStub, ) -> None: configuration = StreamlitPanelConfiguration(panel_script_path="path/to/panel.py") - start_request = StartPanelRequest( - panel_id="test_panel", streamlit_panel_configuration=configuration - ) + configuration_any = Any() + configuration_any.Pack(configuration) + start_request = StartPanelRequest(panel_id="test_panel", panel_configuration=configuration_any) python_panel_service_stub.StartPanel(start_request) stop_request = StopPanelRequest(panel_id="test_panel", reset=False) diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index 806a4d58..d6d4c102 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -17,6 +17,7 @@ SetValueResponse, ) from ni.panels.v1.panel_service_pb2_grpc import PanelServiceServicer +from ni.panels.v1.streamlit_panel_configuration_pb2 import StreamlitPanelConfiguration class FakePythonPanelServicer(PanelServiceServicer): @@ -36,7 +37,9 @@ def StartPanel( # noqa: N802 self, request: StartPanelRequest, context: Any ) -> StartPanelResponse: """Trivial implementation for testing.""" - self._python_path = request.streamlit_panel_configuration.python_path + streamlit_panel_configuration = StreamlitPanelConfiguration() + request.panel_configuration.Unpack(streamlit_panel_configuration) + self._python_path = streamlit_panel_configuration.python_path if self._fail_next_start_panel: self._fail_next_start_panel = False context.abort(grpc.StatusCode.UNAVAILABLE, "Simulated failure") From a78b15ac4de32cb1c435f051cdb65e25054ef4ad Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Fri, 18 Jul 2025 15:31:49 -0500 Subject: [PATCH 7/9] remove service class since it is not needed by the discovery service, provided interface is enough --- src/nipanel/_panel_client.py | 11 +++-------- src/nipanel/_panel_value_accessor.py | 2 -- src/nipanel/_streamlit_constants.py | 1 - src/nipanel/_streamlit_panel.py | 2 -- src/nipanel/_streamlit_panel_initializer.py | 2 -- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 928ab8fc..8c284b06 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -30,20 +30,18 @@ _logger = logging.getLogger(__name__) -PANEL_INTERFACE = "ni.panels.v1.PanelService" +PANEL_SERVICE = "ni.panels.v1.PanelService" class _PanelClient: def __init__( self, *, - service_class: str | None = None, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, grpc_channel: grpc.Channel | None = None, ) -> None: self._initialization_lock = threading.Lock() - self._service_class = service_class self._discovery_client = discovery_client self._grpc_channel_pool = grpc_channel_pool self._grpc_channel = grpc_channel @@ -98,7 +96,7 @@ def _get_stub(self) -> PanelServiceStub: if self._stub is None: if self._grpc_channel is not None: self._stub = PanelServiceStub(self._grpc_channel) - elif self._service_class is not None: + else: with self._initialization_lock: if self._grpc_channel_pool is None: _logger.debug("Creating unshared GrpcChannelPool.") @@ -110,13 +108,10 @@ def _get_stub(self) -> PanelServiceStub: ) service_location = self._discovery_client.resolve_service( - provided_interface=PANEL_INTERFACE, - service_class=self._service_class, + provided_interface=PANEL_SERVICE ) channel = self._grpc_channel_pool.get_channel(service_location.insecure_address) self._stub = PanelServiceStub(channel) - else: - raise TypeError("Either grpc_channel or service_class must be provided.") return self._stub def _invoke_with_retry( diff --git a/src/nipanel/_panel_value_accessor.py b/src/nipanel/_panel_value_accessor.py index 42396cea..4f0f0803 100644 --- a/src/nipanel/_panel_value_accessor.py +++ b/src/nipanel/_panel_value_accessor.py @@ -29,7 +29,6 @@ def __init__( self, *, panel_id: str, - service_class: str | None = None, notify_on_set_value: bool = True, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, @@ -37,7 +36,6 @@ def __init__( ) -> None: """Initialize the accessor.""" self._panel_client = _PanelClient( - service_class=service_class, discovery_client=discovery_client, grpc_channel_pool=grpc_channel_pool, grpc_channel=grpc_channel, diff --git a/src/nipanel/_streamlit_constants.py b/src/nipanel/_streamlit_constants.py index cefeea35..b5e8badd 100644 --- a/src/nipanel/_streamlit_constants.py +++ b/src/nipanel/_streamlit_constants.py @@ -1,2 +1 @@ -STREAMLIT_PYTHON_PANEL_SERVICE = "ni.panels.v1.PanelService" STREAMLIT_REFRESH_COMPONENT_URL = "http://localhost:42001/panel-service/refresh" diff --git a/src/nipanel/_streamlit_panel.py b/src/nipanel/_streamlit_panel.py index 7076a36b..093b86d0 100644 --- a/src/nipanel/_streamlit_panel.py +++ b/src/nipanel/_streamlit_panel.py @@ -9,7 +9,6 @@ from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool from nipanel._panel_value_accessor import PanelValueAccessor -from nipanel._streamlit_constants import STREAMLIT_PYTHON_PANEL_SERVICE @final @@ -41,7 +40,6 @@ def __init__( """ super().__init__( panel_id=panel_id, - service_class=STREAMLIT_PYTHON_PANEL_SERVICE, discovery_client=discovery_client, grpc_channel_pool=grpc_channel_pool, grpc_channel=grpc_channel, diff --git a/src/nipanel/_streamlit_panel_initializer.py b/src/nipanel/_streamlit_panel_initializer.py index ff24bc5d..88f7f494 100644 --- a/src/nipanel/_streamlit_panel_initializer.py +++ b/src/nipanel/_streamlit_panel_initializer.py @@ -5,7 +5,6 @@ from nipanel._convert import is_supported_type from nipanel._panel_value_accessor import PanelValueAccessor -from nipanel._streamlit_constants import STREAMLIT_PYTHON_PANEL_SERVICE from nipanel._streamlit_panel import StreamlitPanel from nipanel.streamlit_refresh import initialize_refresh_component @@ -84,7 +83,6 @@ def _initialize_panel_from_base_path() -> PanelValueAccessor: return PanelValueAccessor( panel_id=panel_id, notify_on_set_value=False, - service_class=STREAMLIT_PYTHON_PANEL_SERVICE, ) From 88c625295cbc9a485330ba6996ca757f07697ccd Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Fri, 18 Jul 2025 15:45:28 -0500 Subject: [PATCH 8/9] cleanup --- src/nipanel/_streamlit_panel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nipanel/_streamlit_panel.py b/src/nipanel/_streamlit_panel.py index 093b86d0..2d258fa7 100644 --- a/src/nipanel/_streamlit_panel.py +++ b/src/nipanel/_streamlit_panel.py @@ -52,12 +52,12 @@ def __init__( @property def panel_script_path(self) -> str: - """Read-only accessor for the panel file path.""" + """Read-only accessor for the streamlit script file path.""" return self._panel_script_path @property def panel_url(self) -> str: - """Read-only accessor for the panel URL.""" + """Read-only accessor for the panel's streamlit webpage URL.""" return self._panel_url def _get_python_path(self) -> str: From ea3409e18bb454f211bd527104b290ada388c45c Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Fri, 18 Jul 2025 15:58:16 -0500 Subject: [PATCH 9/9] copilot feedback --- src/nipanel/_streamlit_panel_initializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nipanel/_streamlit_panel_initializer.py b/src/nipanel/_streamlit_panel_initializer.py index 88f7f494..40eb35d5 100644 --- a/src/nipanel/_streamlit_panel_initializer.py +++ b/src/nipanel/_streamlit_panel_initializer.py @@ -40,7 +40,7 @@ def create_streamlit_panel(streamlit_script_path: Path, panel_id: str = "") -> S "The provided script path must be a valid Streamlit script ending with '.py'." ) - if panel_id == "": + if not panel_id: panel_id = streamlit_script_path.stem path_str = str(streamlit_script_path)