Skip to content

Commit d58d23e

Browse files
stuqdogpenguinland
andauthored
RSDK-10717 - support modules on windows (#924)
Co-authored-by: Alan Davidson <[email protected]>
1 parent 3da41ca commit d58d23e

File tree

2 files changed

+38
-15
lines changed

2 files changed

+38
-15
lines changed

src/viam/module/module.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from viam.resource.registry import Registry
3333
from viam.resource.types import API, RESOURCE_TYPE_COMPONENT, RESOURCE_TYPE_SERVICE, Model, ResourceName, resource_name_from_string
3434
from viam.robot.client import RobotClient
35-
from viam.rpc.dial import DialOptions
35+
from viam.rpc.dial import DialOptions, _host_port_from_url
3636
from viam.rpc.server import Server
3737

3838
# These imports are required to register build-in resources with the registry
@@ -73,7 +73,8 @@ def _parse_module_args() -> argparse.Namespace:
7373
p = argparse.ArgumentParser(description="Start this viam python module")
7474
p.add_argument("socket_path", help="path where this module will serve a unix socket")
7575
p.add_argument("--log-level", type=lambda name: pylogging._nameToLevel[name.upper()], default=logging.INFO)
76-
return p.parse_args()
76+
p.add_argument("--tcp-mode", action='store_true')
77+
return p.parse_known_args()[0]
7778

7879

7980
class Module:
@@ -82,6 +83,7 @@ class Module:
8283
_ready: bool
8384
_log_level: int
8485
_lock: Lock
86+
_tcp_mode: bool
8587
parent: Optional[RobotClient] = None
8688
server: Server
8789
logger: pylogging.Logger
@@ -100,7 +102,7 @@ def from_args(cls) -> Self:
100102
Module: a new Module instance
101103
"""
102104
args = _parse_module_args()
103-
return cls(args.socket_path, log_level=args.log_level)
105+
return cls(args.socket_path, log_level=args.log_level, tcp_mode=args.tcp_mode)
104106

105107
@classmethod
106108
async def run_with_models(cls, *models: ResourceBase):
@@ -132,7 +134,7 @@ async def run_from_registry(cls):
132134
module.add_model_from_registry(*key.split("/")) # pyright: ignore [reportArgumentType]
133135
await module.start()
134136

135-
def __init__(self, address: str, *, log_level: int = logging.INFO) -> None:
137+
def __init__(self, address: str, *, log_level: int = logging.INFO, tcp_mode: bool = False) -> None:
136138
# When a module is launched by viam-server, its stdout is not connected to a tty. In
137139
# response, python disables line buffering, which prevents `print` statements from being
138140
# immediately flushed to viam-server. This behavior can be confusing, interfere with
@@ -143,6 +145,7 @@ def __init__(self, address: str, *, log_level: int = logging.INFO) -> None:
143145
if isinstance(sys.stderr, io.TextIOWrapper):
144146
sys.stderr.reconfigure(line_buffering=True)
145147
self._address = address
148+
self._tcp_mode = tcp_mode
146149
self.server = Server(resources=[], module_service=ModuleRPCService(self))
147150
self._log_level = log_level
148151

@@ -188,7 +191,11 @@ async def _get_dependencies(self, dependencies: Sequence[str]) -> Mapping[Resour
188191
async def start(self):
189192
"""Start the module service and gRPC server"""
190193
try:
191-
await self.server.serve(log_level=self._log_level, path=self._address)
194+
if self._tcp_mode:
195+
host, port = _host_port_from_url(self._address)
196+
await self.server.serve(log_level=self._log_level, host=host, port=port)
197+
else:
198+
await self.server.serve(log_level=self._log_level, path=self._address)
192199
finally:
193200
await self.stop()
194201

src/viam/rpc/server.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from typing import TYPE_CHECKING, Callable, List, Optional
22

3+
import sys
4+
35
from grpclib import GRPCError, Status
46
from grpclib._typing import IServable
57
from grpclib.const import Handler
@@ -82,6 +84,25 @@ async def rcv_and_log_msg():
8284

8385
event.method_func = log_resource_name
8486

87+
async def _serve(
88+
self,
89+
host: Optional[str] = "localhost",
90+
port: Optional[int] = 9090,
91+
log_level: Optional[int] = logging.INFO,
92+
*,
93+
path: Optional[str] = None,
94+
):
95+
96+
if path:
97+
await self._server.start(path=path)
98+
LOGGER.info(f"Serving on {path}")
99+
else:
100+
await self._server.start(host, port)
101+
LOGGER.info(f"Serving on {host}:{port}")
102+
await self._server.wait_closed()
103+
await self.close()
104+
LOGGER.debug("gRPC server closed")
105+
85106
async def serve(
86107
self,
87108
host: Optional[str] = "localhost",
@@ -105,16 +126,11 @@ async def serve(
105126
logging.setLevel(log_level)
106127
listen(self._server, RecvRequest, self._grpc_recvrequest_handler)
107128

108-
with graceful_exit([self._server]):
109-
if path:
110-
await self._server.start(path=path)
111-
LOGGER.info(f"Serving on {path}")
112-
else:
113-
await self._server.start(host, port)
114-
LOGGER.info(f"Serving on {host}:{port}")
115-
await self._server.wait_closed()
116-
await self.close()
117-
LOGGER.debug("gRPC server closed")
129+
if sys.platform != 'win32':
130+
with graceful_exit([self._server]):
131+
await self._serve(host=host, port=port, log_level=log_level, path=path)
132+
else:
133+
await self._serve(host=host, port=port, log_level=log_level, path=path)
118134

119135
@classmethod
120136
async def create_and_serve(

0 commit comments

Comments
 (0)