Skip to content

Commit ab1f2cf

Browse files
mikeprosserniMike Prosser
andauthored
Implement connect() and disconnect() (#5)
* first draft (breaks tests) * PanelNotFoundError when gRPC status code is NOT_FOUND * fix linter issues * create a basic fake server and passing basic tests for it * use FakePanel to fix failing tests * move fixture to conftest.py * remove unused serve() function * cleanup * use a channel pool * add a retry if connect fails, and remove PanelNotFoundError * cleanup * refactor to use PanelClient * misc feedback and FakePythonPanelService * PanelClient cleanup * use channel parameter for testing instead of PortPanel, and move provided_interface and service_class to Panel. * misc feedback * don't return tuples from fixtures --------- Co-authored-by: Mike Prosser <Mike.Prosser@emerson.com>
1 parent 7d1e3b0 commit ab1f2cf

15 files changed

+942
-56
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ See [GitHub's official documentation](https://help.github.com/articles/using-pul
2222
# Getting Started
2323

2424
This is the command to generate the files in /src/ni/pythonpanel/v1/:
25-
`poetry run python -m grpc_tools.protoc --proto_path=protos --python_out=src/ --grpc_python_out=src/ ni/pythonpanel/v1/python_panel_service.proto`
25+
`poetry run python -m grpc_tools.protoc --proto_path=protos --python_out=src/ --grpc_python_out=src/ --mypy_out=src/ --mypy_grpc_out=src/ ni/pythonpanel/v1/python_panel_service.proto`
2626

2727
# Testing
2828

poetry.lock

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

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ packages = [{ include = "nipanel", from = "src" }, { include = "ni", from = "src
1010
python = "^3.9"
1111
grpcio = {version=">=1.49.0,<2.0"}
1212
protobuf = {version=">=4.21"}
13+
ni-measurement-plugin-sdk = {version=">=2.3"}
14+
15+
[tool.poetry.group.dev.dependencies]
16+
grpc-stubs = "^1.53"
17+
types-protobuf = ">=4.21"
1318

1419
[tool.poetry.group.lint.dependencies]
1520
bandit = { version = ">=1.7", extras = ["toml"] }
@@ -36,6 +41,9 @@ build-backend = "poetry.core.masonry.api"
3641
[tool.ni-python-styleguide]
3742
extend_exclude = ".tox,docs,src/ni/pythonpanel/v1"
3843

44+
[tool.black]
45+
extend-exclude = '\.tox/|docs/|src/ni/pythonpanel/v1/'
46+
3947
[tool.mypy]
4048
files = "examples/,src/nipanel/,tests/"
4149
namespace_packages = true
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"""
2+
@generated by mypy-protobuf. Do not edit manually!
3+
isort:skip_file
4+
"""
5+
6+
import builtins
7+
import google.protobuf.any_pb2
8+
import google.protobuf.descriptor
9+
import google.protobuf.message
10+
import typing
11+
12+
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
13+
14+
@typing.final
15+
class ConnectRequest(google.protobuf.message.Message):
16+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
17+
18+
PANEL_ID_FIELD_NUMBER: builtins.int
19+
PANEL_URI_FIELD_NUMBER: builtins.int
20+
panel_id: builtins.str
21+
"""Unique ID of the panel"""
22+
panel_uri: builtins.str
23+
"""Absolute path of the panel's file on disk, or network path to the file"""
24+
def __init__(
25+
self,
26+
*,
27+
panel_id: builtins.str = ...,
28+
panel_uri: builtins.str = ...,
29+
) -> None: ...
30+
def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "panel_uri", b"panel_uri"]) -> None: ...
31+
32+
global___ConnectRequest = ConnectRequest
33+
34+
@typing.final
35+
class ConnectResponse(google.protobuf.message.Message):
36+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
37+
38+
def __init__(
39+
self,
40+
) -> None: ...
41+
42+
global___ConnectResponse = ConnectResponse
43+
44+
@typing.final
45+
class DisconnectRequest(google.protobuf.message.Message):
46+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
47+
48+
PANEL_ID_FIELD_NUMBER: builtins.int
49+
panel_id: builtins.str
50+
"""Unique ID of the panel"""
51+
def __init__(
52+
self,
53+
*,
54+
panel_id: builtins.str = ...,
55+
) -> None: ...
56+
def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id"]) -> None: ...
57+
58+
global___DisconnectRequest = DisconnectRequest
59+
60+
@typing.final
61+
class DisconnectResponse(google.protobuf.message.Message):
62+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
63+
64+
def __init__(
65+
self,
66+
) -> None: ...
67+
68+
global___DisconnectResponse = DisconnectResponse
69+
70+
@typing.final
71+
class GetValueRequest(google.protobuf.message.Message):
72+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
73+
74+
PANEL_ID_FIELD_NUMBER: builtins.int
75+
VALUE_ID_FIELD_NUMBER: builtins.int
76+
panel_id: builtins.str
77+
"""Unique ID of the panel"""
78+
value_id: builtins.str
79+
"""Unique ID of value"""
80+
def __init__(
81+
self,
82+
*,
83+
panel_id: builtins.str = ...,
84+
value_id: builtins.str = ...,
85+
) -> None: ...
86+
def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "value_id", b"value_id"]) -> None: ...
87+
88+
global___GetValueRequest = GetValueRequest
89+
90+
@typing.final
91+
class GetValueResponse(google.protobuf.message.Message):
92+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
93+
94+
VALUE_FIELD_NUMBER: builtins.int
95+
@property
96+
def value(self) -> google.protobuf.any_pb2.Any:
97+
"""The value"""
98+
99+
def __init__(
100+
self,
101+
*,
102+
value: google.protobuf.any_pb2.Any | None = ...,
103+
) -> None: ...
104+
def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ...
105+
def ClearField(self, field_name: typing.Literal["value", b"value"]) -> None: ...
106+
107+
global___GetValueResponse = GetValueResponse
108+
109+
@typing.final
110+
class SetValueRequest(google.protobuf.message.Message):
111+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
112+
113+
PANEL_ID_FIELD_NUMBER: builtins.int
114+
VALUE_ID_FIELD_NUMBER: builtins.int
115+
VALUE_FIELD_NUMBER: builtins.int
116+
panel_id: builtins.str
117+
"""Unique ID of the panel"""
118+
value_id: builtins.str
119+
"""Unique ID of the value"""
120+
@property
121+
def value(self) -> google.protobuf.any_pb2.Any:
122+
"""The value"""
123+
124+
def __init__(
125+
self,
126+
*,
127+
panel_id: builtins.str = ...,
128+
value_id: builtins.str = ...,
129+
value: google.protobuf.any_pb2.Any | None = ...,
130+
) -> None: ...
131+
def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ...
132+
def ClearField(self, field_name: typing.Literal["panel_id", b"panel_id", "value", b"value", "value_id", b"value_id"]) -> None: ...
133+
134+
global___SetValueRequest = SetValueRequest
135+
136+
@typing.final
137+
class SetValueResponse(google.protobuf.message.Message):
138+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
139+
140+
def __init__(
141+
self,
142+
) -> None: ...
143+
144+
global___SetValueResponse = SetValueResponse
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
@generated by mypy-protobuf. Do not edit manually!
3+
isort:skip_file
4+
"""
5+
6+
import abc
7+
import collections.abc
8+
import grpc
9+
import grpc.aio
10+
import ni.pythonpanel.v1.python_panel_service_pb2
11+
import typing
12+
13+
_T = typing.TypeVar("_T")
14+
15+
class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ...
16+
17+
class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg]
18+
...
19+
20+
class PythonPanelServiceStub:
21+
"""Service interface for connecting to python panels"""
22+
23+
def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ...
24+
Connect: grpc.UnaryUnaryMultiCallable[
25+
ni.pythonpanel.v1.python_panel_service_pb2.ConnectRequest,
26+
ni.pythonpanel.v1.python_panel_service_pb2.ConnectResponse,
27+
]
28+
"""Connect to a panel and open it
29+
Status Codes for errors:
30+
- NOT_FOUND: the file for the panel was not found
31+
"""
32+
33+
Disconnect: grpc.UnaryUnaryMultiCallable[
34+
ni.pythonpanel.v1.python_panel_service_pb2.DisconnectRequest,
35+
ni.pythonpanel.v1.python_panel_service_pb2.DisconnectResponse,
36+
]
37+
"""Disconnect from a panel (does not close the panel)
38+
Status Codes for errors:
39+
- NOT_FOUND: the panel with the specified id was not found
40+
"""
41+
42+
GetValue: grpc.UnaryUnaryMultiCallable[
43+
ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest,
44+
ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse,
45+
]
46+
"""Get a value for a control on the panel
47+
Status Codes for errors:
48+
- NOT_FOUND: the panel with the specified id was not found
49+
"""
50+
51+
SetValue: grpc.UnaryUnaryMultiCallable[
52+
ni.pythonpanel.v1.python_panel_service_pb2.SetValueRequest,
53+
ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse,
54+
]
55+
"""Set a value for a control on the panel
56+
Status Codes for errors:
57+
- NOT_FOUND: the panel with the specified id was not found
58+
"""
59+
60+
class PythonPanelServiceAsyncStub:
61+
"""Service interface for connecting to python panels"""
62+
63+
Connect: grpc.aio.UnaryUnaryMultiCallable[
64+
ni.pythonpanel.v1.python_panel_service_pb2.ConnectRequest,
65+
ni.pythonpanel.v1.python_panel_service_pb2.ConnectResponse,
66+
]
67+
"""Connect to a panel and open it
68+
Status Codes for errors:
69+
- NOT_FOUND: the file for the panel was not found
70+
"""
71+
72+
Disconnect: grpc.aio.UnaryUnaryMultiCallable[
73+
ni.pythonpanel.v1.python_panel_service_pb2.DisconnectRequest,
74+
ni.pythonpanel.v1.python_panel_service_pb2.DisconnectResponse,
75+
]
76+
"""Disconnect from a panel (does not close the panel)
77+
Status Codes for errors:
78+
- NOT_FOUND: the panel with the specified id was not found
79+
"""
80+
81+
GetValue: grpc.aio.UnaryUnaryMultiCallable[
82+
ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest,
83+
ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse,
84+
]
85+
"""Get a value for a control on the panel
86+
Status Codes for errors:
87+
- NOT_FOUND: the panel with the specified id was not found
88+
"""
89+
90+
SetValue: grpc.aio.UnaryUnaryMultiCallable[
91+
ni.pythonpanel.v1.python_panel_service_pb2.SetValueRequest,
92+
ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse,
93+
]
94+
"""Set a value for a control on the panel
95+
Status Codes for errors:
96+
- NOT_FOUND: the panel with the specified id was not found
97+
"""
98+
99+
class PythonPanelServiceServicer(metaclass=abc.ABCMeta):
100+
"""Service interface for connecting to python panels"""
101+
102+
@abc.abstractmethod
103+
def Connect(
104+
self,
105+
request: ni.pythonpanel.v1.python_panel_service_pb2.ConnectRequest,
106+
context: _ServicerContext,
107+
) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.ConnectResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.ConnectResponse]]:
108+
"""Connect to a panel and open it
109+
Status Codes for errors:
110+
- NOT_FOUND: the file for the panel was not found
111+
"""
112+
113+
@abc.abstractmethod
114+
def Disconnect(
115+
self,
116+
request: ni.pythonpanel.v1.python_panel_service_pb2.DisconnectRequest,
117+
context: _ServicerContext,
118+
) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.DisconnectResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.DisconnectResponse]]:
119+
"""Disconnect from a panel (does not close the panel)
120+
Status Codes for errors:
121+
- NOT_FOUND: the panel with the specified id was not found
122+
"""
123+
124+
@abc.abstractmethod
125+
def GetValue(
126+
self,
127+
request: ni.pythonpanel.v1.python_panel_service_pb2.GetValueRequest,
128+
context: _ServicerContext,
129+
) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.GetValueResponse]]:
130+
"""Get a value for a control on the panel
131+
Status Codes for errors:
132+
- NOT_FOUND: the panel with the specified id was not found
133+
"""
134+
135+
@abc.abstractmethod
136+
def SetValue(
137+
self,
138+
request: ni.pythonpanel.v1.python_panel_service_pb2.SetValueRequest,
139+
context: _ServicerContext,
140+
) -> typing.Union[ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse, collections.abc.Awaitable[ni.pythonpanel.v1.python_panel_service_pb2.SetValueResponse]]:
141+
"""Set a value for a control on the panel
142+
Status Codes for errors:
143+
- NOT_FOUND: the panel with the specified id was not found
144+
"""
145+
146+
def add_PythonPanelServiceServicer_to_server(servicer: PythonPanelServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ...

0 commit comments

Comments
 (0)