robotcode-jsonrpc2 2.4.0__tar.gz → 2.5.1__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.
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/.gitignore +5 -1
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/PKG-INFO +1 -1
- robotcode_jsonrpc2-2.5.1/src/robotcode/jsonrpc2/__version__.py +1 -0
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/src/robotcode/jsonrpc2/server.py +113 -12
- robotcode_jsonrpc2-2.4.0/src/robotcode/jsonrpc2/__version__.py +0 -1
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/README.md +0 -0
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/pyproject.toml +0 -0
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/src/robotcode/jsonrpc2/__init__.py +0 -0
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/src/robotcode/jsonrpc2/protocol.py +0 -0
- {robotcode_jsonrpc2-2.4.0 → robotcode_jsonrpc2-2.5.1}/src/robotcode/jsonrpc2/py.typed +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.5.1"
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import asyncio
|
|
3
3
|
import io
|
|
4
|
+
import logging
|
|
4
5
|
import sys
|
|
5
6
|
import threading
|
|
7
|
+
import weakref
|
|
6
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
7
9
|
from types import TracebackType
|
|
8
10
|
from typing import (
|
|
@@ -50,6 +52,18 @@ class StdOutTransportAdapter(asyncio.Transport):
|
|
|
50
52
|
self.wfile.flush()
|
|
51
53
|
|
|
52
54
|
|
|
55
|
+
class _JsonRPCServerState:
|
|
56
|
+
__slots__ = ("closed", "in_closing", "logger", "loop", "server", "stdio_stop_event")
|
|
57
|
+
|
|
58
|
+
def __init__(self, loop: asyncio.AbstractEventLoop, logger: logging.Logger) -> None:
|
|
59
|
+
self.logger = logger
|
|
60
|
+
self.loop = loop
|
|
61
|
+
self.server: Optional[asyncio.AbstractServer] = None
|
|
62
|
+
self.stdio_stop_event: Optional[threading.Event] = None
|
|
63
|
+
self.in_closing = False
|
|
64
|
+
self.closed = False
|
|
65
|
+
|
|
66
|
+
|
|
53
67
|
class JsonRPCServer(Generic[TProtocol], abc.ABC):
|
|
54
68
|
_logger = LoggingDescriptor()
|
|
55
69
|
|
|
@@ -65,24 +79,78 @@ class JsonRPCServer(Generic[TProtocol], abc.ABC):
|
|
|
65
79
|
|
|
66
80
|
self._run_func: Optional[Callable[[], None]] = None
|
|
67
81
|
self._serve_func: Optional[Callable[[], Coroutine[None, None, None]]] = None
|
|
68
|
-
self._server: Optional[asyncio.AbstractServer] = None
|
|
69
|
-
|
|
70
|
-
self._stdio_stop_event: Optional[threading.Event] = None
|
|
71
|
-
|
|
72
|
-
self._in_closing = False
|
|
73
|
-
self._closed = False
|
|
74
82
|
|
|
75
83
|
try:
|
|
76
|
-
|
|
84
|
+
loop = asyncio.get_event_loop()
|
|
77
85
|
except RuntimeError:
|
|
78
|
-
|
|
79
|
-
asyncio.set_event_loop(
|
|
86
|
+
loop = asyncio.new_event_loop()
|
|
87
|
+
asyncio.set_event_loop(loop)
|
|
88
|
+
|
|
89
|
+
self._state = _JsonRPCServerState(
|
|
90
|
+
loop,
|
|
91
|
+
logging.getLogger(f"{type(self).__module__}.{type(self).__qualname__}"),
|
|
92
|
+
)
|
|
93
|
+
self._finalizer = weakref.finalize(self, JsonRPCServer._finalize_resources, self._state)
|
|
80
94
|
|
|
81
95
|
if self.loop is not None:
|
|
82
96
|
self.loop.slow_callback_duration = 10
|
|
83
97
|
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
@staticmethod
|
|
99
|
+
def _finalize_resources(state: _JsonRPCServerState) -> None:
|
|
100
|
+
if state.closed:
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
if state.stdio_stop_event is not None:
|
|
104
|
+
state.stdio_stop_event.set()
|
|
105
|
+
|
|
106
|
+
if state.server is not None:
|
|
107
|
+
try:
|
|
108
|
+
if not state.loop.is_closed() and state.loop.is_running():
|
|
109
|
+
state.loop.call_soon_threadsafe(state.server.close)
|
|
110
|
+
else:
|
|
111
|
+
state.server.close()
|
|
112
|
+
except BaseException:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
state.logger.debug(
|
|
116
|
+
"JsonRPCServer was garbage collected without calling close(); resources were cleaned up best-effort only.",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def loop(self) -> asyncio.AbstractEventLoop:
|
|
121
|
+
return self._state.loop
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def _server(self) -> Optional[asyncio.AbstractServer]:
|
|
125
|
+
return self._state.server
|
|
126
|
+
|
|
127
|
+
@_server.setter
|
|
128
|
+
def _server(self, value: Optional[asyncio.AbstractServer]) -> None:
|
|
129
|
+
self._state.server = value
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def _stdio_stop_event(self) -> Optional[threading.Event]:
|
|
133
|
+
return self._state.stdio_stop_event
|
|
134
|
+
|
|
135
|
+
@_stdio_stop_event.setter
|
|
136
|
+
def _stdio_stop_event(self, value: Optional[threading.Event]) -> None:
|
|
137
|
+
self._state.stdio_stop_event = value
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def _in_closing(self) -> bool:
|
|
141
|
+
return self._state.in_closing
|
|
142
|
+
|
|
143
|
+
@_in_closing.setter
|
|
144
|
+
def _in_closing(self, value: bool) -> None:
|
|
145
|
+
self._state.in_closing = value
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def _closed(self) -> bool:
|
|
149
|
+
return self._state.closed
|
|
150
|
+
|
|
151
|
+
@_closed.setter
|
|
152
|
+
def _closed(self, value: bool) -> None:
|
|
153
|
+
self._state.closed = value
|
|
86
154
|
|
|
87
155
|
@_logger.call
|
|
88
156
|
def start(self) -> None:
|
|
@@ -122,6 +190,36 @@ class JsonRPCServer(Generic[TProtocol], abc.ABC):
|
|
|
122
190
|
if self._server and self._server.is_serving():
|
|
123
191
|
self._server.close()
|
|
124
192
|
|
|
193
|
+
def _close_on_loop_thread(self) -> None:
|
|
194
|
+
if self.loop.is_running() and getattr(self.loop, "_thread_id", None) != threading.get_ident():
|
|
195
|
+
closed_event = threading.Event()
|
|
196
|
+
|
|
197
|
+
def close_on_loop() -> None:
|
|
198
|
+
try:
|
|
199
|
+
self._close()
|
|
200
|
+
finally:
|
|
201
|
+
closed_event.set()
|
|
202
|
+
|
|
203
|
+
self.loop.call_soon_threadsafe(close_on_loop)
|
|
204
|
+
closed_event.wait()
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
self._close()
|
|
208
|
+
|
|
209
|
+
def _wait_closed_sync(self) -> None:
|
|
210
|
+
if self._server is None or self.loop.is_closed():
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
if self.loop.is_running():
|
|
214
|
+
if getattr(self.loop, "_thread_id", None) == threading.get_ident():
|
|
215
|
+
asyncio.create_task(self._server.wait_closed(), name=f"{type(self).__name__}.wait_closed")
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
asyncio.run_coroutine_threadsafe(self._server.wait_closed(), self.loop).result()
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
self.loop.run_until_complete(self._server.wait_closed())
|
|
222
|
+
|
|
125
223
|
@_logger.call
|
|
126
224
|
def close(self) -> None:
|
|
127
225
|
if self._in_closing or self._closed:
|
|
@@ -129,10 +227,12 @@ class JsonRPCServer(Generic[TProtocol], abc.ABC):
|
|
|
129
227
|
|
|
130
228
|
self._in_closing = True
|
|
131
229
|
try:
|
|
132
|
-
self.
|
|
230
|
+
self._close_on_loop_thread()
|
|
231
|
+
self._wait_closed_sync()
|
|
133
232
|
finally:
|
|
134
233
|
self._in_closing = False
|
|
135
234
|
self._closed = True
|
|
235
|
+
self._finalizer.detach()
|
|
136
236
|
|
|
137
237
|
@_logger.call
|
|
138
238
|
async def close_async(self) -> None:
|
|
@@ -147,6 +247,7 @@ class JsonRPCServer(Generic[TProtocol], abc.ABC):
|
|
|
147
247
|
finally:
|
|
148
248
|
self._in_closing = False
|
|
149
249
|
self._closed = True
|
|
250
|
+
self._finalizer.detach()
|
|
150
251
|
|
|
151
252
|
async def __aenter__(self) -> Self:
|
|
152
253
|
await self.start_async()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.4.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|