wslink 2.1.3__py3-none-any.whl → 2.2.0__py3-none-any.whl

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/__init__.py CHANGED
@@ -54,7 +54,7 @@ def schedule_callback(delay, callback, *args, **kwargs):
54
54
  return loop.call_later(delay, functools.partial(callback, *args, **kwargs))
55
55
 
56
56
 
57
- def schedule_coroutine(delay, coro_func, *args, **kwargs):
57
+ def schedule_coroutine(delay, coro_func, *args, done_callback=None, **kwargs):
58
58
  """
59
59
  Creates a coroutine out of the provided coroutine function coro_func and
60
60
  the provided args and kwargs, then schedules the coroutine to be called
@@ -73,4 +73,11 @@ def schedule_coroutine(delay, coro_func, *args, **kwargs):
73
73
  # See method above for comment on "get_event_loop()" vs "get_running_loop()".
74
74
  loop = asyncio.get_event_loop()
75
75
  coro_partial = functools.partial(coro_func, *args, **kwargs)
76
+ if done_callback is not None:
77
+ return loop.call_later(
78
+ delay,
79
+ lambda: asyncio.ensure_future(coro_partial()).add_done_callback(
80
+ done_callback
81
+ ),
82
+ )
76
83
  return loop.call_later(delay, lambda: asyncio.ensure_future(coro_partial()))
wslink/protocol.py CHANGED
@@ -152,6 +152,7 @@ class WslinkHandler(object):
152
152
  self.attachment_atomic = asyncio.Lock()
153
153
  self.pub_manager = PublishManager()
154
154
  self.unchunkers = {}
155
+ self.network_monitor = protocol.network_monitor
155
156
 
156
157
  # Build the rpc method dictionary, assuming we were given a serverprotocol
157
158
  if self.getServerProtocol():
@@ -257,7 +258,8 @@ class WslinkHandler(object):
257
258
 
258
259
  full_message = self.unchunkers[client_id].process_chunk(msg.data)
259
260
  if full_message is not None:
260
- await self.onCompleteMessage(full_message, client_id)
261
+ with self.network_monitor:
262
+ await self.onCompleteMessage(full_message, client_id)
261
263
 
262
264
  async def onCompleteMessage(self, rpc, client_id):
263
265
  logger.debug("wslink incoming msg %s", self.payloadWithSecretStripped(rpc))
@@ -282,23 +284,25 @@ class WslinkHandler(object):
282
284
 
283
285
  # Prevent any further processing if token is not valid
284
286
  if not self.isClientAuthenticated(client_id):
285
- await self.sendWrappedError(
286
- rpcid,
287
- AUTHENTICATION_ERROR,
288
- "Unauthorized: Skip message processing",
289
- client_id=client_id,
290
- )
287
+ with self.network_monitor:
288
+ await self.sendWrappedError(
289
+ rpcid,
290
+ AUTHENTICATION_ERROR,
291
+ "Unauthorized: Skip message processing",
292
+ client_id=client_id,
293
+ )
291
294
  return
292
295
 
293
296
  # No matching method found
294
297
  if not methodName in self.functionMap:
295
- await self.sendWrappedError(
296
- rpcid,
297
- METHOD_NOT_FOUND,
298
- "Unregistered method called",
299
- methodName,
300
- client_id=client_id,
301
- )
298
+ with self.network_monitor:
299
+ await self.sendWrappedError(
300
+ rpcid,
301
+ METHOD_NOT_FOUND,
302
+ "Unregistered method called",
303
+ methodName,
304
+ client_id=client_id,
305
+ )
302
306
  return
303
307
 
304
308
  obj, func = self.functionMap[methodName]
@@ -308,31 +312,34 @@ class WslinkHandler(object):
308
312
  self.web_app.last_active_client_id = client_id
309
313
  results = func(*args, **kwargs)
310
314
  if inspect.isawaitable(results):
311
- results = await results
315
+ with self.network_monitor:
316
+ results = await results
312
317
 
313
318
  if self.connections[client_id].closed:
314
319
  # Connection was closed during RPC call.
315
320
  return
316
321
 
317
- await self.sendWrappedMessage(
318
- rpcid, results, method=methodName, client_id=client_id
319
- )
322
+ with self.network_monitor:
323
+ await self.sendWrappedMessage(
324
+ rpcid, results, method=methodName, client_id=client_id
325
+ )
320
326
  except Exception as e_inst:
321
327
  captured_trace = traceback.format_exc()
322
328
  logger.error("Exception raised")
323
329
  logger.error(repr(e_inst))
324
330
  logger.error(captured_trace)
325
- await self.sendWrappedError(
326
- rpcid,
327
- EXCEPTION_ERROR,
328
- "Exception raised",
329
- {
330
- "method": methodName,
331
- "exception": repr(e_inst),
332
- "trace": captured_trace,
333
- },
334
- client_id=client_id,
335
- )
331
+ with self.network_monitor:
332
+ await self.sendWrappedError(
333
+ rpcid,
334
+ EXCEPTION_ERROR,
335
+ "Exception raised",
336
+ {
337
+ "method": methodName,
338
+ "exception": repr(e_inst),
339
+ "trace": captured_trace,
340
+ },
341
+ client_id=client_id,
342
+ )
336
343
 
337
344
  def payloadWithSecretStripped(self, payload):
338
345
  payload = copy.deepcopy(payload)
@@ -397,13 +404,14 @@ class WslinkHandler(object):
397
404
  except Exception:
398
405
  # the content which is not serializable might be arbitrarily large, don't include.
399
406
  # repr(content) would do that...
400
- await self.sendWrappedError(
401
- rpcid,
402
- RESULT_SERIALIZE_ERROR,
403
- "Method result cannot be serialized",
404
- method,
405
- client_id=client_id,
406
- )
407
+ with self.network_monitor:
408
+ await self.sendWrappedError(
409
+ rpcid,
410
+ RESULT_SERIALIZE_ERROR,
411
+ "Method result cannot be serialized",
412
+ method,
413
+ client_id=client_id,
414
+ )
407
415
  return
408
416
 
409
417
  websockets = self.getAuthenticatedWebsockets(client_id, skip_last_active_client)
@@ -411,11 +419,15 @@ class WslinkHandler(object):
411
419
  # aiohttp can not handle pending ws.send_bytes()
412
420
  # tried with semaphore but got exception with >1
413
421
  # https://github.com/aio-libs/aiohttp/issues/2934
414
- async with self.attachment_atomic:
415
- for chunk in generate_chunks(packed_wrapper, MAX_MSG_SIZE):
416
- for ws in websockets:
417
- if ws is not None:
418
- await ws.send_bytes(chunk)
422
+ with self.network_monitor:
423
+ async with self.attachment_atomic:
424
+ for chunk in generate_chunks(packed_wrapper, MAX_MSG_SIZE):
425
+ for ws in websockets:
426
+ if ws is not None:
427
+ await ws.send_bytes(chunk)
428
+
429
+ # Network operation completed
430
+ self.network_monitor.network_call_completed()
419
431
 
420
432
  async def sendWrappedError(self, rpcid, code, message, data=None, client_id=None):
421
433
  wrapper = {
@@ -443,11 +455,15 @@ class WslinkHandler(object):
443
455
  # aiohttp can not handle pending ws.send_bytes()
444
456
  # tried with semaphore but got exception with >1
445
457
  # https://github.com/aio-libs/aiohttp/issues/2934
446
- async with self.attachment_atomic:
447
- for chunk in generate_chunks(packed_wrapper, MAX_MSG_SIZE):
448
- for ws in websockets:
449
- if ws is not None:
450
- await ws.send_bytes(chunk)
458
+ with self.network_monitor:
459
+ async with self.attachment_atomic:
460
+ for chunk in generate_chunks(packed_wrapper, MAX_MSG_SIZE):
461
+ for ws in websockets:
462
+ if ws is not None:
463
+ await ws.send_bytes(chunk)
464
+
465
+ # Network operation completed
466
+ self.network_monitor.network_call_completed()
451
467
 
452
468
  def publish(self, topic, data, client_id=None, skip_last_active_client=False):
453
469
  client_list = [client_id] if client_id else [c_id for c_id in self.connections]
wslink/publish.py CHANGED
@@ -27,6 +27,7 @@ class PublishManager(object):
27
27
  for protocol in self.protocols:
28
28
  # The client is unknown - we send to any client who is subscribed to the topic
29
29
  rpcid = "publish:{0}:{1}".format(topic, self.publishCount)
30
+ protocol.network_monitor.on_enter()
30
31
  schedule_coroutine(
31
32
  0,
32
33
  protocol.sendWrappedMessage,
@@ -34,6 +35,8 @@ class PublishManager(object):
34
35
  data,
35
36
  client_id=client_id,
36
37
  skip_last_active_client=skip_last_active_client,
38
+ # for schedule_coroutine call
39
+ done_callback=protocol.network_monitor.on_exit,
37
40
  )
38
41
 
39
42
 
wslink/websocket.py CHANGED
@@ -5,6 +5,7 @@ ServerProtocol to hook all the needed LinkProtocols together.
5
5
  """
