diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/pythonpanel/v1/python_panel_service.proto index 0dd6073..d131296 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/pythonpanel/v1/python_panel_service.proto @@ -16,20 +16,39 @@ option ruby_package = "NI::PythonPanel::V1"; // Service interface for interacting with python panels service PythonPanelService { + // Enumerate the panels available in the system + // Status Codes for errors: + rpc EnumeratePanels(EnumeratePanelsRequest) returns (EnumeratePanelsResponse); + // 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 rpc OpenPanel(OpenPanelRequest) returns (OpenPanelResponse); // Get a value for a control on the panel // Status Codes for errors: - // - NOT_FOUND: the panel with the specified id was not found + // - 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 rpc GetValue(GetValueRequest) returns (GetValueResponse); // Set a value for a control on the panel // Status Codes for errors: - // - NOT_FOUND: the panel with the specified id was not found + // - 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 EnumeratePanelsRequest { +} + +message EnumeratePanelsResponse { + // The list of panels available in the system + repeated string panel_ids = 1; } message OpenPanelRequest { @@ -40,12 +59,12 @@ message OpenPanelRequest { string panel_uri = 2; } -message OpenPanelResponse { +message OpenPanelResponse { } message GetValueRequest { // Unique ID of the panel - string panel_id = 1; + string panel_id = 1; // Unique ID of value string value_id = 2; @@ -58,8 +77,8 @@ message GetValueResponse { message SetValueRequest { // Unique ID of the panel - string panel_id = 1; - + string panel_id = 1; + // Unique ID of the value string value_id = 2; @@ -67,5 +86,16 @@ message SetValueRequest { google.protobuf.Any value = 3; } -message SetValueResponse { +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 f2c1e9c..79fce9c 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\"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\x10SetValueResponse2\x96\x02\n\x12PythonPanelService\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.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\"\x18\n\x16\x45numeratePanelsRequest\",\n\x17\x45numeratePanelsResponse\x12\x11\n\tpanel_ids\x18\x01 \x03(\t\"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') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.pythonpanel.v1.python_panel_service_pb2', globals()) @@ -22,18 +22,26 @@ 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' - _OPENPANELREQUEST._serialized_start=94 - _OPENPANELREQUEST._serialized_end=149 - _OPENPANELRESPONSE._serialized_start=151 - _OPENPANELRESPONSE._serialized_end=170 - _GETVALUEREQUEST._serialized_start=172 - _GETVALUEREQUEST._serialized_end=225 - _GETVALUERESPONSE._serialized_start=227 - _GETVALUERESPONSE._serialized_end=282 - _SETVALUEREQUEST._serialized_start=284 - _SETVALUEREQUEST._serialized_end=374 - _SETVALUERESPONSE._serialized_start=376 - _SETVALUERESPONSE._serialized_end=394 - _PYTHONPANELSERVICE._serialized_start=397 - _PYTHONPANELSERVICE._serialized_end=675 + _ENUMERATEPANELSREQUEST._serialized_start=94 + _ENUMERATEPANELSREQUEST._serialized_end=118 + _ENUMERATEPANELSRESPONSE._serialized_start=120 + _ENUMERATEPANELSRESPONSE._serialized_end=164 + _OPENPANELREQUEST._serialized_start=166 + _OPENPANELREQUEST._serialized_end=221 + _OPENPANELRESPONSE._serialized_start=223 + _OPENPANELRESPONSE._serialized_end=242 + _GETVALUEREQUEST._serialized_start=244 + _GETVALUEREQUEST._serialized_end=297 + _GETVALUERESPONSE._serialized_start=299 + _GETVALUERESPONSE._serialized_end=354 + _SETVALUEREQUEST._serialized_start=356 + _SETVALUEREQUEST._serialized_end=446 + _SETVALUERESPONSE._serialized_start=448 + _SETVALUERESPONSE._serialized_end=466 + _CLOSEPANELREQUEST._serialized_start=468 + _CLOSEPANELREQUEST._serialized_end=520 + _CLOSEPANELRESPONSE._serialized_start=522 + _CLOSEPANELRESPONSE._serialized_end=542 + _PYTHONPANELSERVICE._serialized_start=545 + _PYTHONPANELSERVICE._serialized_end=1020 # @@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 989f29f..161d039 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi @@ -4,13 +4,43 @@ isort:skip_file """ import builtins +import collections.abc import google.protobuf.any_pb2 import google.protobuf.descriptor +import google.protobuf.internal.containers import google.protobuf.message import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final +class EnumeratePanelsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___EnumeratePanelsRequest = EnumeratePanelsRequest + +@typing.final +class EnumeratePanelsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PANEL_IDS_FIELD_NUMBER: builtins.int + @property + def panel_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """The list of panels available in the system""" + + def __init__( + self, + *, + panel_ids: collections.abc.Iterable[builtins.str] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_ids", b"panel_ids"]) -> None: ... + +global___EnumeratePanelsResponse = EnumeratePanelsResponse + @typing.final class OpenPanelRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -116,3 +146,33 @@ 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 4b04c3e..f3bfc2e 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py @@ -15,6 +15,11 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ + 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, @@ -30,15 +35,29 @@ 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 + Status Codes for errors: + """ + 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 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) @@ -48,7 +67,8 @@ def OpenPanel(self, request, context): def GetValue(self, request, context): """Get a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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!') @@ -57,7 +77,16 @@ def GetValue(self, request, context): def SetValue(self, request, context): """Set a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + """ + 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 + Status Codes for errors: + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -66,6 +95,11 @@ def SetValue(self, request, context): def add_PythonPanelServiceServicer_to_server(servicer, server): rpc_method_handlers = { + '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, @@ -81,6 +115,11 @@ 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) @@ -92,6 +131,23 @@ class PythonPanelService(object): """Service interface for interacting with python panels """ + @staticmethod + def EnumeratePanels(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + 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, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def OpenPanel(request, target, @@ -142,3 +198,20 @@ def SetValue(request, ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ClosePanel(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + 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, + 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 4b9a62a..d6a16b5 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi @@ -21,12 +21,21 @@ 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, + ] + """Enumerate the panels available in the system + Status Codes for errors: + """ + OpenPanel: grpc.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 """ @@ -36,7 +45,8 @@ class PythonPanelServiceStub: ] """Get a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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 """ SetValue: grpc.UnaryUnaryMultiCallable[ @@ -45,18 +55,36 @@ class PythonPanelServiceStub: ] """Set a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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, + ] + """Close a panel + Status Codes for errors: + - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ class PythonPanelServiceAsyncStub: """Service interface for interacting with python panels""" + EnumeratePanels: grpc.aio.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsRequest, + ni.pythonpanel.v1.python_panel_service_pb2.EnumeratePanelsResponse, + ] + """Enumerate the panels available in the system + 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 """ @@ -66,7 +94,8 @@ class PythonPanelServiceAsyncStub: ] """Get a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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 """ SetValue: grpc.aio.UnaryUnaryMultiCallable[ @@ -75,12 +104,31 @@ class PythonPanelServiceAsyncStub: ] """Set a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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( + 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 + Status Codes for errors: + """ + @abc.abstractmethod def OpenPanel( self, @@ -89,6 +137,7 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.OpenPanelResponse, collections.abc.Awaitable[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 """ @@ -100,7 +149,8 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse]]: """Get a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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 """ @abc.abstractmethod @@ -111,7 +161,18 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse]]: """Set a value for a control on the panel Status Codes for errors: - - NOT_FOUND: the panel with the specified id was not found + - 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 7c728cf..5620519 100644 --- a/src/nipanel/_panel.py +++ b/src/nipanel/_panel.py @@ -44,3 +44,11 @@ def panel_uri(self) -> str: 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) diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 82b21c4..48d8fa6 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -8,8 +8,10 @@ import grpc from ni.pythonpanel.v1.python_panel_service_pb2 import ( - GetValueRequest, OpenPanelRequest, + ClosePanelRequest, + EnumeratePanelsRequest, + GetValueRequest, SetValueRequest, ) from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub @@ -58,10 +60,37 @@ def __init__( self._stub: PythonPanelServiceStub | None = None def open_panel(self, panel_id: str, panel_uri: str) -> None: - """Open the panel.""" + """Open the panel. + + Args: + panel_id: The ID of the panel to open. + panel_uri: The URI of the panel script. + """ open_panel_request = OpenPanelRequest(panel_id=panel_id, panel_uri=panel_uri) self._invoke_with_retry(self._get_stub().OpenPanel, open_panel_request) + def close_panel(self, panel_id: str, reset: bool) -> None: + """Close the panel. + + Args: + panel_id: The ID of the panel to close. + 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) + + def enumerate_panels(self) -> list[str]: + """Enumerate all available panels. + + Returns: + A list of panel IDs. + """ + enumerate_panels_request = EnumeratePanelsRequest() + response = self._invoke_with_retry( + self._get_stub().EnumeratePanels, enumerate_panels_request + ) + return list(response.panel_ids) + 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/tests/unit/test_panel_client.py b/tests/unit/test_panel_client.py new file mode 100644 index 0000000..a6d00f1 --- /dev/null +++ b/tests/unit/test_panel_client.py @@ -0,0 +1,66 @@ +import grpc +import pytest + +from nipanel._panel_client import PanelClient + + +def test___enumerate_is_empty(fake_panel_channel: grpc.Channel) -> None: + client = create_panel_client(fake_panel_channel) + + assert client.enumerate_panels() == [] + + +def test___open_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") + + assert client.enumerate_panels() == ["panel1", "panel2"] + + +def test___open_panels___close_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.close_panel("panel1", reset=True) + + assert client.enumerate_panels() == ["panel2"] + + +def test___open_panels___close_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.close_panel("panel1", reset=False) + + assert client.enumerate_panels() == ["panel1", "panel2"] + + +def test___get_unset_value_raises_exception(fake_panel_channel: grpc.Channel) -> None: + client = create_panel_client(fake_panel_channel) + + with pytest.raises(Exception): + client.get_value("panel1", "unset_id") + + +def test___set_value___gets_value(fake_panel_channel: grpc.Channel) -> None: + client = create_panel_client(fake_panel_channel) + + client.set_value("panel1", "val1", "value1") + + assert client.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_python_panel_service_stub.py b/tests/unit/test_python_panel_service_stub.py index 3aaf911..ffcab27 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -2,6 +2,8 @@ from google.protobuf.wrappers_pb2 import StringValue from ni.pythonpanel.v1.python_panel_service_pb2 import ( OpenPanelRequest, + ClosePanelRequest, + EnumeratePanelsRequest, GetValueRequest, SetValueRequest, ) @@ -15,14 +17,25 @@ def test___open_panel___gets_response(python_panel_service_stub: PythonPanelServ assert response is not None # Ensure response is returned -def test___get_value___gets_response( +def test___open_panel___close_panel___gets_response( python_panel_service_stub: PythonPanelServiceStub, ) -> None: - request = GetValueRequest(panel_id="test_panel", value_id="test_value") - response = python_panel_service_stub.GetValue(request) + open_request = OpenPanelRequest(panel_id="test_panel", panel_uri="path/to/panel") + python_panel_service_stub.OpenPanel(open_request) + + request = ClosePanelRequest(panel_id="test_panel", reset=False) + response = python_panel_service_stub.ClosePanel(request) + + assert response is not None # Ensure response is returned + + +def test___enumerate_panels___gets_response( + python_panel_service_stub: PythonPanelServiceStub, +) -> None: + request = EnumeratePanelsRequest() + response = python_panel_service_stub.EnumeratePanels(request) assert response is not None # Ensure response is returned - assert isinstance(response.value, Any) # Ensure the value is of type `Any` def test___set_value___gets_response( @@ -34,3 +47,18 @@ def test___set_value___gets_response( response = python_panel_service_stub.SetValue(request) assert response is not None # Ensure response is returned + + +def test___set_value___get_value___gets_response( + python_panel_service_stub: PythonPanelServiceStub, +) -> None: + test_value = Any() + test_value.Pack(StringValue(value="test_value")) + set_request = SetValueRequest(panel_id="test_panel", value_id="test_value", value=test_value) + python_panel_service_stub.SetValue(set_request) + + request = GetValueRequest(panel_id="test_panel", value_id="test_value") + response = python_panel_service_stub.GetValue(request) + + assert response is not None # Ensure response is returned + assert response.value == test_value # Ensure the value is correct diff --git a/tests/unit/test_streamlit_panel.py b/tests/unit/test_streamlit_panel.py index e1d0a29..f7813b8 100644 --- a/tests/unit/test_streamlit_panel.py +++ b/tests/unit/test_streamlit_panel.py @@ -64,6 +64,35 @@ 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( fake_python_panel_service: FakePythonPanelService, fake_panel_channel: grpc.Channel, diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index 920d3dd..48d4edb 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -1,10 +1,13 @@ from typing import Any import grpc -from google.protobuf import any_pb2 from ni.pythonpanel.v1.python_panel_service_pb2 import ( OpenPanelRequest, OpenPanelResponse, + ClosePanelRequest, + ClosePanelResponse, + EnumeratePanelsRequest, + EnumeratePanelsResponse, GetValueRequest, GetValueResponse, SetValueRequest, @@ -18,7 +21,8 @@ class FakePythonPanelServicer(PythonPanelServiceServicer): def __init__(self) -> None: """Initialize the fake PythonPanelServicer.""" - self._values = {"test_value": any_pb2.Any()} + self._values: dict[str, Any] = {} + self._panel_ids: list[str] = [] self._fail_next_open_panel = False def OpenPanel(self, request: OpenPanelRequest, context: Any) -> OpenPanelResponse: # noqa: N802 @@ -26,8 +30,24 @@ def OpenPanel(self, request: OpenPanelRequest, context: Any) -> OpenPanelRespons if self._fail_next_open_panel: self._fail_next_open_panel = False context.abort(grpc.StatusCode.UNAVAILABLE, "Simulated failure") + self._panel_ids.append(request.panel_id) return OpenPanelResponse() + def ClosePanel( # noqa: N802 + self, request: ClosePanelRequest, context: Any + ) -> ClosePanelResponse: + """Trivial implementation for testing.""" + if request.reset: + self._panel_ids.remove(request.panel_id) + self._values.clear() + return ClosePanelResponse() + + def EnumeratePanels( # noqa: N802 + self, request: EnumeratePanelsRequest, context: Any + ) -> EnumeratePanelsResponse: + """Trivial implementation for testing.""" + return EnumeratePanelsResponse(panel_ids=self._panel_ids) + def GetValue(self, request: GetValueRequest, context: Any) -> GetValueResponse: # noqa: N802 """Trivial implementation for testing.""" value = self._values[request.value_id]