diff --git a/examples/placeholder.py b/examples/placeholder.py index 53ad9a3..aa1c745 100644 --- a/examples/placeholder.py +++ b/examples/placeholder.py @@ -32,7 +32,7 @@ class MyStrEnum(str, enum.Enum): if __name__ == "__main__": my_panel = nipanel.StreamlitPanel( panel_id="placeholder", - streamlit_script_uri=__file__, + streamlit_script_path=__file__, ) my_types = { diff --git a/examples/sample/sample.py b/examples/sample/sample.py index fb5d9f0..09977e4 100644 --- a/examples/sample/sample.py +++ b/examples/sample/sample.py @@ -9,15 +9,12 @@ panel = nipanel.StreamlitPanel( panel_id="sample_panel", - streamlit_script_uri=panel_script_path, + streamlit_script_path=panel_script_path, ) -panel.open_panel() panel.set_value("sample_string", "Hello, World!") panel.set_value("sample_int", 42) panel.set_value("sample_float", 3.14) panel.set_value("sample_bool", True) panel.set_value("float_values", [1.1, 2.2, 3.3]) -input("Press Enter to close the panel...") - -panel.close_panel(reset=True) +print(f"Panel URL: {panel.panel_url}") diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/pythonpanel/v1/python_panel_service.proto index b2c4d03..70cc9a5 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/pythonpanel/v1/python_panel_service.proto @@ -16,15 +16,20 @@ option ruby_package = "NI::PythonPanel::V1"; // Service interface for interacting with python panels service PythonPanelService { - // Enumerate the panels available in the system, including information about the state of the panels and what values they have. + // Start a panel (or connect to if it has already been started) // Status Codes for errors: - rpc EnumeratePanels(EnumeratePanelsRequest) returns (EnumeratePanelsResponse); + // - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - NOT_FOUND: the file for the panel was not found + rpc StartPanel(StartPanelRequest) returns (StartPanelResponse); - // Open a panel + // Stop a panel // Status Codes for errors: // - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - // - NOT_FOUND: the file for the panel was not found - rpc OpenPanel(OpenPanelRequest) returns (OpenPanelResponse); + rpc StopPanel(StopPanelRequest) returns (StopPanelResponse); + + // Enumerate the panels available in the system, including information about the state of the panels and what values they have. + // Status Codes for errors: + rpc EnumeratePanels(EnumeratePanelsRequest) returns (EnumeratePanelsResponse); // Get a value for a control on the panel // Status Codes for errors: @@ -36,41 +41,49 @@ service PythonPanelService { // Status Codes for errors: // - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. rpc SetValue(SetValueRequest) returns (SetValueResponse); +} - // Close a panel - // Status Codes for errors: - // - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - rpc ClosePanel(ClosePanelRequest) returns (ClosePanelResponse); +message StartPanelRequest { + // 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 + string panel_script_path = 2; } -message EnumeratePanelsRequest { +message StartPanelResponse { + // Location of the panel's webpage + string panel_url = 1; } -message PanelInformation { +message StopPanelRequest { // Unique ID of the panel string panel_id = 1; - // Is the panel currently open? - bool is_open = 2; + // Reset all storage associated with panel + bool reset = 2; +} - // IDs of all of the values associated with the panel - repeated string value_ids = 3; +message StopPanelResponse { } -message EnumeratePanelsResponse { - // The list of panels available in the system - repeated PanelInformation panels = 1; +message EnumeratePanelsRequest { } -message OpenPanelRequest { +message PanelInformation { // Unique ID of the panel string panel_id = 1; - // Absolute path of the panel's file on disk, or network path to the file - string panel_uri = 2; + // Location of the panel's webpage + string panel_url = 2; + + // IDs of all of the values associated with the panel + repeated string value_ids = 3; } -message OpenPanelResponse { +message EnumeratePanelsResponse { + // The list of panels available in the system + repeated PanelInformation panels = 1; } message GetValueRequest { @@ -98,15 +111,4 @@ message SetValueRequest { } message SetValueResponse { -} - -message ClosePanelRequest { - // Unique ID of the panel - string panel_id = 1; - - // Reset all storage associated with panel - bool reset = 2; -} - -message ClosePanelResponse { } \ No newline at end of file diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.py b/src/ni/pythonpanel/v1/python_panel_service_pb2.py index dc05d26..d6f5c8c 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\"\x18\n\x16\x45numeratePanelsRequest\"H\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x0f\n\x07is_open\x18\x02 \x01(\x08\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\"7\n\x10OpenPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_uri\x18\x02 \x01(\t\"\x13\n\x11OpenPanelResponse\"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\"Z\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\n\x10SetValueResponse\"4\n\x11\x43losePanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x14\n\x12\x43losePanelResponse2\xdb\x03\n\x12PythonPanelService\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12V\n\tOpenPanel\x12#.ni.pythonpanel.v1.OpenPanelRequest\x1a$.ni.pythonpanel.v1.OpenPanelResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponse\x12Y\n\nClosePanel\x12$.ni.pythonpanel.v1.ClosePanelRequest\x1a%.ni.pythonpanel.v1.ClosePanelResponseB\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\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x19\n\x11panel_script_path\x18\x02 \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\"Z\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\n\x10SetValueResponse2\xdb\x03\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\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,28 +22,28 @@ 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' - _ENUMERATEPANELSREQUEST._serialized_start=94 - _ENUMERATEPANELSREQUEST._serialized_end=118 - _PANELINFORMATION._serialized_start=120 - _PANELINFORMATION._serialized_end=192 - _ENUMERATEPANELSRESPONSE._serialized_start=194 - _ENUMERATEPANELSRESPONSE._serialized_end=272 - _OPENPANELREQUEST._serialized_start=274 - _OPENPANELREQUEST._serialized_end=329 - _OPENPANELRESPONSE._serialized_start=331 - _OPENPANELRESPONSE._serialized_end=350 - _GETVALUEREQUEST._serialized_start=352 - _GETVALUEREQUEST._serialized_end=405 - _GETVALUERESPONSE._serialized_start=407 - _GETVALUERESPONSE._serialized_end=462 - _SETVALUEREQUEST._serialized_start=464 - _SETVALUEREQUEST._serialized_end=554 - _SETVALUERESPONSE._serialized_start=556 - _SETVALUERESPONSE._serialized_end=574 - _CLOSEPANELREQUEST._serialized_start=576 - _CLOSEPANELREQUEST._serialized_end=628 - _CLOSEPANELRESPONSE._serialized_start=630 - _CLOSEPANELRESPONSE._serialized_end=650 - _PYTHONPANELSERVICE._serialized_start=653 - _PYTHONPANELSERVICE._serialized_end=1128 + _STARTPANELREQUEST._serialized_start=94 + _STARTPANELREQUEST._serialized_end=158 + _STARTPANELRESPONSE._serialized_start=160 + _STARTPANELRESPONSE._serialized_end=199 + _STOPPANELREQUEST._serialized_start=201 + _STOPPANELREQUEST._serialized_end=252 + _STOPPANELRESPONSE._serialized_start=254 + _STOPPANELRESPONSE._serialized_end=273 + _ENUMERATEPANELSREQUEST._serialized_start=275 + _ENUMERATEPANELSREQUEST._serialized_end=299 + _PANELINFORMATION._serialized_start=301 + _PANELINFORMATION._serialized_end=375 + _ENUMERATEPANELSRESPONSE._serialized_start=377 + _ENUMERATEPANELSRESPONSE._serialized_end=455 + _GETVALUEREQUEST._serialized_start=457 + _GETVALUEREQUEST._serialized_end=510 + _GETVALUERESPONSE._serialized_start=512 + _GETVALUERESPONSE._serialized_end=567 + _SETVALUEREQUEST._serialized_start=569 + _SETVALUEREQUEST._serialized_end=659 + _SETVALUERESPONSE._serialized_start=661 + _SETVALUERESPONSE._serialized_end=679 + _PYTHONPANELSERVICE._serialized_start=682 + _PYTHONPANELSERVICE._serialized_end=1157 # @@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 74a5036..135cbf7 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi @@ -14,88 +14,124 @@ import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final -class EnumeratePanelsRequest(google.protobuf.message.Message): +class StartPanelRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + PANEL_ID_FIELD_NUMBER: builtins.int + PANEL_SCRIPT_PATH_FIELD_NUMBER: builtins.int + 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""" def __init__( self, + *, + panel_id: builtins.str = ..., + panel_script_path: builtins.str = ..., ) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_script_path", b"panel_script_path"]) -> None: ... -global___EnumeratePanelsRequest = EnumeratePanelsRequest +global___StartPanelRequest = StartPanelRequest @typing.final -class PanelInformation(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""" + def __init__( + self, + *, + panel_url: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_url", b"panel_url"]) -> None: ... + +global___StartPanelResponse = StartPanelResponse + +@typing.final +class StopPanelRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PANEL_ID_FIELD_NUMBER: builtins.int - IS_OPEN_FIELD_NUMBER: builtins.int - VALUE_IDS_FIELD_NUMBER: builtins.int + RESET_FIELD_NUMBER: builtins.int panel_id: builtins.str """Unique ID of the panel""" - is_open: builtins.bool - """Is the panel currently open?""" - @property - def value_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - """IDs of all of the values associated with the panel""" - + reset: builtins.bool + """Reset all storage associated with panel""" def __init__( self, *, panel_id: builtins.str = ..., - is_open: builtins.bool = ..., - value_ids: collections.abc.Iterable[builtins.str] | None = ..., + reset: builtins.bool = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["is_open", b"is_open", "panel_id", b"panel_id", "value_ids", b"value_ids"]) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "reset", b"reset"]) -> None: ... -global___PanelInformation = PanelInformation +global___StopPanelRequest = StopPanelRequest @typing.final -class EnumeratePanelsResponse(google.protobuf.message.Message): +class StopPanelResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PANELS_FIELD_NUMBER: builtins.int - @property - def panels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PanelInformation]: - """The list of panels available in the system""" + def __init__( + self, + ) -> None: ... + +global___StopPanelResponse = StopPanelResponse + +@typing.final +class EnumeratePanelsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__( self, - *, - panels: collections.abc.Iterable[global___PanelInformation] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["panels", b"panels"]) -> None: ... -global___EnumeratePanelsResponse = EnumeratePanelsResponse +global___EnumeratePanelsRequest = EnumeratePanelsRequest @typing.final -class OpenPanelRequest(google.protobuf.message.Message): +class PanelInformation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PANEL_ID_FIELD_NUMBER: builtins.int - PANEL_URI_FIELD_NUMBER: builtins.int + PANEL_URL_FIELD_NUMBER: builtins.int + VALUE_IDS_FIELD_NUMBER: builtins.int panel_id: builtins.str """Unique ID of the panel""" - panel_uri: builtins.str - """Absolute path of the panel's file on disk, or network path to the file""" + panel_url: builtins.str + """Location of the panel's webpage""" + @property + def value_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """IDs of all of the values associated with the panel""" + def __init__( self, *, panel_id: builtins.str = ..., - panel_uri: builtins.str = ..., + panel_url: builtins.str = ..., + value_ids: collections.abc.Iterable[builtins.str] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_uri", b"panel_uri"]) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_url", b"panel_url", "value_ids", b"value_ids"]) -> None: ... -global___OpenPanelRequest = OpenPanelRequest +global___PanelInformation = PanelInformation @typing.final -class OpenPanelResponse(google.protobuf.message.Message): +class EnumeratePanelsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + PANELS_FIELD_NUMBER: builtins.int + @property + def panels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PanelInformation]: + """The list of panels available in the system""" + def __init__( self, + *, + panels: collections.abc.Iterable[global___PanelInformation] | None = ..., ) -> None: ... + def ClearField(self, field_name: typing.Literal["panels", b"panels"]) -> None: ... -global___OpenPanelResponse = OpenPanelResponse +global___EnumeratePanelsResponse = EnumeratePanelsResponse @typing.final class GetValueRequest(google.protobuf.message.Message): @@ -172,33 +208,3 @@ class SetValueResponse(google.protobuf.message.Message): ) -> None: ... global___SetValueResponse = SetValueResponse - -@typing.final -class ClosePanelRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - PANEL_ID_FIELD_NUMBER: builtins.int - RESET_FIELD_NUMBER: builtins.int - panel_id: builtins.str - """Unique ID of the panel""" - reset: builtins.bool - """Reset all storage associated with panel""" - def __init__( - self, - *, - panel_id: builtins.str = ..., - reset: builtins.bool = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "reset", b"reset"]) -> None: ... - -global___ClosePanelRequest = ClosePanelRequest - -@typing.final -class ClosePanelResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - -global___ClosePanelResponse = ClosePanelResponse 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 69689d4..0f5c31b 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py @@ -15,16 +15,21 @@ 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.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, + ) 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, ) - self.OpenPanel = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/OpenPanel', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.OpenPanelRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.OpenPanelResponse.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, @@ -35,56 +40,51 @@ def __init__(self, channel): 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, ) - self.ClosePanel = channel.unary_unary( - '/ni.pythonpanel.v1.PythonPanelService/ClosePanel', - request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.ClosePanelRequest.SerializeToString, - response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.ClosePanelResponse.FromString, - ) class PythonPanelServiceServicer(object): """Service interface for interacting with python panels """ - def EnumeratePanels(self, request, context): - """Enumerate the panels available in the system, including information about the state of the panels and what values they have. + def StartPanel(self, request, context): + """Start a panel (or connect to if it has already been started) Status Codes for errors: + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: the file for the panel was not found """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def OpenPanel(self, request, context): - """Open a panel + def StopPanel(self, request, context): + """Stop a panel Status Codes for errors: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - - NOT_FOUND: the file for the panel was not found """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def GetValue(self, request, context): - """Get a value for a control on the panel + def EnumeratePanels(self, request, context): + """Enumerate the panels available in the system, including information about the state of the panels and what values they have. Status Codes for errors: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - - NOT_FOUND: The value with the specified identifier was not found """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def SetValue(self, request, context): - """Set a value for a control on the panel + def GetValue(self, request, context): + """Get a value for a control on the panel Status Codes for errors: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: The value with the specified identifier was not found """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def ClosePanel(self, request, context): - """Close a panel + def SetValue(self, request, context): + """Set a value for a control on the panel Status Codes for errors: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ @@ -95,16 +95,21 @@ def ClosePanel(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, + ), + '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, + ), '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, ), - 'OpenPanel': grpc.unary_unary_rpc_method_handler( - servicer.OpenPanel, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.OpenPanelRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.OpenPanelResponse.SerializeToString, - ), 'GetValue': grpc.unary_unary_rpc_method_handler( servicer.GetValue, request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueRequest.FromString, @@ -115,11 +120,6 @@ def add_PythonPanelServiceServicer_to_server(servicer, server): 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, ), - 'ClosePanel': grpc.unary_unary_rpc_method_handler( - servicer.ClosePanel, - request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.ClosePanelRequest.FromString, - response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.ClosePanelResponse.SerializeToString, - ), } generic_handler = grpc.method_handlers_generic_handler( 'ni.pythonpanel.v1.PythonPanelService', rpc_method_handlers) @@ -132,7 +132,7 @@ class PythonPanelService(object): """ @staticmethod - def EnumeratePanels(request, + def StartPanel(request, target, options=(), channel_credentials=None, @@ -142,14 +142,14 @@ 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.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) @staticmethod - def OpenPanel(request, + def StopPanel(request, target, options=(), channel_credentials=None, @@ -159,14 +159,14 @@ def OpenPanel(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/OpenPanel', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.OpenPanelRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.OpenPanelResponse.FromString, + 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, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def GetValue(request, + def EnumeratePanels(request, target, options=(), channel_credentials=None, @@ -176,14 +176,14 @@ 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.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, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def SetValue(request, + def GetValue(request, target, options=(), channel_credentials=None, @@ -193,14 +193,14 @@ 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.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, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def ClosePanel(request, + def SetValue(request, target, options=(), channel_credentials=None, @@ -210,8 +210,8 @@ def ClosePanel(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/ni.pythonpanel.v1.PythonPanelService/ClosePanel', - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.ClosePanelRequest.SerializeToString, - ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.ClosePanelResponse.FromString, + 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, 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 98fb91b..b2d5fa6 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi @@ -21,22 +21,31 @@ class PythonPanelServiceStub: """Service interface for interacting with python panels""" def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ... - EnumeratePanels: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, - ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse, + StartPanel: grpc.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StartPanelResponse, ] - """Enumerate the panels available in the system, including information about the state of the panels and what values they have. + """Start a panel (or connect to if it has already been started) Status Codes for errors: + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: the file for the panel was not found """ - OpenPanel: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelResponse, + StopPanel: grpc.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StopPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse, ] - """Open a panel + """Stop a panel Status Codes for errors: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - - NOT_FOUND: the file for the panel was not found + """ + + EnumeratePanels: grpc.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, + ni.pythonpanel.v1.python_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[ @@ -58,17 +67,27 @@ class PythonPanelServiceStub: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ - ClosePanel: grpc.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelResponse, +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, ] - """Close a panel + """Start a panel (or connect to if it has already been started) Status Codes for errors: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: the file for the panel was not found """ -class PythonPanelServiceAsyncStub: - """Service interface for interacting with python panels""" + StopPanel: grpc.aio.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.StopPanelRequest, + ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse, + ] + """Stop a panel + Status Codes for errors: + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + """ EnumeratePanels: grpc.aio.UnaryUnaryMultiCallable[ ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, @@ -78,16 +97,6 @@ class PythonPanelServiceAsyncStub: Status Codes for errors: """ - OpenPanel: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelResponse, - ] - """Open a panel - Status Codes for errors: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - - NOT_FOUND: the file for the panel was not found - """ - GetValue: grpc.aio.UnaryUnaryMultiCallable[ ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest, ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse, @@ -107,38 +116,40 @@ class PythonPanelServiceAsyncStub: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ - ClosePanel: grpc.aio.UnaryUnaryMultiCallable[ - ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelRequest, - ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelResponse, - ] - """Close a panel - Status Codes for errors: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - """ - class PythonPanelServiceServicer(metaclass=abc.ABCMeta): """Service interface for interacting with python panels""" @abc.abstractmethod - def EnumeratePanels( + def StartPanel( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, + request: ni.pythonpanel.v1.python_panel_service_pb2.StartPanelRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse]]: - """Enumerate the panels available in the system, including information about the state of the panels and what values they have. + ) -> 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) Status Codes for errors: + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: the file for the panel was not found """ @abc.abstractmethod - def OpenPanel( + def StopPanel( self, - request: ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelRequest, + request: ni.pythonpanel.v1.python_panel_service_pb2.StopPanelRequest, context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelResponse]]: - """Open a panel + ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.StopPanelResponse]]: + """Stop a panel Status Codes for errors: - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. - - NOT_FOUND: the file for the panel was not found + """ + + @abc.abstractmethod + def EnumeratePanels( + self, + request: ni.pythonpanel.v1.python_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]]: + """Enumerate the panels available in the system, including information about the state of the panels and what values they have. + Status Codes for errors: """ @abc.abstractmethod @@ -164,15 +175,4 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ - @abc.abstractmethod - def ClosePanel( - self, - request: ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelRequest, - context: _ServicerContext, - ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.ClosePanelResponse]]: - """Close a panel - Status Codes for errors: - - INVALID_ARGUMENT: The specified 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: ... diff --git a/src/nipanel/_panel.py b/src/nipanel/_panel.py index fc2c5f2..0419f84 100644 --- a/src/nipanel/_panel.py +++ b/src/nipanel/_panel.py @@ -1,12 +1,10 @@ from __future__ import annotations from abc import ABC -from types import TracebackType import grpc from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool -from typing_extensions import Self from nipanel._panel_value_accessor import PanelValueAccessor @@ -14,13 +12,13 @@ class Panel(PanelValueAccessor, ABC): """This class allows you to open a panel and specify values for its controls.""" - __slots__ = ["_panel_uri"] + __slots__ = ["_panel_script_path", "_panel_url"] def __init__( self, *, panel_id: str, - panel_uri: str, + panel_script_path: str, provided_interface: str, service_class: str, discovery_client: DiscoveryClient | None = None, @@ -36,36 +34,15 @@ def __init__( grpc_channel_pool=grpc_channel_pool, grpc_channel=grpc_channel, ) - self._panel_uri = panel_uri + self._panel_script_path = panel_script_path + self._panel_url = self._panel_client.start_panel(panel_id, panel_script_path) @property - def panel_uri(self) -> str: - """Read-only accessor for the panel URI.""" - return self._panel_uri + def panel_script_path(self) -> str: + """Read-only accessor for the panel script path.""" + return self._panel_script_path - def __enter__(self) -> Self: - """Enter the runtime context related to this object.""" - self.open_panel() - return self - - def __exit__( - self, - exctype: type[BaseException] | None, - excinst: BaseException | None, - exctb: TracebackType | None, - ) -> bool | None: - """Exit the runtime context related to this object.""" - self.close_panel(reset=False) - return None - - def open_panel(self) -> None: - """Open the panel.""" - self._panel_client.open_panel(self._panel_id, self._panel_uri) - - def close_panel(self, reset: bool) -> None: - """Close the panel. - - Args: - reset: Whether to reset all storage associated with the panel. - """ - self._panel_client.close_panel(self._panel_id, reset=reset) + @property + def panel_url(self) -> str: + """Read-only accessor for the panel URL.""" + return self._panel_url diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 715f12c..993711a 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -8,8 +8,8 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - OpenPanelRequest, - ClosePanelRequest, + StartPanelRequest, + StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, SetValueRequest, @@ -59,38 +59,46 @@ def __init__( self._grpc_channel = grpc_channel self._stub: PythonPanelServiceStub | None = None - def open_panel(self, panel_id: str, panel_uri: str) -> None: - """Open the panel. + def start_panel(self, panel_id: str, panel_script_path: str) -> str: + """Start the panel. Args: - panel_id: The ID of the panel to open. - panel_uri: The URI of the panel script. + panel_id: The ID of the panel to start. + panel_script_path: The path of the panel script file. + + Returns: + The URL of the panel. """ - open_panel_request = OpenPanelRequest(panel_id=panel_id, panel_uri=panel_uri) - self._invoke_with_retry(self._get_stub().OpenPanel, open_panel_request) + start_panel_request = StartPanelRequest( + panel_id=panel_id, panel_script_path=panel_script_path + ) + response = self._invoke_with_retry(self._get_stub().StartPanel, start_panel_request) + return response.panel_url - def close_panel(self, panel_id: str, reset: bool) -> None: - """Close the panel. + def stop_panel(self, panel_id: str, reset: bool) -> None: + """Stop the panel. Args: - panel_id: The ID of the panel to close. + panel_id: The ID of the panel to stop. reset: Whether to reset all storage associated with panel. """ - close_panel_request = ClosePanelRequest(panel_id=panel_id, reset=reset) - self._invoke_with_retry(self._get_stub().ClosePanel, close_panel_request) + stop_panel_request = StopPanelRequest(panel_id=panel_id, reset=reset) + self._invoke_with_retry(self._get_stub().StopPanel, stop_panel_request) - def enumerate_panels(self) -> dict[str, tuple[bool, list[str]]]: + def enumerate_panels(self) -> dict[str, tuple[str, list[str]]]: """Enumerate all available panels. Returns: - A dictionary mapping panel IDs to a tuple containing a boolean indicating if the panel - is open and a list of value IDs associated with the panel. + A dictionary mapping panel IDs to a tuple containing the url of the panel (if it is + running) and a list of value IDs associated with the panel. """ enumerate_panels_request = EnumeratePanelsRequest() response = self._invoke_with_retry( self._get_stub().EnumeratePanels, enumerate_panels_request ) - return {panel.panel_id: (panel.is_open, list(panel.value_ids)) for panel in response.panels} + return { + panel.panel_id: (panel.panel_url, list(panel.value_ids)) for panel in response.panels + } def set_value(self, panel_id: str, value_id: str, value: object) -> None: """Set the value for the control with value_id. diff --git a/src/nipanel/_streamlit_panel.py b/src/nipanel/_streamlit_panel.py index 20d9628..16607cb 100644 --- a/src/nipanel/_streamlit_panel.py +++ b/src/nipanel/_streamlit_panel.py @@ -17,7 +17,7 @@ class StreamlitPanel(Panel): def __init__( self, panel_id: str, - streamlit_script_uri: str, + streamlit_script_path: str, *, discovery_client: DiscoveryClient | None = None, grpc_channel_pool: GrpcChannelPool | None = None, @@ -27,7 +27,7 @@ def __init__( Args: panel_id: A unique identifier for the panel. - streamlit_script_uri: The file path of the Streamlit script. + streamlit_script_path: The file path of the Streamlit script. grpc_channel: An optional gRPC channel to use for communication with the panel service. Returns: @@ -35,7 +35,7 @@ def __init__( """ super().__init__( panel_id=panel_id, - panel_uri=streamlit_script_uri, + panel_script_path=streamlit_script_path, provided_interface=STREAMLIT_PYTHON_PANEL_SERVICE, service_class=STREAMLIT_PYTHON_PANEL_SERVICE, discovery_client=discovery_client, diff --git a/tests/unit/test_panel_client.py b/tests/unit/test_panel_client.py index 4b7d2d9..ce59d67 100644 --- a/tests/unit/test_panel_client.py +++ b/tests/unit/test_panel_client.py @@ -10,44 +10,44 @@ def test___enumerate_is_empty(fake_panel_channel: grpc.Channel) -> None: assert client.enumerate_panels() == {} -def test___open_panels___enumerate_has_panels(fake_panel_channel: grpc.Channel) -> None: +def test___start_panels___enumerate_has_panels(fake_panel_channel: grpc.Channel) -> None: client = create_panel_client(fake_panel_channel) - client.open_panel("panel1", "uri1") - client.open_panel("panel2", "uri2") + client.start_panel("panel1", "uri1") + client.start_panel("panel2", "uri2") assert client.enumerate_panels() == { - "panel1": (True, []), - "panel2": (True, []), + "panel1": ("http://localhost:50051/panel1", []), + "panel2": ("http://localhost:50051/panel2", []), } -def test___open_panels___close_panel_1_with_reset___enumerate_has_panel_2( +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.open_panel("panel1", "uri1") - client.open_panel("panel2", "uri2") + client.start_panel("panel1", "uri1") + client.start_panel("panel2", "uri2") - client.close_panel("panel1", reset=True) + client.stop_panel("panel1", reset=True) assert client.enumerate_panels() == { - "panel2": (True, []), + "panel2": ("http://localhost:50051/panel2", []), } -def test___open_panels___close_panel_1_without_reset___enumerate_has_both_panels( +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.open_panel("panel1", "uri1") - client.open_panel("panel2", "uri2") + client.start_panel("panel1", "uri1") + client.start_panel("panel2", "uri2") - client.close_panel("panel1", reset=False) + client.stop_panel("panel1", reset=False) assert client.enumerate_panels() == { - "panel1": (False, []), - "panel2": (True, []), + "panel1": ("", []), + "panel2": ("http://localhost:50051/panel2", []), } @@ -65,7 +65,7 @@ def test___set_value___enumerate_panels_shows_value( client.set_value("panel1", "val1", "value1") - assert client.enumerate_panels() == {"panel1": (False, ["val1"])} + assert client.enumerate_panels() == {"panel1": ("", ["val1"])} def test___set_value___gets_value(fake_panel_channel: grpc.Channel) -> None: diff --git a/tests/unit/test_python_panel_service_stub.py b/tests/unit/test_python_panel_service_stub.py index ffcab27..f6f4276 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -1,8 +1,8 @@ from google.protobuf.any_pb2 import Any from google.protobuf.wrappers_pb2 import StringValue from ni.pythonpanel.v1.python_panel_service_pb2 import ( - OpenPanelRequest, - ClosePanelRequest, + StartPanelRequest, + StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, SetValueRequest, @@ -10,21 +10,21 @@ from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub -def test___open_panel___gets_response(python_panel_service_stub: PythonPanelServiceStub) -> None: - request = OpenPanelRequest(panel_id="test_panel", panel_uri="path/to/panel") - response = python_panel_service_stub.OpenPanel(request) +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) - assert response is not None # Ensure response is returned + assert response.panel_url == "http://localhost:50051/test_panel" -def test___open_panel___close_panel___gets_response( +def test___start_panel___stop_panel___gets_response( python_panel_service_stub: PythonPanelServiceStub, ) -> None: - open_request = OpenPanelRequest(panel_id="test_panel", panel_uri="path/to/panel") - python_panel_service_stub.OpenPanel(open_request) + open_request = StartPanelRequest(panel_id="test_panel", panel_script_path="path/to/panel") + python_panel_service_stub.StartPanel(open_request) - request = ClosePanelRequest(panel_id="test_panel", reset=False) - response = python_panel_service_stub.ClosePanel(request) + request = StopPanelRequest(panel_id="test_panel", reset=False) + response = python_panel_service_stub.StopPanel(request) assert response is not None # Ensure response is returned diff --git a/tests/unit/test_streamlit_panel.py b/tests/unit/test_streamlit_panel.py index 3590350..9adf971 100644 --- a/tests/unit/test_streamlit_panel.py +++ b/tests/unit/test_streamlit_panel.py @@ -6,28 +6,29 @@ from tests.utils._fake_python_panel_service import FakePythonPanelService -def test___panel___has_panel_id_and_panel_uri() -> None: - panel = StreamlitPanel("my_panel", "path/to/script") +def test___panel___has_panel_id_and_panel_script_path(fake_panel_channel: grpc.Channel) -> None: + panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) assert panel.panel_id == "my_panel" - assert panel.panel_uri == "path/to/script" + assert panel.panel_script_path == "path/to/script" -def test___different_panels___have_different_panel_ids_and_uris() -> None: - panel1 = StreamlitPanel("panel1", "path/to/script1") - panel2 = StreamlitPanel("panel2", "path/to/script2") +def test___different_panels___have_different_panel_ids_and_panel_script_paths( + fake_panel_channel: grpc.Channel, +) -> None: + panel1 = StreamlitPanel("panel1", "path/to/script1", grpc_channel=fake_panel_channel) + panel2 = StreamlitPanel("panel2", "path/to/script2", grpc_channel=fake_panel_channel) assert panel1.panel_id == "panel1" assert panel2.panel_id == "panel2" - assert panel1._panel_uri == "path/to/script1" - assert panel2._panel_uri == "path/to/script2" + assert panel1._panel_script_path == "path/to/script1" + assert panel2._panel_script_path == "path/to/script2" assert panel1._panel_client != panel2._panel_client -def test___opened_panel___set_value___gets_same_value( +def test___panel___set_value___gets_same_value( fake_panel_channel: grpc.Channel, ) -> None: panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - panel.open_panel() value_id = "test_id" string_value = "test_value" @@ -36,11 +37,10 @@ def test___opened_panel___set_value___gets_same_value( assert panel.get_value(value_id) == string_value -def test___opened_panel___panel_set_value___accessor_gets_same_value( +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) - panel.open_panel() accessor = StreamlitPanelValueAccessor("my_panel", grpc_channel=fake_panel_channel) value_id = "test_id" @@ -50,11 +50,10 @@ def test___opened_panel___panel_set_value___accessor_gets_same_value( assert accessor.get_value(value_id) == string_value -def test___opened_panel___accessor_set_value___panel_gets_same_value( +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) - panel.open_panel() accessor = StreamlitPanelValueAccessor("my_panel", grpc_channel=fake_panel_channel) value_id = "test_id" @@ -64,67 +63,41 @@ def test___opened_panel___accessor_set_value___panel_gets_same_value( assert panel.get_value(value_id) == string_value -def test___opened_panel_with_value___close_without_reset___gets_value( - fake_panel_channel: grpc.Channel, -) -> None: - panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - panel.open_panel() - value_id = "test_id" - string_value = "test_value" - panel.set_value(value_id, string_value) - - panel.close_panel(reset=False) - - assert panel.get_value(value_id) == string_value - - -def test___opened_panel_with_value___close_with_reset___get_throws( - fake_panel_channel: grpc.Channel, -) -> None: - panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - panel.open_panel() - value_id = "test_id" - string_value = "test_value" - panel.set_value(value_id, string_value) - - panel.close_panel(reset=True) - - with pytest.raises(grpc.RpcError): - panel.get_value(value_id) - - -def test___first_open_panel_fails___open_panel___gets_value( +def test___first_start_will_fail___start_panel___panel_is_functional( fake_python_panel_service: FakePythonPanelService, fake_panel_channel: grpc.Channel, ) -> None: - """Test that panel.open_panel() will automatically retry once.""" + """Test that panel.start_panel() will automatically retry once.""" service = fake_python_panel_service # Simulate a failure on the first attempt - service.servicer.fail_next_open_panel() - panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) + service.servicer.fail_next_start_panel() - panel.open_panel() + panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) value_id = "test_id" string_value = "test_value" panel.set_value(value_id, string_value) assert panel.get_value(value_id) == string_value + assert panel._panel_client.enumerate_panels() == { + "my_panel": ("http://localhost:50051/my_panel", [value_id]) + } -def test___unopened_panel___set_value___sets_value( +def test___panel___set_value___sets_value( fake_panel_channel: grpc.Channel, ) -> None: - """Test that set_value() succeeds before the user opens the panel.""" panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) value_id = "test_id" string_value = "test_value" panel.set_value(value_id, string_value) - assert panel._panel_client.enumerate_panels() == {"my_panel": (False, [value_id])} + assert panel._panel_client.enumerate_panels() == { + "my_panel": ("http://localhost:50051/my_panel", [value_id]) + } -def test___unopened_panel___get_unset_value___raises_exception( +def test___panel___get_unset_value___raises_exception( fake_panel_channel: grpc.Channel, ) -> None: """Test that get_value() raises an exception for an unset value.""" @@ -135,10 +108,9 @@ def test___unopened_panel___get_unset_value___raises_exception( panel.get_value(value_id) -def test___unopened_panel___get_set_value___gets_value( +def test___panel___set_value___gets_value( fake_panel_channel: grpc.Channel, ) -> None: - """Test that get_value() succeeds for a set value before the user opens the panel.""" panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) value_id = "test_id" @@ -238,7 +210,6 @@ def test___sequence_of_builtin_type___set_value___gets_same_value( fake_panel_channel: grpc.Channel, value_payload: object, ) -> None: - """Test that set_value() succeeds before the user opens the panel.""" panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) value_id = "test_id" @@ -248,45 +219,18 @@ def test___sequence_of_builtin_type___set_value___gets_same_value( assert list(received_value) == list(value_payload) # type: ignore [call-overload] -def test___open_panel___panel_is_open_and_in_memory( +def test___panel___panel_is_running_and_in_memory( fake_panel_channel: grpc.Channel, ) -> None: panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - assert not is_panel_in_memory(panel) - - panel.open_panel() assert is_panel_in_memory(panel) - assert is_panel_open(panel) - - -def test___with_panel___opens_and_closes_panel( - fake_panel_channel: grpc.Channel, -) -> None: - panel = StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) - - with panel: - assert is_panel_in_memory(panel) - assert is_panel_open(panel) - - assert is_panel_in_memory(panel) - assert not is_panel_open(panel) - - -def test___with_panel___set_value___gets_same_value( - fake_panel_channel: grpc.Channel, -) -> None: - with StreamlitPanel("my_panel", "path/to/script", grpc_channel=fake_panel_channel) as panel: - value_id = "test_id" - string_value = "test_value" - panel.set_value(value_id, string_value) - - assert panel.get_value(value_id) == string_value + assert is_panel_running(panel) def is_panel_in_memory(panel: StreamlitPanel) -> bool: return panel.panel_id in panel._panel_client.enumerate_panels().keys() -def is_panel_open(panel: StreamlitPanel) -> bool: - return panel._panel_client.enumerate_panels()[panel.panel_id][0] +def is_panel_running(panel: StreamlitPanel) -> bool: + return panel._panel_client.enumerate_panels()[panel.panel_id][0] != "" diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index 2f20f04..3397273 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -2,10 +2,10 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - OpenPanelRequest, - OpenPanelResponse, - ClosePanelRequest, - ClosePanelResponse, + StartPanelRequest, + StartPanelResponse, + StopPanelRequest, + StopPanelResponse, EnumeratePanelsRequest, EnumeratePanelsResponse, PanelInformation, @@ -23,24 +23,24 @@ class FakePythonPanelServicer(PythonPanelServiceServicer): def __init__(self) -> None: """Initialize the fake PythonPanelServicer.""" self._panel_ids: list[str] = [] - self._panel_is_open: dict[str, bool] = {} + self._panel_is_running: dict[str, bool] = {} self._panel_value_ids: dict[str, dict[str, Any]] = {} - self._fail_next_open_panel = False + self._fail_next_start_panel = False - def OpenPanel(self, request: OpenPanelRequest, context: Any) -> OpenPanelResponse: # noqa: N802 + def StartPanel( # noqa: N802 + self, request: StartPanelRequest, context: Any + ) -> StartPanelResponse: """Trivial implementation for testing.""" - if self._fail_next_open_panel: - self._fail_next_open_panel = False + if self._fail_next_start_panel: + self._fail_next_start_panel = False context.abort(grpc.StatusCode.UNAVAILABLE, "Simulated failure") - self._open_panel(request.panel_id) - return OpenPanelResponse() + self._start_panel(request.panel_id) + return StartPanelResponse(panel_url=self._get_panel_url(request.panel_id)) - def ClosePanel( # noqa: N802 - self, request: ClosePanelRequest, context: Any - ) -> ClosePanelResponse: + def StopPanel(self, request: StopPanelRequest, context: Any) -> StopPanelResponse: # noqa: N802 """Trivial implementation for testing.""" - self._close_panel(request.reset, request.panel_id) - return ClosePanelResponse() + self._stop_panel(request.reset, request.panel_id) + return StopPanelResponse() def EnumeratePanels( # noqa: N802 self, request: EnumeratePanelsRequest, context: Any @@ -50,7 +50,7 @@ def EnumeratePanels( # noqa: N802 for panel_id in self._panel_ids: panel = PanelInformation( panel_id=panel_id, - is_open=self._panel_is_open[panel_id], + panel_url=self._get_panel_url(panel_id), value_ids=self._panel_value_ids[panel_id], ) response.panels.append(panel) @@ -67,28 +67,33 @@ def SetValue(self, request: SetValueRequest, context: Any) -> SetValueResponse: self._panel_value_ids[request.panel_id][request.value_id] = request.value return SetValueResponse() - def fail_next_open_panel(self) -> None: - """Set whether the OpenPanel method should fail the next time it is called.""" - self._fail_next_open_panel = True + def fail_next_start_panel(self) -> None: + """Set whether the StartPanel method should fail the next time it is called.""" + self._fail_next_start_panel = True def _init_panel(self, panel_id: str) -> None: if panel_id not in self._panel_ids: self._panel_ids.append(panel_id) - self._panel_is_open[panel_id] = False + self._panel_is_running[panel_id] = False self._panel_value_ids[panel_id] = {} - def _open_panel(self, panel_id: str) -> None: + def _start_panel(self, panel_id: str) -> None: if panel_id not in self._panel_ids: self._panel_ids.append(panel_id) - self._panel_is_open[panel_id] = True + self._panel_is_running[panel_id] = True self._panel_value_ids[panel_id] = {} else: - self._panel_is_open[panel_id] = True + self._panel_is_running[panel_id] = True - def _close_panel(self, reset: bool, panel_id: str) -> None: + def _stop_panel(self, reset: bool, panel_id: str) -> None: if reset: self._panel_ids.remove(panel_id) - self._panel_is_open.pop(panel_id) + self._panel_is_running.pop(panel_id) self._panel_value_ids.pop(panel_id) else: - self._panel_is_open[panel_id] = False + self._panel_is_running[panel_id] = False + + def _get_panel_url(self, panel_id: str) -> str: + if not self._panel_is_running.get(panel_id, False): + return "" + return f"http://localhost:50051/{panel_id}"