6
6
 
7
7
  import logging
8
+ import asyncio
8
9
 
9
10
  from . import register as exportRpc
10
11
  from . import schedule_callback
@@ -65,6 +66,42 @@ class LinkProtocol(object):
65
66
  # =============================================================================
66
67
 
67
68
 
69
+ class NetworkMonitor:
70
+ def __init__(self):
71
+ self.pending = 0
72
+ self.event = asyncio.Event()
73
+
74
+ def network_call_completed(self):
75
+ self.event.set()
76
+
77
+ def on_enter(self, *args, **kwargs):
78
+ self.pending += 1
79
+
80
+ def on_exit(self, *args, **kwargs):
81
+ self.pending -= 1
82
+ if self.pending == 0 and not self.event.is_set():
83
+ self.event.set()
84
+
85
+ # Sync ctx manager
86
+ def __enter__(self):
87
+ self.on_enter()
88
+ return self
89
+
90
+ def __exit__(self, exc_type, exc_value, exc_traceback):
91
+ self.on_exit()
92
+
93
+ # Async ctx manager
94
+ async def __aenter__(self):
95
+ self.on_enter()
96
+ return self
97
+
98
+ async def __aexit__(self, exc_t, exc_v, exc_tb):
99
+ self.on_exit()
100
+ while self.pending:
101
+ self.event.clear()
102
+ await self.event.wait()
103
+
104
+
68
105
  class ServerProtocol(object):
