socketflow 0.2.1__tar.gz → 0.2.3__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.
Files changed (28) hide show
  1. {socketflow-0.2.1 → socketflow-0.2.3}/PKG-INFO +1 -1
  2. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/__init__.py +1 -1
  3. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/client_side/client.py +13 -0
  4. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/blueprint.py +16 -0
  5. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/event_loop.py +14 -0
  6. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/transport.py +11 -2
  7. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/server_side/server.py +25 -9
  8. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow.egg-info/PKG-INFO +1 -1
  9. {socketflow-0.2.1 → socketflow-0.2.3}/README.md +0 -0
  10. {socketflow-0.2.1 → socketflow-0.2.3}/setup.cfg +0 -0
  11. {socketflow-0.2.1 → socketflow-0.2.3}/setup.py +0 -0
  12. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/client_side/__init__.py +0 -0
  13. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/__init__.py +0 -0
  14. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/compression.py +0 -0
  15. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/dispatcher.py +0 -0
  16. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/event.py +0 -0
  17. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/exceptions.py +0 -0
  18. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/logs.py +0 -0
  19. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/message_handler.py +0 -0
  20. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/message_manager.py +0 -0
  21. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/metrics.py +0 -0
  22. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/global_side/protocol.py +0 -0
  23. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow/server_side/__init__.py +0 -0
  24. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow.egg-info/SOURCES.txt +0 -0
  25. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow.egg-info/dependency_links.txt +0 -0
  26. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow.egg-info/not-zip-safe +0 -0
  27. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow.egg-info/requires.txt +0 -0
  28. {socketflow-0.2.1 → socketflow-0.2.3}/socketflow.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketflow
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: TCP networking for Python, without the boilerplate.
5
5
  Home-page: https://github.com/ayammaximilian/socketflow
6
6
  Author: SocketFlow Team
@@ -34,7 +34,7 @@ from .global_side.protocol import (
34
34
  MAX_PROTOCOL_VERSION,
35
35
  )
36
36
 
37
- __version__ = "0.2.1"
37
+ __version__ = "0.2.3"
38
38
  __all__ = [
39
39
  "TcpServer",
40
40
  "TcpClient",
@@ -235,6 +235,12 @@ class TcpClientProtocol:
235
235
  with self._control_lock:
236
236
  self.socket.sendall(data)
237
237
 
238
+ def has_backpressure(self) -> bool:
239
+ """True when the outbound queue to the server is full."""
240
+ if self._writer is None:
241
+ return False
242
+ return self._writer.has_backpressure()
243
+
238
244
  def close(self):
239
245
  self._writer.abort()
240
246
 
@@ -986,6 +992,13 @@ class TcpClient:
986
992
  def is_connected(self):
987
993
  return self._connected
988
994
 
995
+ def has_backpressure(self) -> bool:
996
+ """True when the outbound queue to the server is full."""
997
+ protocol = self._protocol
998
+ if protocol is None:
999
+ return False
1000
+ return protocol.has_backpressure()
1001
+
989
1002
  @property
990
1003
  def handshake_completed(self):
991
1004
  return self._handshake_completed
@@ -78,6 +78,22 @@ class Blueprint:
78
78
  "Blueprint not registered with client or server"
79
79
  )
80
80
 
