wslink 2.2.2__py3-none-any.whl → 2.3.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- wslink/backends/generic/core.py +1 -1
- wslink/backends/jupyter/core.py +1 -41
- wslink/emitter.py +63 -0
- wslink/protocol.py +15 -3
- wslink/websocket.py +12 -3
- {wslink-2.2.2.dist-info → wslink-2.3.0.dist-info}/METADATA +1 -1
- {wslink-2.2.2.dist-info → wslink-2.3.0.dist-info}/RECORD +9 -8
- {wslink-2.2.2.dist-info → wslink-2.3.0.dist-info}/WHEEL +0 -0
- {wslink-2.2.2.dist-info → wslink-2.3.0.dist-info}/top_level.txt +0 -0
wslink/backends/generic/core.py
CHANGED
wslink/backends/jupyter/core.py
CHANGED
@@ -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__()
|
wslink/emitter.py
ADDED
@@ -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)
|
wslink/protocol.py
CHANGED
@@ -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,
|
wslink/websocket.py
CHANGED
@@ -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
|
@@ -1,25 +1,26 @@
|
|
1
1
|
wslink/LICENSE,sha256=I44UH7kDVqxDLnnlOWw_hFL2Fz7RjQ_4vPzZv9NYgTU,1483
|
2
2
|
wslink/__init__.py,sha256=qdLGVhzpEXBoraTnLr5S-8bX9oK8pZCQWe8rZRlwAco,2946
|
3
3
|
wslink/chunking.py,sha256=1DJlGG6fjknGFrqPOtqUUc5tCrijldP7Kdx56d5e3Wg,7337
|
4
|
+
wslink/emitter.py,sha256=4nm5fOwfvSb5tHq5cZtUsF8YNpuphl05QZQzNHljfRU,1669
|
4
5
|
wslink/launcher.py,sha256=8VMs3juObLkyGYQFNLjMoo4qFpKIcxWz0kS-af-DKO4,21170
|
5
|
-
wslink/protocol.py,sha256=
|
6
|
+
wslink/protocol.py,sha256=IUvdYzXaaKP4qllJps6uQ8zvbipbH6MVwU6o6f60Kuc,17265
|
6
7
|
wslink/publish.py,sha256=Xyv9piZT4HxO5l_SA7zeReH8t6tisVHUgU57Hjzgkcg,1595
|
7
8
|
wslink/relay.py,sha256=E8Lzu2Ay7KbOheN1-ArAZawo8lLqdDgJXOZSBuMknYs,86
|
8
9
|
wslink/server.py,sha256=yvhCjpzPOfhbZrpDvW9i4H_uSyuQAe3ZOP-BRBmgHQA,9326
|
9
10
|
wslink/ssl_context.py,sha256=hNOJJCdrStws1Qf6vPvY4vTk9Bf8J5d90W3fS0cRv8o,2290
|
10
11
|
wslink/uri.py,sha256=woCQ4yChUqTMg9IT6YYDtUYeKmCg7OUCEgeBGA-19DY,384
|
11
|
-
wslink/websocket.py,sha256=
|
12
|
+
wslink/websocket.py,sha256=T47xgY6fgQQqDqcxmZpamIEvhQtvw_TWGeF2Es1dIpI,6350
|
12
13
|
wslink/backends/__init__.py,sha256=cyJGjm-YyBSyOEX81owyTbJ3YnrA6dB7--B4LnsEtHI,1214
|
13
14
|
wslink/backends/aiohttp/__init__.py,sha256=SSK5a_lxuEZGyW_mMMipgvAhJQQLhxAImR3jRokFchM,9301
|
14
15
|
wslink/backends/aiohttp/launcher.py,sha256=gHNMvtgNHEwBN_QBRDSCrTp2B4K1PsfV81rKaHi7Cxo,8897
|
15
16
|
wslink/backends/aiohttp/relay.py,sha256=oZAzIQTpsQaObWXaa-_VtoTOUQALC_QLDd9UvWspYaU,13311
|
16
17
|
wslink/backends/generic/__init__.py,sha256=Qu65gWsd2xCSsxybnDtEDI5vMjHN-F5jgPZOyNIxnGs,112
|
17
|
-
wslink/backends/generic/core.py,sha256
|
18
|
+
wslink/backends/generic/core.py,sha256=-tlNgXdvEr8BrwosxBWlUJVk5yiGMLfbK3EWs5VEKNM,4358
|
18
19
|
wslink/backends/jupyter/__init__.py,sha256=Qu65gWsd2xCSsxybnDtEDI5vMjHN-F5jgPZOyNIxnGs,112
|
19
|
-
wslink/backends/jupyter/core.py,sha256=
|
20
|
+
wslink/backends/jupyter/core.py,sha256=F8R3uH4m6RHCrHHRiA5UAgDOLdyGbpuCSAgCZxANREk,2794
|
20
21
|
wslink/backends/tornado/__init__.py,sha256=Qu65gWsd2xCSsxybnDtEDI5vMjHN-F5jgPZOyNIxnGs,112
|
21
22
|
wslink/backends/tornado/core.py,sha256=tPMkkhWuO_ovkisVim0zcegwZKEAG4IRUdd_O_0a_R0,2157
|
22
|
-
wslink-2.
|
23
|
-
wslink-2.
|
24
|
-
wslink-2.
|
25
|
-
wslink-2.
|
23
|
+
wslink-2.3.0.dist-info/METADATA,sha256=hisMCyrZAwxLjXz24KZ74-VaFyHvusXzfpnzU0U5wJM,3120
|
24
|
+
wslink-2.3.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
25
|
+
wslink-2.3.0.dist-info/top_level.txt,sha256=N0d8eqvhwhfW1p1yPTmvxlbzhjz7ZyhBfysNvaFqpQY,7
|
26
|
+
wslink-2.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|