69
106
  """
70
107
  Defines the core server protocol for wslink. Gathers a list of LinkProtocol
@@ -72,6 +109,7 @@ class ServerProtocol(object):
72
109
  """
73
110
 
74
111
  def __init__(self):
112
+ self.network_monitor = NetworkMonitor()
75
113
  self.linkProtocols = []
76
114
  self.secret = None
77
115
  self.initialize()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wslink
3
- Version: 2.1.3
3
+ Version: 2.2.0
4
4
  Summary: Python/JavaScript library for communicating over WebSocket
5
5
  Home-page: https://github.com/kitware/wslink
6
6
  Author: Kitware, Inc.
@@ -1,14 +1,14 @@
1
1
  wslink/LICENSE,sha256=I44UH7kDVqxDLnnlOWw_hFL2Fz7RjQ_4vPzZv9NYgTU,1483
2
- wslink/__init__.py,sha256=7Uyn6lk5LhykHbBwQ9pwSmX1YhxDanD9TT6H95DXzhk,2709
2
+ wslink/__init__.py,sha256=qdLGVhzpEXBoraTnLr5S-8bX9oK8pZCQWe8rZRlwAco,2946
3
3
  wslink/chunking.py,sha256=1DJlGG6fjknGFrqPOtqUUc5tCrijldP7Kdx56d5e3Wg,7337
