carla-driver-interface 2.1.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/PKG-INFO +30 -1
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/README.md +29 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/pyproject.toml +1 -1
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/pyproject.toml.orig +1 -1
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/cli.py +8 -2
- carla_driver_interface-2.2.0/src/carla_driver_interface/inprocess.py +709 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/server.py +29 -6
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/LICENSE +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/__init__.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/__init__.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/carla_driver_pb2.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/carla_driver_pb2.pyi +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/carla_driver_pb2_grpc.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/common_pb2.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/common_pb2.pyi +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/egodriver_pb2.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/egodriver_pb2.pyi +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/egodriver_pb2_grpc.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/sensorsim_pb2.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/_proto/sensorsim_pb2.pyi +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/alpasim.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/contract.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/driver.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/geometry.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/hdmap.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/policies/__init__.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/policies/constant_speed.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/policies/route_follower.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/polyline.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/protocol.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/py.typed +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/service.py +0 -0
- {carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carla-driver-interface
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Both ends of alpasim's egodriver gRPC contract: serve a driving policy, or drive one without CARLA
|
|
5
5
|
Author: Masaya Kataoka
|
|
6
6
|
Author-email: Masaya Kataoka <ms.kataoka@gmail.com>
|
|
@@ -104,6 +104,35 @@ or, in a test, `FakeLoop("localhost:50051", [FakeCamera()])` from
|
|
|
104
104
|
`LoopResult`). The ego follows the returned plan exactly, so what is tested is the policy
|
|
105
105
|
and the wire, not a controller.
|
|
106
106
|
|
|
107
|
+
## Serving on a Unix socket, or in the runtime's own process
|
|
108
|
+
|
|
109
|
+
A server binds a Unix domain socket when its host names one: `serve --host
|
|
110
|
+
unix:///run/policy.sock` (`build_server(policy, host="unix:///run/policy.sock")`; the
|
|
111
|
+
port is unused). A client dials the same address.
|
|
112
|
+
|
|
113
|
+
A runtime that runs the policy in its own process can skip the socket altogether:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from carla_driver_interface.inprocess import in_process_channel
|
|
117
|
+
from carla_driver_interface.protocol import EgodriverServiceStub
|
|
118
|
+
|
|
119
|
+
# A grpc.Channel; mode="alpasim", as on a server.
|
|
120
|
+
channel = in_process_channel(MyPolicy())
|
|
121
|
+
# The generated stubs, unchanged.
|
|
122
|
+
stub = EgodriverServiceStub(channel)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`InProcessChannel` keeps the contract byte for byte: each request is serialized and parsed
|
|
126
|
+
by the servicer's own deserializer, each response likewise, so both sides see the bytes
|
|
127
|
+
the wire would carry. The servicer is the same `EgodriverServicer`, with a context that
|
|
128
|
+
does what grpc's does (`abort`, `set_code`/`set_details`, an exception as `UNKNOWN`,
|
|
129
|
+
invocation, initial and trailing metadata -- the contract revision is negotiated through
|
|
130
|
+
them), and a failed call is a `grpc.RpcError` with the code and details a server's would
|
|
131
|
+
carry. The test suite runs every wire test over TCP, a Unix socket and this channel and
|
|
132
|
+
compares what the client sees, errors included. Only the socket's own behaviour is
|
|
133
|
+
missing: a call runs on the calling thread, so a deadline is reported, as
|
|
134
|
+
`DEADLINE_EXCEEDED`, when the call returns rather than cutting it short.
|
|
135
|
+
|
|
107
136
|
## Running a policy in alpasim
|
|
108
137
|
|
|
109
138
|
alpasim runs its driver as a container its wizard starts, and its runtime drives it over
|
|
@@ -88,6 +88,35 @@ or, in a test, `FakeLoop("localhost:50051", [FakeCamera()])` from
|
|
|
88
88
|
`LoopResult`). The ego follows the returned plan exactly, so what is tested is the policy
|
|
89
89
|
and the wire, not a controller.
|
|
90
90
|
|
|
91
|
+
## Serving on a Unix socket, or in the runtime's own process
|
|
92
|
+
|
|
93
|
+
A server binds a Unix domain socket when its host names one: `serve --host
|
|
94
|
+
unix:///run/policy.sock` (`build_server(policy, host="unix:///run/policy.sock")`; the
|
|
95
|
+
port is unused). A client dials the same address.
|
|
96
|
+
|
|
97
|
+
A runtime that runs the policy in its own process can skip the socket altogether:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from carla_driver_interface.inprocess import in_process_channel
|
|
101
|
+
from carla_driver_interface.protocol import EgodriverServiceStub
|
|
102
|
+
|
|
103
|
+
# A grpc.Channel; mode="alpasim", as on a server.
|
|
104
|
+
channel = in_process_channel(MyPolicy())
|
|
105
|
+
# The generated stubs, unchanged.
|
|
106
|
+
stub = EgodriverServiceStub(channel)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`InProcessChannel` keeps the contract byte for byte: each request is serialized and parsed
|
|
110
|
+
by the servicer's own deserializer, each response likewise, so both sides see the bytes
|
|
111
|
+
the wire would carry. The servicer is the same `EgodriverServicer`, with a context that
|
|
112
|
+
does what grpc's does (`abort`, `set_code`/`set_details`, an exception as `UNKNOWN`,
|
|
113
|
+
invocation, initial and trailing metadata -- the contract revision is negotiated through
|
|
114
|
+
them), and a failed call is a `grpc.RpcError` with the code and details a server's would
|
|
115
|
+
carry. The test suite runs every wire test over TCP, a Unix socket and this channel and
|
|
116
|
+
compares what the client sees, errors included. Only the socket's own behaviour is
|
|
117
|
+
missing: a call runs on the calling thread, so a deadline is reported, as
|
|
118
|
+
`DEADLINE_EXCEEDED`, when the call returns rather than cutting it short.
|
|
119
|
+
|
|
91
120
|
## Running a policy in alpasim
|
|
92
121
|
|
|
93
122
|
alpasim runs its driver as a container its wizard starts, and its runtime drives it over
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/cli.py
RENAMED
|
@@ -56,7 +56,11 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
56
56
|
|
|
57
57
|
serve = commands.add_parser("serve", help="serve a policy over gRPC")
|
|
58
58
|
_add_policy_arguments(serve, os.environ.get(POLICY_ENV, "route_follower"))
|
|
59
|
-
serve.add_argument(
|
|
59
|
+
serve.add_argument(
|
|
60
|
+
"--host",
|
|
61
|
+
default="0.0.0.0",
|
|
62
|
+
help="address to bind, or a Unix socket (unix:///path/policy.sock; --port unused)",
|
|
63
|
+
)
|
|
60
64
|
serve.add_argument("--port", type=int, default=50051)
|
|
61
65
|
serve.add_argument("--cruise-speed", type=float, default=None, help="m/s")
|
|
62
66
|
serve.add_argument(
|
|
@@ -76,7 +80,9 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
76
80
|
demo = commands.add_parser(
|
|
77
81
|
"demo", help="drive a policy server through the CARLA-free fake loop"
|
|
78
82
|
)
|
|
79
|
-
demo.add_argument(
|
|
83
|
+
demo.add_argument(
|
|
84
|
+
"--driver", default="localhost:50051", help="policy host:port, or unix:///path"
|
|
85
|
+
)
|
|
80
86
|
demo.add_argument("--steps", type=int, default=100)
|
|
81
87
|
demo.add_argument("--step", type=float, default=0.1, help="seconds per drive call")
|
|
82
88
|
demo.add_argument("--camera-width", type=int, default=FakeCamera.width)
|
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
"""The egodriver contract without a socket: a gRPC channel that calls the servicer.
|
|
2
|
+
|
|
3
|
+
A runtime that serves its policy from its own process (autoware_carla_scenario's
|
|
4
|
+
``driver.policy``) still pays for TCP: every drive's megabytes of LiDAR are copied
|
|
5
|
+
through the loopback and HTTP/2 framing twice. :class:`InProcessChannel` is a
|
|
6
|
+
:class:`grpc.Channel` that skips the transport and nothing else:
|
|
7
|
+
|
|
8
|
+
* the stubs are the generated ones (``EgodriverServiceStub``,
|
|
9
|
+
``CarlaVehicleServiceStub``), and the servicer registers on the channel with the
|
|
10
|
+
generated ``add_*_to_server`` exactly as on a server, so method names and
|
|
11
|
+
(de)serializers are the generated code's;
|
|
12
|
+
* every request is serialized to bytes and parsed from them by the servicer's
|
|
13
|
+
deserializer, and every response likewise -- the bytes each side sees are the
|
|
14
|
+
bytes it would see on the wire, never a shared message object;
|
|
15
|
+
* the servicer gets a :class:`grpc.ServicerContext` with the semantics it can
|
|
16
|
+
observe on a real server: ``abort`` ends the call with its code and details,
|
|
17
|
+
an exception becomes ``UNKNOWN`` ("Exception calling application: ..."),
|
|
18
|
+
``set_code`` / ``set_details`` fail a call that returns, invocation metadata,
|
|
19
|
+
``send_initial_metadata`` (once) and trailing metadata reach the other side
|
|
20
|
+
(the contract revision is negotiated through them,
|
|
21
|
+
:mod:`carla_driver_interface.contract`);
|
|
22
|
+
* the client sees a failed call as a :class:`grpc.RpcError` that is also a
|
|
23
|
+
:class:`grpc.Call` (``code()``, ``details()``, both metadata), as grpc raises
|
|
24
|
+
it; an unknown method is ``UNIMPLEMENTED`` ("Method not found!"), an
|
|
25
|
+
oversized request ``RESOURCE_EXHAUSTED``, a call on a closed channel a
|
|
26
|
+
``ValueError``.
|
|
27
|
+
|
|
28
|
+
What differs is only what a socket adds: the servicer runs on the calling
|
|
29
|
+
thread, so a deadline cannot cut a call short -- a call that overran its
|
|
30
|
+
``timeout`` fails with ``DEADLINE_EXCEEDED`` when it returns, its effects done,
|
|
31
|
+
as a server that finishes after the client gave up has done them too.
|
|
32
|
+
|
|
33
|
+
Use :func:`in_process_channel` for a policy, as :func:`~carla_driver_interface.server.build_server`
|
|
34
|
+
serves one::
|
|
35
|
+
|
|
36
|
+
channel = in_process_channel(policy)
|
|
37
|
+
stub = EgodriverServiceStub(channel)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
import collections
|
|
43
|
+
import logging
|
|
44
|
+
import threading
|
|
45
|
+
import time
|
|
46
|
+
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
|
|
47
|
+
|
|
48
|
+
import grpc
|
|
49
|
+
|
|
50
|
+
from .driver import BaseDriver
|
|
51
|
+
from .protocol import (
|
|
52
|
+
MAX_MESSAGE_BYTES,
|
|
53
|
+
add_CarlaVehicleServiceServicer_to_server,
|
|
54
|
+
add_EgodriverServiceServicer_to_server,
|
|
55
|
+
channel_options,
|
|
56
|
+
)
|
|
57
|
+
from .service import EgodriverServicer, ServiceMode
|
|
58
|
+
|
|
59
|
+
logger = logging.getLogger(__name__)
|
|
60
|
+
|
|
61
|
+
__all__ = ["InProcessChannel", "InProcessRpcError", "in_process_channel"]
|
|
62
|
+
|
|
63
|
+
#: What :meth:`grpc.ServicerContext.peer` answers on this channel.
|
|
64
|
+
PEER = "in-process"
|
|
65
|
+
|
|
66
|
+
Metadata = Tuple[Tuple[str, Any], ...]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class _Metadatum(collections.namedtuple("_Metadatum", ("key", "value"))):
|
|
70
|
+
"""One metadata entry, as grpc hands them out (``key``, ``value``, a pair)."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _metadata(entries: Optional[Iterable[Tuple[str, Any]]]) -> Metadata:
|
|
74
|
+
"""*entries* as grpc delivers them: checked as grpc checks them, then frozen.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
TypeError: A value grpc refuses before sending (not ``str``/``bytes``,
|
|
78
|
+
or ``str`` under a ``-bin`` key).
|
|
79
|
+
InProcessRpcError: ``INTERNAL`` for a key HTTP/2 cannot carry.
|
|
80
|
+
"""
|
|
81
|
+
out = []
|
|
82
|
+
for key, value in entries or ():
|
|
83
|
+
if key.endswith("-bin"):
|
|
84
|
+
if not isinstance(value, bytes):
|
|
85
|
+
raise TypeError(
|
|
86
|
+
f'Binary metadata key="{key}" expected bytes, got {type(value)}'
|
|
87
|
+
)
|
|
88
|
+
elif not isinstance(value, (str, bytes)):
|
|
89
|
+
raise TypeError(f"Expected str, not {type(value)}")
|
|
90
|
+
if not _legal_key(key):
|
|
91
|
+
raise InProcessRpcError(grpc.StatusCode.INTERNAL, "Invalid metadata")
|
|
92
|
+
if isinstance(value, bytes) and not key.endswith("-bin"):
|
|
93
|
+
value = value.decode("ascii")
|
|
94
|
+
out.append(_Metadatum(key, value))
|
|
95
|
+
return tuple(out)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
_KEY_CHARS = frozenset("abcdefghijklmnopqrstuvwxyz0123456789-_.")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _legal_key(key: str) -> bool:
|
|
102
|
+
# HTTP/2 header names, lower case; grpc reserves the ``grpc-`` prefix.
|
|
103
|
+
return bool(key) and set(key) <= _KEY_CHARS and not key.startswith("grpc-")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class InProcessRpcError(grpc.RpcError, grpc.Call, grpc.Future):
|
|
107
|
+
"""A failed call, as grpc raises one: an :class:`grpc.RpcError`, a :class:`grpc.Call`
|
|
108
|
+
and a done :class:`grpc.Future` (an interceptor's continuation returns it as the
|
|
109
|
+
call's outcome)."""
|
|
110
|
+
|
|
111
|
+
def __init__(
|
|
112
|
+
self,
|
|
113
|
+
code: grpc.StatusCode,
|
|
114
|
+
details: str,
|
|
115
|
+
initial_metadata: Metadata = (),
|
|
116
|
+
trailing_metadata: Metadata = (),
|
|
117
|
+
) -> None:
|
|
118
|
+
super().__init__(details)
|
|
119
|
+
self._code = code
|
|
120
|
+
self._details = details
|
|
121
|
+
self._initial_metadata = initial_metadata
|
|
122
|
+
self._trailing_metadata = trailing_metadata
|
|
123
|
+
|
|
124
|
+
def code(self) -> grpc.StatusCode:
|
|
125
|
+
return self._code
|
|
126
|
+
|
|
127
|
+
def details(self) -> str:
|
|
128
|
+
return self._details
|
|
129
|
+
|
|
130
|
+
def initial_metadata(self) -> Metadata:
|
|
131
|
+
return self._initial_metadata
|
|
132
|
+
|
|
133
|
+
def trailing_metadata(self) -> Metadata:
|
|
134
|
+
return self._trailing_metadata
|
|
135
|
+
|
|
136
|
+
def debug_error_string(self) -> str:
|
|
137
|
+
return f"in-process call failed: {self._code.name}: {self._details}"
|
|
138
|
+
|
|
139
|
+
def is_active(self) -> bool:
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
def time_remaining(self) -> Optional[float]:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
def cancel(self) -> bool:
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
def add_callback(self, callback: Callable[[], None]) -> bool:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
# -- grpc.Future, already done -------------------------------------------------
|
|
152
|
+
|
|
153
|
+
def cancelled(self) -> bool:
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def running(self) -> bool:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
def done(self) -> bool:
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
def result(self, timeout: Optional[float] = None) -> Any:
|
|
163
|
+
raise self
|
|
164
|
+
|
|
165
|
+
def exception(self, timeout: Optional[float] = None) -> Optional[Exception]:
|
|
166
|
+
return self
|
|
167
|
+
|
|
168
|
+
def traceback(self, timeout: Optional[float] = None) -> Any:
|
|
169
|
+
return self.__traceback__
|
|
170
|
+
|
|
171
|
+
def add_done_callback(self, fn: Callable[[Any], None]) -> None:
|
|
172
|
+
fn(self)
|
|
173
|
+
|
|
174
|
+
def __str__(self) -> str:
|
|
175
|
+
return (
|
|
176
|
+
f"<{type(self).__name__} of RPC that terminated with:\n"
|
|
177
|
+
f'\tstatus = {self._code}\n\tdetails = "{self._details}"\n>'
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
__repr__ = __str__
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class _Aborted(Exception):
|
|
184
|
+
"""Raised by :meth:`_ServicerContext.abort`; grpc raises a bare ``Exception``."""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class _ServicerContext(grpc.ServicerContext):
|
|
188
|
+
"""The server half of one call: what a servicer can see of grpc's own context."""
|
|
189
|
+
|
|
190
|
+
def __init__(self, metadata: Metadata, deadline: Optional[float]) -> None:
|
|
191
|
+
self._invocation_metadata = metadata
|
|
192
|
+
self._deadline = deadline
|
|
193
|
+
self._lock = threading.Lock()
|
|
194
|
+
self.initial_metadata: Optional[Metadata] = None
|
|
195
|
+
self.trailing: Optional[Metadata] = None
|
|
196
|
+
self.status_code: Optional[grpc.StatusCode] = None
|
|
197
|
+
self.status_details: Optional[str] = None
|
|
198
|
+
self.aborted = False
|
|
199
|
+
self._callbacks: List[Callable[[], None]] = []
|
|
200
|
+
self._active = True
|
|
201
|
+
|
|
202
|
+
# -- grpc.RpcContext -----------------------------------------------------
|
|
203
|
+
|
|
204
|
+
def is_active(self) -> bool:
|
|
205
|
+
return self._active and (self._deadline is None or time.time() < self._deadline)
|
|
206
|
+
|
|
207
|
+
def time_remaining(self) -> Optional[float]:
|
|
208
|
+
if self._deadline is None:
|
|
209
|
+
return None
|
|
210
|
+
return max(self._deadline - time.time(), 0.0)
|
|
211
|
+
|
|
212
|
+
def cancel(self) -> None:
|
|
213
|
+
with self._lock:
|
|
214
|
+
self.status_code = grpc.StatusCode.CANCELLED
|
|
215
|
+
self.status_details = "Cancelled"
|
|
216
|
+
self.aborted = True
|
|
217
|
+
raise _Aborted()
|
|
218
|
+
|
|
219
|
+
def add_callback(self, callback: Callable[[], None]) -> bool:
|
|
220
|
+
with self._lock:
|
|
221
|
+
if not self._active:
|
|
222
|
+
return False
|
|
223
|
+
self._callbacks.append(callback)
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
# -- grpc.ServicerContext --------------------------------------------------
|
|
227
|
+
|
|
228
|
+
def invocation_metadata(self) -> Metadata:
|
|
229
|
+
return self._invocation_metadata
|
|
230
|
+
|
|
231
|
+
def peer(self) -> str:
|
|
232
|
+
return PEER
|
|
233
|
+
|
|
234
|
+
def peer_identities(self) -> Optional[Iterable[bytes]]:
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
def peer_identity_key(self) -> Optional[str]:
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
def auth_context(self) -> Dict[str, Iterable[bytes]]:
|
|
241
|
+
return {}
|
|
242
|
+
|
|
243
|
+
def set_compression(self, compression: grpc.Compression) -> None:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
def disable_next_message_compression(self) -> None:
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
def send_initial_metadata(
|
|
250
|
+
self, initial_metadata: Iterable[Tuple[str, Any]]
|
|
251
|
+
) -> None:
|
|
252
|
+
with self._lock:
|
|
253
|
+
if self.initial_metadata is not None:
|
|
254
|
+
raise ValueError("Initial metadata no longer allowed!")
|
|
255
|
+
self.initial_metadata = _metadata(initial_metadata)
|
|
256
|
+
|
|
257
|
+
def set_trailing_metadata(
|
|
258
|
+
self, trailing_metadata: Iterable[Tuple[str, Any]]
|
|
259
|
+
) -> None:
|
|
260
|
+
with self._lock:
|
|
261
|
+
self.trailing = _metadata(trailing_metadata)
|
|
262
|
+
|
|
263
|
+
def trailing_metadata(self) -> Optional[Metadata]:
|
|
264
|
+
return self.trailing
|
|
265
|
+
|
|
266
|
+
def abort(self, code: grpc.StatusCode, details: str) -> None:
|
|
267
|
+
if code == grpc.StatusCode.OK:
|
|
268
|
+
logger.error("abort() called with StatusCode.OK; returning UNKNOWN")
|
|
269
|
+
code, details = grpc.StatusCode.UNKNOWN, ""
|
|
270
|
+
with self._lock:
|
|
271
|
+
self.status_code = code
|
|
272
|
+
self.status_details = details
|
|
273
|
+
self.aborted = True
|
|
274
|
+
raise _Aborted()
|
|
275
|
+
|
|
276
|
+
def abort_with_status(self, status: grpc.Status) -> None:
|
|
277
|
+
self.trailing = _metadata(status.trailing_metadata)
|
|
278
|
+
self.abort(status.code, status.details)
|
|
279
|
+
|
|
280
|
+
def set_code(self, code: grpc.StatusCode) -> None:
|
|
281
|
+
with self._lock:
|
|
282
|
+
self.status_code = code
|
|
283
|
+
|
|
284
|
+
def code(self) -> Optional[grpc.StatusCode]:
|
|
285
|
+
return self.status_code
|
|
286
|
+
|
|
287
|
+
def set_details(self, details: str) -> None:
|
|
288
|
+
with self._lock:
|
|
289
|
+
self.status_details = details
|
|
290
|
+
|
|
291
|
+
def details(self) -> Optional[str]:
|
|
292
|
+
return self.status_details
|
|
293
|
+
|
|
294
|
+
# -- the call's end ----------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
def finish(self) -> None:
|
|
297
|
+
with self._lock:
|
|
298
|
+
self._active = False
|
|
299
|
+
callbacks, self._callbacks = self._callbacks, []
|
|
300
|
+
for callback in callbacks:
|
|
301
|
+
try:
|
|
302
|
+
callback()
|
|
303
|
+
except Exception: # noqa: BLE001 - grpc logs and carries on too
|
|
304
|
+
logger.exception("a call's termination callback raised")
|
|
305
|
+
|
|
306
|
+
def failure(self, code: grpc.StatusCode, details: str) -> InProcessRpcError:
|
|
307
|
+
"""The client's error for a call that ended here; the context's own status wins."""
|
|
308
|
+
return InProcessRpcError(
|
|
309
|
+
self.status_code or code,
|
|
310
|
+
details if self.status_details is None else self.status_details,
|
|
311
|
+
self.initial_metadata or (),
|
|
312
|
+
self.trailing or (),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class _Call(grpc.Call):
|
|
317
|
+
"""A completed call's outcome as ``with_call`` hands it back."""
|
|
318
|
+
|
|
319
|
+
def __init__(self, initial_metadata: Metadata, trailing_metadata: Metadata) -> None:
|
|
320
|
+
self._initial_metadata = initial_metadata
|
|
321
|
+
self._trailing_metadata = trailing_metadata
|
|
322
|
+
|
|
323
|
+
def initial_metadata(self) -> Metadata:
|
|
324
|
+
return self._initial_metadata
|
|
325
|
+
|
|
326
|
+
def trailing_metadata(self) -> Metadata:
|
|
327
|
+
return self._trailing_metadata
|
|
328
|
+
|
|
329
|
+
def code(self) -> grpc.StatusCode:
|
|
330
|
+
return grpc.StatusCode.OK
|
|
331
|
+
|
|
332
|
+
def details(self) -> str:
|
|
333
|
+
return ""
|
|
334
|
+
|
|
335
|
+
def is_active(self) -> bool:
|
|
336
|
+
return False
|
|
337
|
+
|
|
338
|
+
def time_remaining(self) -> Optional[float]:
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
def cancel(self) -> bool:
|
|
342
|
+
return False
|
|
343
|
+
|
|
344
|
+
def add_callback(self, callback: Callable[[], None]) -> bool:
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class _Future(grpc.Future, grpc.Call):
|
|
349
|
+
"""``future()``'s answer: already done, since the call ran on this thread."""
|
|
350
|
+
|
|
351
|
+
def __init__(
|
|
352
|
+
self, response: Any, call: Optional[_Call], error: Optional[InProcessRpcError]
|
|
353
|
+
):
|
|
354
|
+
self._response = response
|
|
355
|
+
self._call = call
|
|
356
|
+
self._error = error
|
|
357
|
+
|
|
358
|
+
def cancel(self) -> bool:
|
|
359
|
+
return False
|
|
360
|
+
|
|
361
|
+
def cancelled(self) -> bool:
|
|
362
|
+
return False
|
|
363
|
+
|
|
364
|
+
def running(self) -> bool:
|
|
365
|
+
return False
|
|
366
|
+
|
|
367
|
+
def done(self) -> bool:
|
|
368
|
+
return True
|
|
369
|
+
|
|
370
|
+
def result(self, timeout: Optional[float] = None) -> Any:
|
|
371
|
+
if self._error is not None:
|
|
372
|
+
raise self._error
|
|
373
|
+
return self._response
|
|
374
|
+
|
|
375
|
+
def exception(self, timeout: Optional[float] = None) -> Optional[Exception]:
|
|
376
|
+
return self._error
|
|
377
|
+
|
|
378
|
+
def traceback(self, timeout: Optional[float] = None) -> Any:
|
|
379
|
+
return None if self._error is None else self._error.__traceback__
|
|
380
|
+
|
|
381
|
+
def add_done_callback(self, fn: Callable[[Any], None]) -> None:
|
|
382
|
+
fn(self)
|
|
383
|
+
|
|
384
|
+
def _outcome(self) -> grpc.Call:
|
|
385
|
+
return self._error if self._error is not None else self._call # type: ignore[return-value]
|
|
386
|
+
|
|
387
|
+
def initial_metadata(self) -> Metadata:
|
|
388
|
+
return self._outcome().initial_metadata()
|
|
389
|
+
|
|
390
|
+
def trailing_metadata(self) -> Metadata:
|
|
391
|
+
return self._outcome().trailing_metadata()
|
|
392
|
+
|
|
393
|
+
def code(self) -> grpc.StatusCode:
|
|
394
|
+
return self._outcome().code()
|
|
395
|
+
|
|
396
|
+
def details(self) -> str:
|
|
397
|
+
return self._outcome().details()
|
|
398
|
+
|
|
399
|
+
def is_active(self) -> bool:
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
def time_remaining(self) -> Optional[float]:
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
def add_callback(self, callback: Callable[[], None]) -> bool:
|
|
406
|
+
return False
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class _UnaryUnary(grpc.UnaryUnaryMultiCallable):
|
|
410
|
+
def __init__(
|
|
411
|
+
self,
|
|
412
|
+
channel: "InProcessChannel",
|
|
413
|
+
method: str,
|
|
414
|
+
request_serializer: Optional[Callable[[Any], bytes]],
|
|
415
|
+
response_deserializer: Optional[Callable[[bytes], Any]],
|
|
416
|
+
) -> None:
|
|
417
|
+
self._channel = channel
|
|
418
|
+
self._method = method
|
|
419
|
+
self._serialize = request_serializer
|
|
420
|
+
self._deserialize = response_deserializer
|
|
421
|
+
|
|
422
|
+
def __call__(
|
|
423
|
+
self,
|
|
424
|
+
request: Any,
|
|
425
|
+
timeout: Optional[float] = None,
|
|
426
|
+
metadata: Optional[Sequence[Tuple[str, Any]]] = None,
|
|
427
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
428
|
+
wait_for_ready: Optional[bool] = None,
|
|
429
|
+
compression: Optional[grpc.Compression] = None,
|
|
430
|
+
) -> Any:
|
|
431
|
+
response, _ = self.with_call(request, timeout, metadata)
|
|
432
|
+
return response
|
|
433
|
+
|
|
434
|
+
def with_call(
|
|
435
|
+
self,
|
|
436
|
+
request: Any,
|
|
437
|
+
timeout: Optional[float] = None,
|
|
438
|
+
metadata: Optional[Sequence[Tuple[str, Any]]] = None,
|
|
439
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
440
|
+
wait_for_ready: Optional[bool] = None,
|
|
441
|
+
compression: Optional[grpc.Compression] = None,
|
|
442
|
+
) -> Tuple[Any, grpc.Call]:
|
|
443
|
+
return self._channel._invoke(
|
|
444
|
+
self._method, request, self._serialize, self._deserialize, timeout, metadata
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def future(
|
|
448
|
+
self,
|
|
449
|
+
request: Any,
|
|
450
|
+
timeout: Optional[float] = None,
|
|
451
|
+
metadata: Optional[Sequence[Tuple[str, Any]]] = None,
|
|
452
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
453
|
+
wait_for_ready: Optional[bool] = None,
|
|
454
|
+
compression: Optional[grpc.Compression] = None,
|
|
455
|
+
) -> _Future:
|
|
456
|
+
try:
|
|
457
|
+
response, call = self.with_call(request, timeout, metadata)
|
|
458
|
+
except InProcessRpcError as error:
|
|
459
|
+
return _Future(None, None, error)
|
|
460
|
+
return _Future(response, call, None) # type: ignore[arg-type]
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class _HandlerCallDetails(
|
|
464
|
+
collections.namedtuple("_HandlerCallDetails", ("method", "invocation_metadata")),
|
|
465
|
+
grpc.HandlerCallDetails,
|
|
466
|
+
):
|
|
467
|
+
pass
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _option(options: Sequence[Tuple[str, Any]], name: str, default: int) -> int:
|
|
471
|
+
value = dict(options).get(name, default)
|
|
472
|
+
return int(value) if int(value) >= 0 else 2**63
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class InProcessChannel(grpc.Channel):
|
|
476
|
+
"""A :class:`grpc.Channel` whose unary calls run the registered servicers here.
|
|
477
|
+
|
|
478
|
+
It is its own server too: a servicer registers on it with the generated
|
|
479
|
+
``add_*_to_server(servicer, channel)``, as on a :class:`grpc.Server`.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
options: Channel options; only the message size limits are read
|
|
483
|
+
(``grpc.max_send_message_length``, ``grpc.max_receive_message_length``),
|
|
484
|
+
:func:`~carla_driver_interface.protocol.channel_options` by default,
|
|
485
|
+
as both ends of this contract use.
|
|
486
|
+
"""
|
|
487
|
+
|
|
488
|
+
def __init__(self, options: Optional[Sequence[Tuple[str, Any]]] = None) -> None:
|
|
489
|
+
options = channel_options() if options is None else list(options)
|
|
490
|
+
self._max_send = _option(
|
|
491
|
+
options, "grpc.max_send_message_length", MAX_MESSAGE_BYTES
|
|
492
|
+
)
|
|
493
|
+
self._max_receive = _option(
|
|
494
|
+
options, "grpc.max_receive_message_length", MAX_MESSAGE_BYTES
|
|
495
|
+
)
|
|
496
|
+
self._generic_handlers: List[grpc.GenericRpcHandler] = []
|
|
497
|
+
self._registered: Dict[str, grpc.RpcMethodHandler] = {}
|
|
498
|
+
self._closed = False
|
|
499
|
+
|
|
500
|
+
# -- the server half ---------------------------------------------------------
|
|
501
|
+
|
|
502
|
+
def add_generic_rpc_handlers(
|
|
503
|
+
self, generic_rpc_handlers: Iterable[grpc.GenericRpcHandler]
|
|
504
|
+
) -> None:
|
|
505
|
+
"""Register handlers, as :meth:`grpc.Server.add_generic_rpc_handlers` does."""
|
|
506
|
+
self._generic_handlers.extend(generic_rpc_handlers)
|
|
507
|
+
|
|
508
|
+
def add_registered_method_handlers(
|
|
509
|
+
self, service_name: str, method_handlers: Dict[str, grpc.RpcMethodHandler]
|
|
510
|
+
) -> None:
|
|
511
|
+
"""Register handlers by method, as newer generated code also does on a server."""
|
|
512
|
+
for name, handler in method_handlers.items():
|
|
513
|
+
self._registered[f"/{service_name}/{name}"] = handler
|
|
514
|
+
|
|
515
|
+
def _handler(
|
|
516
|
+
self, method: str, metadata: Metadata
|
|
517
|
+
) -> Optional[grpc.RpcMethodHandler]:
|
|
518
|
+
if method in self._registered:
|
|
519
|
+
return self._registered[method]
|
|
520
|
+
details = _HandlerCallDetails(method, metadata)
|
|
521
|
+
for generic in self._generic_handlers:
|
|
522
|
+
handler = generic.service(details)
|
|
523
|
+
if handler is not None:
|
|
524
|
+
return handler
|
|
525
|
+
return None
|
|
526
|
+
|
|
527
|
+
# -- one call ----------------------------------------------------------------
|
|
528
|
+
|
|
529
|
+
def _invoke(
|
|
530
|
+
self,
|
|
531
|
+
method: str,
|
|
532
|
+
request: Any,
|
|
533
|
+
serialize: Optional[Callable[[Any], bytes]],
|
|
534
|
+
deserialize: Optional[Callable[[bytes], Any]],
|
|
535
|
+
timeout: Optional[float],
|
|
536
|
+
metadata: Optional[Sequence[Tuple[str, Any]]],
|
|
537
|
+
) -> Tuple[Any, grpc.Call]:
|
|
538
|
+
if self._closed:
|
|
539
|
+
raise ValueError("Cannot invoke RPC on closed channel!")
|
|
540
|
+
invocation = _metadata(metadata)
|
|
541
|
+
started = time.time()
|
|
542
|
+
# grpc's Python client reads timeout=0 as no deadline at all.
|
|
543
|
+
deadline = started + timeout if timeout else None
|
|
544
|
+
if timeout is not None and timeout < 0:
|
|
545
|
+
raise InProcessRpcError(
|
|
546
|
+
grpc.StatusCode.DEADLINE_EXCEEDED, "Deadline Exceeded"
|
|
547
|
+
)
|
|
548
|
+
try:
|
|
549
|
+
request_bytes = request if serialize is None else serialize(request)
|
|
550
|
+
except Exception: # noqa: BLE001 - grpc reports any serializer failure
|
|
551
|
+
raise InProcessRpcError(
|
|
552
|
+
grpc.StatusCode.INTERNAL, "Exception serializing request!"
|
|
553
|
+
) from None
|
|
554
|
+
if len(request_bytes) > self._max_send:
|
|
555
|
+
raise InProcessRpcError(
|
|
556
|
+
grpc.StatusCode.RESOURCE_EXHAUSTED,
|
|
557
|
+
f"CLIENT: Sent message larger than max ({len(request_bytes)} vs. "
|
|
558
|
+
f"{self._max_send})",
|
|
559
|
+
)
|
|
560
|
+
handler = self._handler(method, invocation)
|
|
561
|
+
if handler is None:
|
|
562
|
+
raise InProcessRpcError(grpc.StatusCode.UNIMPLEMENTED, "Method not found!")
|
|
563
|
+
if handler.unary_unary is None:
|
|
564
|
+
raise NotImplementedError(
|
|
565
|
+
f"{method} is not unary-unary; the in-process channel serves unary calls only"
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
try:
|
|
569
|
+
response_bytes = self._serve(handler, request_bytes, invocation, deadline)
|
|
570
|
+
except InProcessRpcError:
|
|
571
|
+
# Over the wire the client has given up by the time a late abort is
|
|
572
|
+
# sent, so it sees DEADLINE_EXCEEDED, not the servicer's status.
|
|
573
|
+
if deadline is not None and time.time() > deadline:
|
|
574
|
+
raise InProcessRpcError(
|
|
575
|
+
grpc.StatusCode.DEADLINE_EXCEEDED, "Deadline Exceeded"
|
|
576
|
+
) from None
|
|
577
|
+
raise
|
|
578
|
+
if deadline is not None and time.time() > deadline:
|
|
579
|
+
# The servicer ran to the end on this thread; the client gave up.
|
|
580
|
+
raise InProcessRpcError(
|
|
581
|
+
grpc.StatusCode.DEADLINE_EXCEEDED, "Deadline Exceeded"
|
|
582
|
+
)
|
|
583
|
+
context, payload = response_bytes
|
|
584
|
+
initial = context.initial_metadata or ()
|
|
585
|
+
trailing = context.trailing or ()
|
|
586
|
+
if len(payload) > self._max_receive:
|
|
587
|
+
raise InProcessRpcError(
|
|
588
|
+
grpc.StatusCode.RESOURCE_EXHAUSTED,
|
|
589
|
+
f"CLIENT: Received message larger than max ({len(payload)} vs. "
|
|
590
|
+
f"{self._max_receive})",
|
|
591
|
+
initial,
|
|
592
|
+
trailing,
|
|
593
|
+
)
|
|
594
|
+
try:
|
|
595
|
+
response = payload if deserialize is None else deserialize(payload)
|
|
596
|
+
except Exception: # noqa: BLE001
|
|
597
|
+
raise InProcessRpcError(
|
|
598
|
+
grpc.StatusCode.INTERNAL,
|
|
599
|
+
"Exception deserializing response!",
|
|
600
|
+
initial,
|
|
601
|
+
trailing,
|
|
602
|
+
) from None
|
|
603
|
+
return response, _Call(initial, trailing)
|
|
604
|
+
|
|
605
|
+
def _serve(
|
|
606
|
+
self,
|
|
607
|
+
handler: grpc.RpcMethodHandler,
|
|
608
|
+
request_bytes: bytes,
|
|
609
|
+
invocation: Metadata,
|
|
610
|
+
deadline: Optional[float],
|
|
611
|
+
) -> Tuple[_ServicerContext, bytes]:
|
|
612
|
+
"""The server's half: parse, call, serialize; an error ends it as grpc's would."""
|
|
613
|
+
context = _ServicerContext(invocation, deadline)
|
|
614
|
+
try:
|
|
615
|
+
try:
|
|
616
|
+
request = (
|
|
617
|
+
request_bytes
|
|
618
|
+
if handler.request_deserializer is None
|
|
619
|
+
else handler.request_deserializer(request_bytes)
|
|
620
|
+
)
|
|
621
|
+
except Exception: # noqa: BLE001
|
|
622
|
+
raise context.failure(
|
|
623
|
+
grpc.StatusCode.INTERNAL, "Exception deserializing request!"
|
|
624
|
+
) from None
|
|
625
|
+
try:
|
|
626
|
+
response = handler.unary_unary(request, context)
|
|
627
|
+
except Exception as exception: # noqa: BLE001 - as grpc's server
|
|
628
|
+
if context.aborted:
|
|
629
|
+
raise context.failure(
|
|
630
|
+
grpc.StatusCode.UNKNOWN, "RPC Aborted"
|
|
631
|
+
) from None
|
|
632
|
+
details = f"Exception calling application: {exception}"
|
|
633
|
+
logger.exception(details)
|
|
634
|
+
raise context.failure(grpc.StatusCode.UNKNOWN, details) from None
|
|
635
|
+
if context.aborted:
|
|
636
|
+
# A servicer that swallowed its own abort still aborted.
|
|
637
|
+
raise context.failure(grpc.StatusCode.UNKNOWN, "RPC Aborted")
|
|
638
|
+
try:
|
|
639
|
+
payload = (
|
|
640
|
+
response
|
|
641
|
+
if handler.response_serializer is None
|
|
642
|
+
else handler.response_serializer(response)
|
|
643
|
+
)
|
|
644
|
+
except Exception: # noqa: BLE001
|
|
645
|
+
payload = None
|
|
646
|
+
if payload is None:
|
|
647
|
+
raise context.failure(
|
|
648
|
+
grpc.StatusCode.INTERNAL, "Failed to serialize response!"
|
|
649
|
+
)
|
|
650
|
+
if context.status_code not in (None, grpc.StatusCode.OK):
|
|
651
|
+
raise context.failure(context.status_code, "")
|
|
652
|
+
return context, payload
|
|
653
|
+
finally:
|
|
654
|
+
context.finish()
|
|
655
|
+
|
|
656
|
+
# -- grpc.Channel ------------------------------------------------------------
|
|
657
|
+
|
|
658
|
+
def unary_unary(
|
|
659
|
+
self,
|
|
660
|
+
method: str,
|
|
661
|
+
request_serializer: Optional[Callable[[Any], bytes]] = None,
|
|
662
|
+
response_deserializer: Optional[Callable[[bytes], Any]] = None,
|
|
663
|
+
_registered_method: Optional[bool] = False,
|
|
664
|
+
) -> grpc.UnaryUnaryMultiCallable:
|
|
665
|
+
return _UnaryUnary(self, method, request_serializer, response_deserializer)
|
|
666
|
+
|
|
667
|
+
def unary_stream(self, method: str, *args: Any, **kwargs: Any) -> Any:
|
|
668
|
+
raise NotImplementedError("the in-process channel serves unary calls only")
|
|
669
|
+
|
|
670
|
+
def stream_unary(self, method: str, *args: Any, **kwargs: Any) -> Any:
|
|
671
|
+
raise NotImplementedError("the in-process channel serves unary calls only")
|
|
672
|
+
|
|
673
|
+
def stream_stream(self, method: str, *args: Any, **kwargs: Any) -> Any:
|
|
674
|
+
raise NotImplementedError("the in-process channel serves unary calls only")
|
|
675
|
+
|
|
676
|
+
def subscribe(
|
|
677
|
+
self,
|
|
678
|
+
callback: Callable[[grpc.ChannelConnectivity], None],
|
|
679
|
+
try_to_connect: bool = False,
|
|
680
|
+
) -> None:
|
|
681
|
+
callback(grpc.ChannelConnectivity.READY)
|
|
682
|
+
|
|
683
|
+
def unsubscribe(self, callback: Callable[[grpc.ChannelConnectivity], None]) -> None:
|
|
684
|
+
pass
|
|
685
|
+
|
|
686
|
+
def close(self) -> None:
|
|
687
|
+
self._closed = True
|
|
688
|
+
|
|
689
|
+
def __enter__(self) -> "InProcessChannel":
|
|
690
|
+
return self
|
|
691
|
+
|
|
692
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
693
|
+
self.close()
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def in_process_channel(
|
|
697
|
+
driver: BaseDriver, mode: ServiceMode = "carla"
|
|
698
|
+
) -> InProcessChannel:
|
|
699
|
+
"""A channel serving ``driver`` in this process, as :func:`~carla_driver_interface.server.build_server` serves it.
|
|
700
|
+
|
|
701
|
+
The same :class:`~carla_driver_interface.service.EgodriverServicer`, registered
|
|
702
|
+
for both services a server carries (``egodriver.EgodriverService`` and
|
|
703
|
+
``carla_driver.v0.CarlaVehicleService``).
|
|
704
|
+
"""
|
|
705
|
+
channel = InProcessChannel(channel_options())
|
|
706
|
+
servicer = EgodriverServicer(driver, mode)
|
|
707
|
+
add_EgodriverServiceServicer_to_server(servicer, channel)
|
|
708
|
+
add_CarlaVehicleServiceServicer_to_server(servicer, channel)
|
|
709
|
+
return channel
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/server.py
RENAMED
|
@@ -21,7 +21,17 @@ from .service import EgodriverServicer, ServiceMode
|
|
|
21
21
|
|
|
22
22
|
logger = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
|
-
__all__ = ["build_server", "run_server", "serving"]
|
|
24
|
+
__all__ = ["UNIX_SCHEMES", "bind_address", "build_server", "run_server", "serving"]
|
|
25
|
+
|
|
26
|
+
#: gRPC address schemes naming a Unix domain socket rather than a TCP host.
|
|
27
|
+
UNIX_SCHEMES = ("unix:", "unix-abstract:")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def bind_address(host: str, port: int) -> str:
|
|
31
|
+
"""The address :func:`build_server` binds: ``host:port``, or ``host`` itself
|
|
32
|
+
when it names a Unix domain socket (``unix:///run/policy.sock``,
|
|
33
|
+
``unix:relative.sock``, ``unix-abstract:name``), which has no port."""
|
|
34
|
+
return host if host.startswith(UNIX_SCHEMES) else f"{host}:{port}"
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
def build_server(
|
|
@@ -36,6 +46,10 @@ def build_server(
|
|
|
36
46
|
Returns the server and the bound port, which differs from ``port`` when ``port``
|
|
37
47
|
is 0 -- useful for tests. ``mode`` names the runtime on the other end
|
|
38
48
|
(:class:`~carla_driver_interface.service.EgodriverServicer`).
|
|
49
|
+
|
|
50
|
+
``host`` may instead be a Unix domain socket address (``unix:///path``, see
|
|
51
|
+
:func:`bind_address`); ``port`` is then unused and the bound port is 0. A
|
|
52
|
+
client dials the same address.
|
|
39
53
|
"""
|
|
40
54
|
server = grpc.server(
|
|
41
55
|
futures.ThreadPoolExecutor(max_workers=max_workers), options=channel_options()
|
|
@@ -44,10 +58,15 @@ def build_server(
|
|
|
44
58
|
add_EgodriverServiceServicer_to_server(servicer, server)
|
|
45
59
|
# The vehicle command the egodriver contract has no field for.
|
|
46
60
|
add_CarlaVehicleServiceServicer_to_server(servicer, server)
|
|
47
|
-
|
|
61
|
+
address = bind_address(host, port)
|
|
62
|
+
try:
|
|
63
|
+
bound_port = server.add_insecure_port(address)
|
|
64
|
+
except RuntimeError:
|
|
65
|
+
bound_port = 0
|
|
48
66
|
if bound_port == 0:
|
|
49
|
-
raise RuntimeError(f"failed to bind {
|
|
50
|
-
|
|
67
|
+
raise RuntimeError(f"failed to bind {address}")
|
|
68
|
+
# A Unix socket has no port; grpc answers 1 for one that bound.
|
|
69
|
+
return server, 0 if host.startswith(UNIX_SCHEMES) else bound_port
|
|
51
70
|
|
|
52
71
|
|
|
53
72
|
@contextmanager
|
|
@@ -58,13 +77,17 @@ def serving(
|
|
|
58
77
|
max_workers: int = 8,
|
|
59
78
|
mode: ServiceMode = "carla",
|
|
60
79
|
) -> Iterator[int]:
|
|
61
|
-
"""Run a server for ``driver`` for the duration of the block; yields its port
|
|
80
|
+
"""Run a server for ``driver`` for the duration of the block; yields its port
|
|
81
|
+
(0 on a Unix socket, :func:`build_server`)."""
|
|
62
82
|
server, bound_port = build_server(
|
|
63
83
|
driver, port=port, host=host, max_workers=max_workers, mode=mode
|
|
64
84
|
)
|
|
65
85
|
server.start()
|
|
66
86
|
logger.info(
|
|
67
|
-
"policy %r serving on %s
|
|
87
|
+
"policy %r serving on %s (%s mode)",
|
|
88
|
+
driver.name,
|
|
89
|
+
bind_address(host, bound_port),
|
|
90
|
+
mode,
|
|
68
91
|
)
|
|
69
92
|
try:
|
|
70
93
|
yield bound_port
|
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/alpasim.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/contract.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/driver.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/geometry.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/hdmap.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/polyline.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/protocol.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/py.typed
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/service.py
RENAMED
|
File without changes
|
{carla_driver_interface-2.1.0 → carla_driver_interface-2.2.0}/src/carla_driver_interface/testing.py
RENAMED
|
File without changes
|