81
+ def has_backpressure(self, client_addr: tuple = None) -> bool:
82
+ """True when the outbound queue is full.
83
+
84
+ On a server blueprint, client_addr selects the client to check.
85
+ """
86
+ if hasattr(self, "_client") and self._client:
87
+ return self._client.has_backpressure()
88
+ elif hasattr(self, "_server") and self._server:
89
+ if not client_addr:
90
+ raise ExceptionType.BlueprintError("client_addr parameter is required")
91
+ return self._server.has_backpressure(client_addr)
92
+ else:
93
+ raise ExceptionType.BlueprintError(
94
+ "Blueprint not registered with client or server"
95
+ )
96
+
81
97
  def send(
82
98
  self,
83
99
  data: Any,
@@ -176,6 +176,12 @@ class EventLoopWriter:
176
176
  return True
177
177
  return self.connection.flush(timeout)
178
178
 
179
+ def has_backpressure(self) -> bool:
180
+ """True when the next send would raise Backpressure."""
181
+ if self.connection is None:
182
+ return False
183
+ return self.connection.has_backpressure()
184
+
179
185
  def abort(self, timeout=1.0):
180
186
  if self.connection is not None:
181
187
  self.connection.close()
@@ -233,6 +239,14 @@ class _SelectorConnection:
233
239
  with self._lock:
234
240
  return self._pending_writes
235
241
 
242
+ def has_backpressure(self) -> bool:
243
+ """True when the next send would raise Backpressure."""
244
+ with self._lock:
245
+ return (
246
+ self._pending_writes >= self._max_pending_writes
247
+ or self._queued_bytes >= self._max_queue_bytes
248
+ )
249
+
236
250
  def flush(self, timeout: float = 5.0) -> bool:
237
251
  """Wait until the send buffer is empty.
238
252
 
@@ -50,7 +50,6 @@ class MemoryBudget:
50
50
  self._used_bytes = max(0, self._used_bytes - amount)
51
51
 
52
52
 
53
-
54
53
  class TimeoutScheduler:
55
54
  """Run request timeouts from a single shared thread.
56
55
 
@@ -217,7 +216,9 @@ class PendingResponseRegistry:
217
216
  key = self._key(data_id, owner)
218
217
  with self._lock:
219
218
  if key in self._responses:
220
- raise ValueError(f"A request with data_id {data_id!r} is already pending")
219
+ raise ValueError(
220
+ f"A request with data_id {data_id!r} is already pending"
221
+ )
221
222
  self._responses[key] = future
222
223
 
223
224
  def pop(self, data_id: str, owner: Any = None) -> Optional[Any]:
@@ -335,6 +336,14 @@ class SocketWriter:
335
336
  return False
336
337
  time.sleep(0.002)
337
338
 
339
+ def has_backpressure(self) -> bool:
340
+ """True when the next send would raise Backpressure."""
341
+ with self._state_lock:
342
+ return (
343
+ self._pending_writes >= self._max_pending_writes
344
+ or self._queued_bytes >= self._max_queue_bytes
345
+ )
346
+
338
347
  def _complete(self, payload: bytes):
339
348
  with self._state_lock:
340
349
  self._queued_bytes -= len(payload)
@@ -436,6 +436,12 @@ class TcpServerProtocol:
436
436
  except Exception:
437
437
  return False
438
438
 
439
+ def has_backpressure(self) -> bool:
440
+ """True when the outbound queue to this client is full."""
441
+ if self._writer is None:
442
+ return False
443
+ return self._writer.has_backpressure()
444
+
439
445
  def close(self):
440
446
  if self._writer is not None:
441
447
  self._writer.abort()
@@ -815,20 +821,17 @@ class TcpServer:
815
821
  pass
816
822
 
817
823
  protocol.attach_socket(client_socket)
818
- try:
819
- protocol._send_server_ready()
820
- except Exception:
821
- protocol.handle_connection_lost()
822
- return
823
824
  with self._clients_lock:
824
825
  self._clients[address] = protocol
825
-
826
826
  try:
827
+ protocol._send_server_ready()
827
828
  if not self.auth_required:
829
+ if self.require_handshake:
830
+ protocol._send_handshake_ok()
828
831
  protocol._notify_connected()
829
832
  except Exception:
830
833
  protocol.handle_connection_lost()
831
- raise
834
+ return
832
835
 
833
836
  while True:
834
837
  try:
@@ -943,6 +946,10 @@ class TcpServer:
943
946
  self._clients[address] = protocol
944
947
  protocol._event_loop_mode = False
945
948
  protocol._send_server_ready()
949
+ if not self.auth_required:
950
+ if self.require_handshake:
951
+ protocol._send_handshake_ok()
952
+ protocol._notify_connected()
946
953
  protocol._event_loop_mode = True
947
954
 
948
955
  def on_close():
@@ -962,8 +969,6 @@ class TcpServer:
962
969
  memory_budget=self.memory_budget,
963
970
  )
964
971
  registered = True
965
- if not self.auth_required and not self.require_handshake:
966
- protocol._notify_connected()
967
972
  except Exception as error:
968
973
  if protocol is not None:
969
974
  protocol.handle_connection_lost()
@@ -1432,6 +1437,17 @@ class TcpServer:
1432
1437
  with self._clients_lock:
1433
1438
  return client_addr in self._clients
1434
1439
 
1440
+ def has_backpressure(self, client_addr) -> bool:
1441
+ """True when the outbound queue to one client is full.
1442
+
1443
+ Returns False when the client is not connected.
1444
+ """
1445
+ with self._clients_lock:
1446
+ protocol = self._clients.get(client_addr)
1447
+ if protocol is None:
1448
+ return False
1449
+ return protocol.has_backpressure()
1450
+
1435
1451
  def get_client_identity(self, client_addr: tuple) -> Optional[str]:
1436
1452
  """Return the verified certificate common name for a client, or None."""
1437
1453
  with self._clients_lock:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketflow
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: TCP networking for Python, without the boilerplate.
5
5
  Home-page: https://github.com/ayammaximilian/socketflow
6
6
  Author: SocketFlow Team
File without changes
File without changes
File without changes