4
4
  wslink/launcher.py,sha256=8VMs3juObLkyGYQFNLjMoo4qFpKIcxWz0kS-af-DKO4,21170
5
- wslink/protocol.py,sha256=V5kqfHebfyXzmZq0-ua0SjDkOXdQmx1Mu29G3hORgx0,15971
6
- wslink/publish.py,sha256=9G5TXqyGr-LCo_LwHYhzif6lhG2iXDvEBmEgwR8fh1M,1437
5
+ wslink/protocol.py,sha256=T3F_Y_pZ4qXUApjpDoi9zXDkDvp3qnlKQICj2jnPlsA,16744
6
+ wslink/publish.py,sha256=Xyv9piZT4HxO5l_SA7zeReH8t6tisVHUgU57Hjzgkcg,1595
7
7
  wslink/relay.py,sha256=E8Lzu2Ay7KbOheN1-ArAZawo8lLqdDgJXOZSBuMknYs,86
8
8
  wslink/server.py,sha256=yvhCjpzPOfhbZrpDvW9i4H_uSyuQAe3ZOP-BRBmgHQA,9326
9
9
  wslink/ssl_context.py,sha256=hNOJJCdrStws1Qf6vPvY4vTk9Bf8J5d90W3fS0cRv8o,2290
10
10
  wslink/uri.py,sha256=woCQ4yChUqTMg9IT6YYDtUYeKmCg7OUCEgeBGA-19DY,384
11
- wslink/websocket.py,sha256=pBiWqkL8Zn8LuSJ9nv3yA-KjEynbolOQ2gLHtQFJ2Ic,4611
11
+ wslink/websocket.py,sha256=Gdp8ll5z3wq6gBU0GSVu2iq_o598Mxt8jQ1wZi6Z87A,5520
12
12
  wslink/backends/__init__.py,sha256=cyJGjm-YyBSyOEX81owyTbJ3YnrA6dB7--B4LnsEtHI,1214
13
13
  wslink/backends/aiohttp/__init__.py,sha256=Bc3uPVrhNXnn5Xt3Tck2Th-dsysz1VAnQdh8WzRn1y0,9253
14
14
  wslink/backends/aiohttp/launcher.py,sha256=gHNMvtgNHEwBN_QBRDSCrTp2B4K1PsfV81rKaHi7Cxo,8897
@@ -19,7 +19,7 @@ wslink/backends/jupyter/__init__.py,sha256=Qu65gWsd2xCSsxybnDtEDI5vMjHN-F5jgPZOy
19
19
  wslink/backends/jupyter/core.py,sha256=PCQN-uZPFROnRv8B5dNwnwHV67o4Bpme3_Z6V-zbOUA,3864
20
20
  wslink/backends/tornado/__init__.py,sha256=Qu65gWsd2xCSsxybnDtEDI5vMjHN-F5jgPZOyNIxnGs,112
21
21
  wslink/backends/tornado/core.py,sha256=tPMkkhWuO_ovkisVim0zcegwZKEAG4IRUdd_O_0a_R0,2157
22
- wslink-2.1.3.dist-info/METADATA,sha256=MY8aB802QkaDhIEEw7ekhqlW371QtyqLk4uws5kMqwY,3120
23
- wslink-2.1.3.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
24
- wslink-2.1.3.dist-info/top_level.txt,sha256=N0d8eqvhwhfW1p1yPTmvxlbzhjz7ZyhBfysNvaFqpQY,7
25
- wslink-2.1.3.dist-info/RECORD,,
22
+ wslink-2.2.0.dist-info/METADATA,sha256=SEj4XM87pZO5pMxU6WTxYxyGz7rZdxFOspQkHEYTkEg,3120
23
+ wslink-2.2.0.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
24
+ wslink-2.2.0.dist-info/top_level.txt,sha256=N0d8eqvhwhfW1p1yPTmvxlbzhjz7ZyhBfysNvaFqpQY,7
25
+ wslink-2.2.0.dist-info/RECORD,,
File without changes