wslink 2.2.2__tar.gz → 2.3.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.
- {wslink-2.2.2 → wslink-2.3.0}/PKG-INFO +1 -1
- {wslink-2.2.2 → wslink-2.3.0}/setup.cfg +1 -1
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/generic/core.py +1 -1
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/jupyter/core.py +1 -41
- wslink-2.3.0/src/wslink/emitter.py +63 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/protocol.py +15 -3
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/websocket.py +12 -3
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink.egg-info/PKG-INFO +1 -1
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink.egg-info/SOURCES.txt +1 -0
- {wslink-2.2.2 → wslink-2.3.0}/MANIFEST.in +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/README.rst +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/setup.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/LICENSE +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/__init__.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/__init__.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/aiohttp/__init__.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/aiohttp/launcher.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/aiohttp/relay.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/generic/__init__.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/jupyter/__init__.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/tornado/__init__.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/backends/tornado/core.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/chunking.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/launcher.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/publish.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/relay.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/server.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/ssl_context.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink/uri.py +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink.egg-info/dependency_links.txt +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink.egg-info/requires.txt +0 -0
- {wslink-2.2.2 → wslink-2.3.0}/src/wslink.egg-info/top_level.txt +0 -0
@@ -1,49 +1,9 @@
|
|
1
|
-
import asyncio
|
2
1
|
from functools import partial
|
2
|
+
from wslink.emitter import EventEmitter
|
3
3
|
from wslink.backends.generic.core import GenericServer
|
4
4
|
from IPython.core.getipython import get_ipython
|
5
5
|
|
6
6
|
|
7
|
-
class EventEmitter:
|
8
|
-
def __init__(self):
|
9
|
-
self._listeners = {}
|
10
|
-
|
11
|
-
def clear(self):
|
12
|
-
self._listeners = {}
|
13
|
-
|
14
|
-
def emit(self, event, *args, **kwargs):
|
15
|
-
listeners = self._listeners.get(event)
|
16
|
-
if listeners is None:
|
17
|
-
return
|
18
|
-
|
19
|
-
loop = asyncio.get_running_loop()
|
20
|
-
coroutine_run = (
|
21
|
-
loop.create_task if (loop and loop.is_running()) else asyncio.run
|
22
|
-
)
|
23
|
-
|
24
|
-
for listener in listeners:
|
25
|
-
if asyncio.iscoroutinefunction(listener):
|
26
|
-
coroutine_run(listener(*args, **kwargs))
|
27
|
-
else:
|
28
|
-
listener(*args, **kwargs)
|
29
|
-
|
30
|
-
def add_event_listener(self, event, listener):
|
31
|
-
listeners = self._listeners.get(event)
|
32
|
-
if listeners is None:
|
33
|
-
listeners = set()
|
34
|
-
self._listeners[event] = listeners
|
35
|
-
|
36
|
-
listeners.add(listener)
|
37
|
-
|
38
|
-
def remove_event_listener(self, event, listener):
|
39
|
-
listeners = self._listeners.get(event)
|
40
|
-
if listeners is None:
|
41
|
-
return
|
42
|
-
|
43
|
-
if listener in listeners:
|
44
|
-
listeners.remove(listener)
|
45
|
-
|
46
|
-
|
47
7
|
class WsJupyterComm(EventEmitter):
|
48
8
|
def __init__(self, kernel=None):
|
49
9
|
super().__init__()
|
@@ -0,0 +1,63 @@
|
|
1
|
+
from typing import TypeVar, Generic, LiteralString
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import functools
|
5
|
+
|
6
|
+
|
7
|
+
T = TypeVar("T", bound=LiteralString)
|
8
|
+
|
9
|
+
|
10
|
+
class EventEmitter(Generic[T]):
|
11
|
+
def __init__(self):
|
12
|
+
self._listeners = {}
|
13
|
+
|
14
|
+
def clear(self):
|
15
|
+
self._listeners = {}
|
16
|
+
|
17
|
+
def __call__(self, event: T, *args, **kwargs):
|
18
|
+
self.emit(event, *args, **kwargs)
|
19
|
+
|
20
|
+
def __getattr__(self, name: T):
|
21
|
+
return functools.partial(self.emit, name)
|
22
|
+
|
23
|
+
def emit(self, event: T, *args, **kwargs):
|
24
|
+
listeners = self._listeners.get(event)
|
25
|
+
if listeners is None:
|
26
|
+
return
|
27
|
+
|
28
|
+
loop = asyncio.get_running_loop()
|
29
|
+
coroutine_run = (
|
30
|
+
loop.create_task if (loop and loop.is_running()) else asyncio.run
|
31
|
+
)
|
32
|
+
|
33
|
+
for listener in listeners:
|
34
|
+
if asyncio.iscoroutinefunction(listener):
|
35
|
+
coroutine_run(listener(*args, **kwargs))
|
36
|
+
else:
|
37
|
+
listener(*args, **kwargs)
|
38
|
+
|
39
|
+
def add_event_listener(self, event: T, listener):
|
40
|
+
listeners = self._listeners.get(event)
|
41
|
+
if listeners is None:
|
42
|
+
listeners = set()
|
43
|
+
self._listeners[event] = listeners
|
44
|
+
|
45
|
+
listeners.add(listener)
|
46
|
+
|
47
|
+
def remove_event_listener(self, event: T, listener):
|
48
|
+
listeners = self._listeners.get(event)
|
49
|
+
if listeners is None:
|
50
|
+
return
|
51
|
+
|
52
|
+
if listener in listeners:
|
53
|
+
listeners.remove(listener)
|
54
|
+
|
55
|
+
def has(self, event: T):
|
56
|
+
return self.listeners_count(event) > 0
|
57
|
+
|
58
|
+
def listeners_count(self, event: T):
|
59
|
+
listeners = self._listeners.get(event)
|
60
|
+
if listeners is None:
|
61
|
+
return 0
|
62
|
+
|
63
|
+
return len(listeners)
|
@@ -9,6 +9,7 @@ import traceback
|
|
9
9
|
from wslink import schedule_coroutine
|
10
10
|
from wslink.publish import PublishManager
|
11
11
|
from wslink.chunking import generate_chunks, UnChunker
|
12
|
+
from wslink.websocket import ServerProtocol
|
12
13
|
|
13
14
|
# from http://www.jsonrpc.org/specification, section 5.1
|
14
15
|
METHOD_NOT_FOUND = -32601
|
@@ -141,7 +142,7 @@ class AbstractWebApp:
|
|
141
142
|
|
142
143
|
|
143
144
|
class WslinkHandler(object):
|
144
|
-
def __init__(self, protocol
|
145
|
+
def __init__(self, protocol: ServerProtocol, web_app=None):
|
145
146
|
self.serverProtocol = protocol
|
146
147
|
self.web_app = web_app
|
147
148
|
self.functionMap = {}
|
@@ -153,6 +154,7 @@ class WslinkHandler(object):
|
|
153
154
|
self.pub_manager = PublishManager()
|
154
155
|
self.unchunkers = {}
|
155
156
|
self.network_monitor = protocol.network_monitor
|
157
|
+
self.log_emitter = protocol.log_emitter
|
156
158
|
|
157
159
|
# Build the rpc method dictionary, assuming we were given a serverprotocol
|
158
160
|
if self.getServerProtocol():
|
@@ -253,7 +255,9 @@ class WslinkHandler(object):
|
|
253
255
|
|
254
256
|
async def onMessage(self, is_binary, msg, client_id):
|
255
257
|
if not is_binary:
|
256
|
-
|
258
|
+
error_message = "wslink is not expecting text message:\n> %s"
|
259
|
+
logger.critical(error_message, msg.data)
|
260
|
+
self.log_emitter.critical(error_message % msg.data)
|
257
261
|
return
|
258
262
|
|
259
263
|
full_message = self.unchunkers[client_id].process_chunk(msg.data)
|
@@ -262,7 +266,13 @@ class WslinkHandler(object):
|
|
262
266
|
await self.onCompleteMessage(full_message, client_id)
|
263
267
|
|
264
268
|
async def onCompleteMessage(self, rpc, client_id):
|
265
|
-
|
269
|
+
debug_message = "wslink incoming msg %s"
|
270
|
+
stripped_payload = self.payloadWithSecretStripped(rpc)
|
271
|
+
logger.debug(debug_message, stripped_payload)
|
272
|
+
|
273
|
+
if self.log_emitter.has("debug"):
|
274
|
+
self.log_emitter.debug(debug_message % stripped_payload)
|
275
|
+
|
266
276
|
if "id" not in rpc:
|
267
277
|
return
|
268
278
|
|
@@ -295,6 +305,7 @@ class WslinkHandler(object):
|
|
295
305
|
|
296
306
|
# No matching method found
|
297
307
|
if not methodName in self.functionMap:
|
308
|
+
self.log_emitter.error(f"Method not found: {methodName}")
|
298
309
|
with self.network_monitor:
|
299
310
|
await self.sendWrappedError(
|
300
311
|
rpcid,
|
@@ -328,6 +339,7 @@ class WslinkHandler(object):
|
|
328
339
|
logger.error("Exception raised")
|
329
340
|
logger.error(repr(e_inst))
|
330
341
|
logger.error(captured_trace)
|
342
|
+
self.log_emitter.exception(e_inst)
|
331
343
|
with self.network_monitor:
|
332
344
|
await self.sendWrappedError(
|
333
345
|
rpcid,
|
@@ -6,9 +6,12 @@ ServerProtocol to hook all the needed LinkProtocols together.
|
|
6
6
|
|
7
7
|
import logging
|
8
8
|
import asyncio
|
9
|
+
from typing import Literal
|
10
|
+
from dataclasses import dataclass
|
9
11
|
|
10
|
-
from
|
11
|
-
from
|
12
|
+
from wslink import register as exportRpc
|
13
|
+
from wslink import schedule_callback
|
14
|
+
from wslink.emitter import EventEmitter
|
12
15
|
|
13
16
|
logger = logging.getLogger(__name__)
|
14
17
|
|
@@ -117,6 +120,9 @@ class NetworkMonitor:
|
|
117
120
|
await self.event.wait()
|
118
121
|
|
119
122
|
|
123
|
+
LogEmitterEvents = Literal["exception", "error", "critical", "info", "debug"]
|
124
|
+
|
125
|
+
|
120
126
|
class ServerProtocol(object):
|
121
127
|
"""
|
122
128
|
Defines the core server protocol for wslink. Gathers a list of LinkProtocol
|
@@ -125,6 +131,7 @@ class ServerProtocol(object):
|
|
125
131
|
|
126
132
|
def __init__(self):
|
127
133
|
self.network_monitor = NetworkMonitor()
|
134
|
+
self.log_emitter = EventEmitter[LogEmitterEvents]()
|
128
135
|
self.linkProtocols = []
|
129
136
|
self.secret = None
|
130
137
|
self.initialize()
|
@@ -169,7 +176,9 @@ class ServerProtocol(object):
|
|
169
176
|
try:
|
170
177
|
self.linkProtocols.remove(protocol)
|
171
178
|
except ValueError as e:
|
172
|
-
|
179
|
+
error_message = "Link protocol missing from registered list."
|
180
|
+
logger.error(error_message)
|
181
|
+
self.log_emitter("error", error_message)
|
173
182
|
|
174
183
|
def getLinkProtocols(self):
|
175
184
|
return self.linkProtocols
|
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
|
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
|