diff --git a/protos/ni/pythonpanel/v1/python_panel_service.proto b/protos/ni/pythonpanel/v1/python_panel_service.proto index 0476d06..ab833ac 100644 --- a/protos/ni/pythonpanel/v1/python_panel_service.proto +++ b/protos/ni/pythonpanel/v1/python_panel_service.proto @@ -18,13 +18,18 @@ option ruby_package = "NI::PythonPanel::V1"; service PythonPanelService { // 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 + // - INVALID_ARGUMENT: + // - The panel script filename doesn't end in .py. + // - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - NOT_FOUND: + // - The panel script file was not found. + // - The python executable file was not found. rpc StartPanel(StartPanelRequest) returns (StartPanelResponse); // Stop a panel // Status Codes for errors: - // - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - INVALID_ARGUMENT: + // - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. 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. @@ -33,12 +38,25 @@ service PythonPanelService { // 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. + // - INVALID_ARGUMENT: + // - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - The value 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); + // Try to get a value for a control on the panel + // Status Codes for errors: + // - INVALID_ARGUMENT: + // - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + rpc TryGetValue(TryGetValueRequest) returns (TryGetValueResponse); + // 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. + // - INVALID_ARGUMENT: + // - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + // - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. rpc SetValue(SetValueRequest) returns (SetValueResponse); } @@ -98,6 +116,19 @@ message GetValueRequest { message GetValueResponse { // The value + google.protobuf.Any value = 1; +} + +message TryGetValueRequest { + // Unique ID of the panel + string panel_id = 1; + + // Unique ID of value + string value_id = 2; +} + +message TryGetValueResponse { + // The value, if it was found optional google.protobuf.Any value = 1; } diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2.py b/src/ni/pythonpanel/v1/python_panel_service_pb2.py index 3d03f69..b8b36dd 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.py @@ -14,7 +14,7 @@ from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"U\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x19\n\x11panel_script_path\x18\x02 \x01(\t\x12\x13\n\x0bpython_path\x18\x03 \x01(\t\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_url\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_url\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"F\n\x10GetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\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') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,ni/pythonpanel/v1/python_panel_service.proto\x12\x11ni.pythonpanel.v1\x1a\x19google/protobuf/any.proto\"U\n\x11StartPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x19\n\x11panel_script_path\x18\x02 \x01(\t\x12\x13\n\x0bpython_path\x18\x03 \x01(\t\"\'\n\x12StartPanelResponse\x12\x11\n\tpanel_url\x18\x01 \x01(\t\"3\n\x10StopPanelRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\r\n\x05reset\x18\x02 \x01(\x08\"\x13\n\x11StopPanelResponse\"\x18\n\x16\x45numeratePanelsRequest\"J\n\x10PanelInformation\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x11\n\tpanel_url\x18\x02 \x01(\t\x12\x11\n\tvalue_ids\x18\x03 \x03(\t\"N\n\x17\x45numeratePanelsResponse\x12\x33\n\x06panels\x18\x01 \x03(\x0b\x32#.ni.pythonpanel.v1.PanelInformation\"5\n\x0fGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"7\n\x10GetValueResponse\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"8\n\x12TryGetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\"I\n\x13TryGetValueResponse\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x88\x01\x01\x42\x08\n\x06_value\"j\n\x0fSetValueRequest\x12\x10\n\x08panel_id\x18\x01 \x01(\t\x12\x10\n\x08value_id\x18\x02 \x01(\t\x12#\n\x05value\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0e\n\x06notify\x18\x04 \x01(\x08\"\x12\n\x10SetValueResponse2\xb9\x04\n\x12PythonPanelService\x12Y\n\nStartPanel\x12$.ni.pythonpanel.v1.StartPanelRequest\x1a%.ni.pythonpanel.v1.StartPanelResponse\x12V\n\tStopPanel\x12#.ni.pythonpanel.v1.StopPanelRequest\x1a$.ni.pythonpanel.v1.StopPanelResponse\x12h\n\x0f\x45numeratePanels\x12).ni.pythonpanel.v1.EnumeratePanelsRequest\x1a*.ni.pythonpanel.v1.EnumeratePanelsResponse\x12S\n\x08GetValue\x12\".ni.pythonpanel.v1.GetValueRequest\x1a#.ni.pythonpanel.v1.GetValueResponse\x12\\\n\x0bTryGetValue\x12%.ni.pythonpanel.v1.TryGetValueRequest\x1a&.ni.pythonpanel.v1.TryGetValueResponse\x12S\n\x08SetValue\x12\".ni.pythonpanel.v1.SetValueRequest\x1a#.ni.pythonpanel.v1.SetValueResponseB\x9a\x01\n\x15\x63om.ni.pythonpanel.v1B\x17PythonPanelServiceProtoP\x01Z\rpythonpanelv1\xf8\x01\x01\xa2\x02\x04NIPP\xaa\x02\"NationalInstruments.PythonPanel.V1\xca\x02\x11NI\\PythonPanel\\V1\xea\x02\x13NI::PythonPanel::V1b\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ni.pythonpanel.v1.python_panel_service_pb2', globals()) @@ -39,11 +39,15 @@ _GETVALUEREQUEST._serialized_start=478 _GETVALUEREQUEST._serialized_end=531 _GETVALUERESPONSE._serialized_start=533 - _GETVALUERESPONSE._serialized_end=603 - _SETVALUEREQUEST._serialized_start=605 - _SETVALUEREQUEST._serialized_end=711 - _SETVALUERESPONSE._serialized_start=713 - _SETVALUERESPONSE._serialized_end=731 - _PYTHONPANELSERVICE._serialized_start=734 - _PYTHONPANELSERVICE._serialized_end=1209 + _GETVALUERESPONSE._serialized_end=588 + _TRYGETVALUEREQUEST._serialized_start=590 + _TRYGETVALUEREQUEST._serialized_end=646 + _TRYGETVALUERESPONSE._serialized_start=648 + _TRYGETVALUERESPONSE._serialized_end=721 + _SETVALUEREQUEST._serialized_start=723 + _SETVALUEREQUEST._serialized_end=829 + _SETVALUERESPONSE._serialized_start=831 + _SETVALUERESPONSE._serialized_end=849 + _PYTHONPANELSERVICE._serialized_start=852 + _PYTHONPANELSERVICE._serialized_end=1421 # @@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 aba0543..f55eb02 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2.pyi @@ -166,6 +166,45 @@ class GetValueResponse(google.protobuf.message.Message): def value(self) -> google.protobuf.any_pb2.Any: """The value""" + def __init__( + self, + *, + value: google.protobuf.any_pb2.Any | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["value", b"value"]) -> None: ... + +global___GetValueResponse = GetValueResponse + +@typing.final +class TryGetValueRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PANEL_ID_FIELD_NUMBER: builtins.int + VALUE_ID_FIELD_NUMBER: builtins.int + panel_id: builtins.str + """Unique ID of the panel""" + value_id: builtins.str + """Unique ID of value""" + def __init__( + self, + *, + panel_id: builtins.str = ..., + value_id: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "value_id", b"value_id"]) -> None: ... + +global___TryGetValueRequest = TryGetValueRequest + +@typing.final +class TryGetValueResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + @property + def value(self) -> google.protobuf.any_pb2.Any: + """The value, if it was found""" + def __init__( self, *, @@ -175,7 +214,7 @@ class GetValueResponse(google.protobuf.message.Message): def ClearField(self, field_name: typing.Literal["_value", b"_value", "value", b"value"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["_value", b"_value"]) -> typing.Literal["value"] | None: ... -global___GetValueResponse = GetValueResponse +global___TryGetValueResponse = TryGetValueResponse @typing.final class SetValueRequest(google.protobuf.message.Message): diff --git a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py index 01ba9c9..f2a3f72 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.py @@ -35,6 +35,11 @@ def __init__(self, channel): request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueRequest.SerializeToString, response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueResponse.FromString, ) + self.TryGetValue = channel.unary_unary( + '/ni.pythonpanel.v1.PythonPanelService/TryGetValue', + request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueRequest.SerializeToString, + response_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueResponse.FromString, + ) self.SetValue = channel.unary_unary( '/ni.pythonpanel.v1.PythonPanelService/SetValue', request_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueRequest.SerializeToString, @@ -49,8 +54,12 @@ class PythonPanelServiceServicer(object): 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 + - INVALID_ARGUMENT: + - The panel script filename doesn't end in .py. + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The panel script file was not found. + - The python executable file was not found. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -59,7 +68,8 @@ def StartPanel(self, request, context): 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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -76,7 +86,22 @@ def EnumeratePanels(self, request, context): 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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value 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 TryGetValue(self, request, context): + """Try to get a value for a control on the panel + Status Codes for errors: + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -85,7 +110,9 @@ def GetValue(self, request, context): 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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -114,6 +141,11 @@ def add_PythonPanelServiceServicer_to_server(servicer, server): request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueRequest.FromString, response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.GetValueResponse.SerializeToString, ), + 'TryGetValue': grpc.unary_unary_rpc_method_handler( + servicer.TryGetValue, + request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueRequest.FromString, + response_serializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueResponse.SerializeToString, + ), 'SetValue': grpc.unary_unary_rpc_method_handler( servicer.SetValue, request_deserializer=ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.SetValueRequest.FromString, @@ -198,6 +230,23 @@ def GetValue(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def TryGetValue(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/TryGetValue', + ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueRequest.SerializeToString, + ni_dot_pythonpanel_dot_v1_dot_python__panel__service__pb2.TryGetValueResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def SetValue(request, target, 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 622c2e0..a38cae8 100644 --- a/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi +++ b/src/ni/pythonpanel/v1/python_panel_service_pb2_grpc.pyi @@ -27,8 +27,12 @@ class PythonPanelServiceStub: ] """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 + - INVALID_ARGUMENT: + - The panel script filename doesn't end in .py. + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The panel script file was not found. + - The python executable file was not found. """ StopPanel: grpc.UnaryUnaryMultiCallable[ @@ -37,7 +41,8 @@ class PythonPanelServiceStub: ] """Stop a panel Status Codes for errors: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ EnumeratePanels: grpc.UnaryUnaryMultiCallable[ @@ -54,7 +59,22 @@ class PythonPanelServiceStub: ] """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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The value with the specified identifier was not found. + """ + + TryGetValue: grpc.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueRequest, + ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse, + ] + """Try to get a value for a control on the panel + Status Codes for errors: + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ SetValue: grpc.UnaryUnaryMultiCallable[ @@ -63,7 +83,9 @@ class PythonPanelServiceStub: ] """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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ class PythonPanelServiceAsyncStub: @@ -75,8 +97,12 @@ class PythonPanelServiceAsyncStub: ] """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 + - INVALID_ARGUMENT: + - The panel script filename doesn't end in .py. + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The panel script file was not found. + - The python executable file was not found. """ StopPanel: grpc.aio.UnaryUnaryMultiCallable[ @@ -85,7 +111,8 @@ class PythonPanelServiceAsyncStub: ] """Stop a panel Status Codes for errors: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ EnumeratePanels: grpc.aio.UnaryUnaryMultiCallable[ @@ -102,7 +129,22 @@ class PythonPanelServiceAsyncStub: ] """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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The value with the specified identifier was not found. + """ + + TryGetValue: grpc.aio.UnaryUnaryMultiCallable[ + ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueRequest, + ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse, + ] + """Try to get a value for a control on the panel + Status Codes for errors: + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ SetValue: grpc.aio.UnaryUnaryMultiCallable[ @@ -111,7 +153,9 @@ class PythonPanelServiceAsyncStub: ] """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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): @@ -125,8 +169,12 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): ) -> 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 + - INVALID_ARGUMENT: + - The panel script filename doesn't end in .py. + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The panel script file was not found. + - The python executable file was not found. """ @abc.abstractmethod @@ -137,7 +185,8 @@ class PythonPanelServiceServicer(metaclass=abc.ABCMeta): ) -> 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. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ @abc.abstractmethod @@ -158,7 +207,24 @@ 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: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - NOT_FOUND: + - The value with the specified identifier was not found. + """ + + @abc.abstractmethod + def TryGetValue( + self, + request: ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueRequest, + context: _ServicerContext, + ) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.TryGetValueResponse]]: + """Try to get a value for a control on the panel + Status Codes for errors: + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ @abc.abstractmethod @@ -169,7 +235,9 @@ 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: - - INVALID_ARGUMENT: The specified identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - INVALID_ARGUMENT: + - The panel identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. + - The value identifier contains invalid characters. Only alphanumeric characters and underscores are allowed. """ def add_PythonPanelServiceServicer_to_server(servicer: PythonPanelServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ... diff --git a/src/nipanel/_panel_client.py b/src/nipanel/_panel_client.py index 278378e..9de42d3 100644 --- a/src/nipanel/_panel_client.py +++ b/src/nipanel/_panel_client.py @@ -1,5 +1,3 @@ -"""Client for accessing the NI Python Panel Service.""" - from __future__ import annotations import logging @@ -12,6 +10,7 @@ StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, + TryGetValueRequest, SetValueRequest, ) from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub @@ -30,9 +29,7 @@ _logger = logging.getLogger(__name__) -class PanelClient: - """Client for accessing the NI Python Panel Service.""" - +class _PanelClient: def __init__( self, *, @@ -42,15 +39,6 @@ def __init__( grpc_channel_pool: GrpcChannelPool | None = None, grpc_channel: grpc.Channel | None = None, ) -> None: - """Initialize the panel client. - - Args: - provided_interface: The interface provided by the service. - service_class: The class of the service. - discovery_client: An optional discovery client. - grpc_channel: An optional panel gRPC channel. - grpc_channel_pool: An optional gRPC channel pool. - """ self._initialization_lock = threading.Lock() self._provided_interface = provided_interface self._service_class = service_class @@ -60,16 +48,6 @@ def __init__( self._stub: PythonPanelServiceStub | None = None def start_panel(self, panel_id: str, panel_script_path: str, python_path: str) -> str: - """Start the panel. - - Args: - panel_id: The ID of the panel to start. - panel_script_path: The path of the panel script file. - python_path: The path to the Python executable. - - Returns: - The URL of the panel. - """ start_panel_request = StartPanelRequest( panel_id=panel_id, panel_script_path=panel_script_path, python_path=python_path ) @@ -77,22 +55,10 @@ def start_panel(self, panel_id: str, panel_script_path: str, python_path: str) - return response.panel_url def stop_panel(self, panel_id: str, reset: bool) -> None: - """Stop the panel. - - Args: - panel_id: The ID of the panel to stop. - reset: Whether to reset all storage associated with panel. - """ 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[str, list[str]]]: - """Enumerate all available panels. - - Returns: - 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 @@ -102,37 +68,24 @@ def enumerate_panels(self) -> dict[str, tuple[str, list[str]]]: } def set_value(self, panel_id: str, value_id: str, value: object, notify: bool) -> None: - """Set the value for the control with value_id. - - Args: - panel_id: The ID of the panel. - value_id: The ID of the control. - value: The value to set. - notify: Whether to notify other clients of the new value. - """ new_any = to_any(value) set_value_request = SetValueRequest( panel_id=panel_id, value_id=value_id, value=new_any, notify=notify ) self._invoke_with_retry(self._get_stub().SetValue, set_value_request) - def get_value(self, panel_id: str, value_id: str) -> tuple[bool, object]: - """Get the value for the control with value_id. - - Args: - panel_id: The ID of the panel. - value_id: The ID of the control. - - Returns: - A tuple containing a boolean indicating whether the value was successfully retrieved and - the value itself (or None if not present). - """ + def get_value(self, panel_id: str, value_id: str) -> object: get_value_request = GetValueRequest(panel_id=panel_id, value_id=value_id) response = self._invoke_with_retry(self._get_stub().GetValue, get_value_request) + return from_any(response.value) + + def try_get_value(self, panel_id: str, value_id: str) -> object | None: + try_get_value_request = TryGetValueRequest(panel_id=panel_id, value_id=value_id) + response = self._invoke_with_retry(self._get_stub().TryGetValue, try_get_value_request) if response.HasField("value"): - return True, from_any(response.value) + return from_any(response.value) else: - return False, None + return None def _get_stub(self) -> PythonPanelServiceStub: if self._stub is None: diff --git a/src/nipanel/_panel_value_accessor.py b/src/nipanel/_panel_value_accessor.py index cb53d56..c123bd0 100644 --- a/src/nipanel/_panel_value_accessor.py +++ b/src/nipanel/_panel_value_accessor.py @@ -9,7 +9,7 @@ from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool -from nipanel._panel_client import PanelClient +from nipanel._panel_client import _PanelClient _T = TypeVar("_T") @@ -37,7 +37,7 @@ def __init__( grpc_channel: grpc.Channel | None = None, ) -> None: """Initialize the accessor.""" - self._panel_client = PanelClient( + self._panel_client = _PanelClient( provided_interface=provided_interface, service_class=service_class, discovery_client=discovery_client, @@ -71,8 +71,8 @@ def get_value(self, value_id: str, default_value: _T | None = None) -> _T | obje Returns: The value, or the default value if not set """ - found, value = self._panel_client.get_value(self._panel_id, value_id) - if not found: + value = self._panel_client.try_get_value(self._panel_id, value_id) + if value is None: if default_value is not None: return default_value raise KeyError(f"Value with id '{value_id}' not found on panel '{self._panel_id}'.") diff --git a/tests/unit/test_convert.py b/tests/unit/test_convert.py index 921b129..7580fef 100644 --- a/tests/unit/test_convert.py +++ b/tests/unit/test_convert.py @@ -144,6 +144,12 @@ def test___python_panel_collection___to_any___valid_paneltype_value( assert unpack_dest.values == expected_value +def test___none_value___to_any___raises_type_error() -> None: + """Test that passing None to to_any() raises a TypeError.""" + with pytest.raises(TypeError): + nipanel._convert.to_any(None) + + # ======================================================== # Built-in Types: Protobuf to Python # ======================================================== diff --git a/tests/unit/test_panel_client.py b/tests/unit/test_panel_client.py index 151d912..6f422da 100644 --- a/tests/unit/test_panel_client.py +++ b/tests/unit/test_panel_client.py @@ -1,6 +1,7 @@ import grpc +import pytest -from nipanel._panel_client import PanelClient +from nipanel._panel_client import _PanelClient def test___enumerate_is_empty(fake_panel_channel: grpc.Channel) -> None: @@ -50,12 +51,21 @@ def test___start_panels___stop_panel_1_without_reset___enumerate_has_both_panels } -def test___get_unset_value___returns_not_found(fake_panel_channel: grpc.Channel) -> None: +def test___get_unset_value___raises_exception(fake_panel_channel: grpc.Channel) -> None: client = create_panel_client(fake_panel_channel) - response = client.get_value("panel1", "unset_id") + with pytest.raises(grpc.RpcError) as exc_info: + client.get_value("panel1", "unset_id") - assert response == (False, None) + assert exc_info.value.code() == grpc.StatusCode.NOT_FOUND + + +def test___try_get_unset_value___returns_none(fake_panel_channel: grpc.Channel) -> None: + client = create_panel_client(fake_panel_channel) + + response = client.try_get_value("panel1", "unset_id") + + assert response is None def test___set_value___enumerate_panels_shows_value( @@ -73,11 +83,11 @@ def test___set_value___gets_value(fake_panel_channel: grpc.Channel) -> None: client.set_value("panel1", "val1", "value1", notify=False) - assert client.get_value("panel1", "val1") == (True, "value1") + assert client.try_get_value("panel1", "val1") == "value1" -def create_panel_client(fake_panel_channel: grpc.Channel) -> PanelClient: - return PanelClient( +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 f6f4276..a26f671 100644 --- a/tests/unit/test_python_panel_service_stub.py +++ b/tests/unit/test_python_panel_service_stub.py @@ -1,3 +1,4 @@ +import pytest from google.protobuf.any_pb2 import Any from google.protobuf.wrappers_pb2 import StringValue from ni.pythonpanel.v1.python_panel_service_pb2 import ( @@ -5,6 +6,7 @@ StopPanelRequest, EnumeratePanelsRequest, GetValueRequest, + TryGetValueRequest, SetValueRequest, ) from ni.pythonpanel.v1.python_panel_service_pb2_grpc import PythonPanelServiceStub @@ -62,3 +64,36 @@ def test___set_value___get_value___gets_response( assert response is not None # Ensure response is returned assert response.value == test_value # Ensure the value is correct + + +def test___no_value___get_value___raises_exception( + python_panel_service_stub: PythonPanelServiceStub, +) -> None: + request = GetValueRequest(panel_id="test_panel", value_id="test_value") + with pytest.raises(Exception): + python_panel_service_stub.GetValue(request) + + +def test___set_value___try_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 = TryGetValueRequest(panel_id="test_panel", value_id="test_value") + response = python_panel_service_stub.TryGetValue(request) + + assert response is not None # Ensure response is returned + assert response.value == test_value # Ensure the value is correct + + +def test___no_value___try_get_value___gets_no_value( + python_panel_service_stub: PythonPanelServiceStub, +) -> None: + request = TryGetValueRequest(panel_id="test_panel", value_id="test_value") + response = python_panel_service_stub.TryGetValue(request) + + assert response is not None # Ensure response is returned + assert response.HasField("value") is False # Ensure no value is returned diff --git a/tests/utils/_fake_python_panel_servicer.py b/tests/utils/_fake_python_panel_servicer.py index ac74fe6..77d5824 100644 --- a/tests/utils/_fake_python_panel_servicer.py +++ b/tests/utils/_fake_python_panel_servicer.py @@ -11,6 +11,8 @@ PanelInformation, GetValueRequest, GetValueResponse, + TryGetValueRequest, + TryGetValueResponse, SetValueRequest, SetValueResponse, ) @@ -63,10 +65,19 @@ def EnumeratePanels( # noqa: N802 def GetValue(self, request: GetValueRequest, context: Any) -> GetValueResponse: # noqa: N802 """Trivial implementation for testing.""" if request.value_id not in self._panel_value_ids.get(request.panel_id, {}): - return GetValueResponse() + context.abort(grpc.StatusCode.NOT_FOUND, "Value ID not found in panel") value = self._panel_value_ids[request.panel_id][request.value_id] return GetValueResponse(value=value) + def TryGetValue( # noqa: N802 + self, request: TryGetValueRequest, context: Any + ) -> TryGetValueResponse: + """Trivial implementation for testing.""" + if request.value_id not in self._panel_value_ids.get(request.panel_id, {}): + return TryGetValueResponse() + value = self._panel_value_ids[request.panel_id][request.value_id] + return TryGetValueResponse(value=value) + def SetValue(self, request: SetValueRequest, context: Any) -> SetValueResponse: # noqa: N802 """Trivial implementation for testing.""" self._init_panel(request.panel_id)