socketflow 0.1.2__tar.gz → 0.1.4__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 (22) hide show
  1. {socketflow-0.1.2 → socketflow-0.1.4}/PKG-INFO +1 -1
  2. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/__init__.py +1 -1
  3. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/client_side/client.py +13 -5
  4. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/blueprint.py +11 -5
  5. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/dispatcher.py +5 -1
  6. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/event.py +7 -0
  7. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/server_side/server.py +9 -4
  8. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow.egg-info/PKG-INFO +1 -1
  9. {socketflow-0.1.2 → socketflow-0.1.4}/README.md +0 -0
  10. {socketflow-0.1.2 → socketflow-0.1.4}/setup.cfg +0 -0
  11. {socketflow-0.1.2 → socketflow-0.1.4}/setup.py +0 -0
  12. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/client_side/__init__.py +0 -0
  13. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/__init__.py +0 -0
  14. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/compression.py +0 -0
  15. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/exceptions.py +0 -0
  16. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/message_handler.py +0 -0
  17. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/global_side/message_manager.py +0 -0
  18. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow/server_side/__init__.py +0 -0
  19. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow.egg-info/SOURCES.txt +0 -0
  20. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow.egg-info/dependency_links.txt +0 -0
  21. {socketflow-0.1.2 → socketflow-0.1.4}/socketflow.egg-info/not-zip-safe +0 -0
  22. {socketflow-0.1.2 → socketflow-0.1.4}/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.1.2
3
+ Version: 0.1.4
4
4
  Summary: TCP networking for Python, without the boilerplate.
5
5
  Home-page: https://github.com/ayammaximilian/socketflow
6
6
  Author: SocketFlow Team
@@ -20,7 +20,7 @@ from .global_side.exceptions import (
20
20
  ExceptionType,
21
21
  )
22
22
 
23
- __version__ = "0.1.2"
23
+ __version__ = "0.1.4"
24
24
  __all__ = [
25
25
  "TcpServer",
26
26
  "TcpClient",
@@ -5,6 +5,7 @@ from ..global_side.event import (
5
5
  ConnectData,
6
6
  DisconnectData,
7
7
  MessageReceivedData,
8
+ ResponseData,
8
9
  ErrorData,
9
10
  )
10
11
  from ..global_side.dispatcher import EventDispatcher
@@ -86,14 +87,17 @@ class TcpClientProtocol:
86
87
  server_addr = (
87
88
  self.socket.getpeername() if self.socket else ("unknown", 0)
88
89
  )
90
+ wait_response = headers.get("wait_response", False)
89
91
  event_data = MessageReceivedData(
90
- data=body, server_addr=server_addr, data_id=data_id
92
+ data=body, server_addr=server_addr, data_id=data_id,
93
+ direct_response=wait_response
91
94
  )
92
95
 
96
+ # Resolve pending wait_response futures
93
97
  if data_id and data_id in self.client.pending_responses:
94
98
  future = self.client.pending_responses.pop(data_id)
95
99
  if hasattr(future, "set_result") and not future.done():
96
- future.set_result(body)
100
+ future.set_result(ResponseData(data=body, data_id=data_id))
97
101
  continue
98
102
 
99
103
  if path:
@@ -294,6 +298,7 @@ class TcpClient:
294
298
  "type": "__user__",
295
299
  "id": data_id,
296
300
  "path": path,
301
+ "wait_response": wait_response,
297
302
  }
298
303
 
299
304
  if self.compress:
@@ -337,7 +342,9 @@ class TcpClient:
337
342
  )
338
343
  )
339
344
 
340
- threading.Timer(wait_response_timeout, timeout_handler).start()
345
+ timer = threading.Timer(wait_response_timeout, timeout_handler)
346
+ timer.daemon = True
347
+ timer.start()
341
348
 
342
349
  try:
343
350
  return future.result()
@@ -354,6 +361,7 @@ class TcpClient:
354
361
  f"Error while waiting for response: {e}"
355
362
  )
356
363
  finally:
364
+ timer.cancel()
357
365
  self.pending_responses.pop(data_id, None)
358
366
 
359
367
  def wait(self):
@@ -375,8 +383,8 @@ class TcpClient:
375
383
  def event(self, event_type: str):
376
384
  return self.dispatcher.event(event_type)
377
385
 
378
- def path(self, path: str, middleware=None):
379
- return self.dispatcher.path(path, middleware)
386
+ def path(self, path: str, middleware=None, block: bool = False):
387
+ return self.dispatcher.path(path, middleware, block)
380
388
 
381
389
  def register_blueprint(self, blueprint):
382
390
  blueprint._client = self # Associate blueprint with this client
@@ -8,6 +8,7 @@ class Blueprint:
8
8
  self._event_handlers: Dict[str, List[Callable]] = {}
9
9
  self._path_handlers: Dict[str, List[Callable]] = {}
10
10
  self._path_middleware: Dict[str, List[Callable]] = {}
11
+ self._path_blocking: Dict[str, bool] = {}
11
12
 
12
13
  def event(self, event_type: str):
13
14
  """Decorator for event handlers"""
@@ -18,11 +19,11 @@ class Blueprint:
18
19
 
19
20
  return decorator
20
21
 
21
- def path(self, path: str, middleware=None):
22
+ def path(self, path: str, middleware=None, block: bool = False):
22
23
  """Decorator for path handlers"""
23
24
 
24
25
  def decorator(func):
25
- self.register_path(path, func, middleware)
26
+ self.register_path(path, func, middleware, block)
26
27
  return func
27
28
 
28
29
  return decorator
@@ -33,7 +34,7 @@ class Blueprint:
33
34
  self._event_handlers[event_type] = []
34
35
  self._event_handlers[event_type].append(handler)
35
36
 
36
- def register_path(self, path: str, handler: Callable, middleware=None):
37
+ def register_path(self, path: str, handler: Callable, middleware=None, block: bool = False):
37
38
  """Register a path handler"""
38
39
  if path not in self._path_handlers:
39
40
  self._path_handlers[path] = []
@@ -48,6 +49,10 @@ class Blueprint:
48
49
  else:
49
50
  self._path_middleware[path].append(middleware)
50
51
 
52
+ # Store blocking
53
+ if block:
54
+ self._path_blocking[path] = True
55
+
51
56
  def register_with_dispatcher(self, dispatcher):
52
57
  """Register all handlers with a dispatcher"""
53
58
  for event_type, handlers in self._event_handlers.items():
@@ -56,9 +61,10 @@ class Blueprint:
56
61
 
57
62
  for path, handlers in self._path_handlers.items():
58
63
  for handler in handlers:
59
- # Get middleware for this path
64
+ # Get middleware and blocking for this path
60
65
  middleware = self._path_middleware.get(path, [])
61
- dispatcher.register_path(path, handler, middleware)
66
+ block = self._path_blocking.get(path, False)
67
+ dispatcher.register_path(path, handler, middleware, block)
62
68
 
63
69
  def is_connected(self, client_addr: tuple = None):
64
70
  if hasattr(self, "_client") and self._client:
@@ -166,7 +166,11 @@ class EventDispatcher:
166
166
  # Check if blocking is enabled for this path and we have a client ID
167
167
  lock = None
168
168
  if is_blocking and client_id:
169
- lock_key = (path, client_id)
169
+ # Use the pattern string for parameterized paths so all dynamic
170
+ # paths sharing the same pattern (e.g. /slow/1, /slow/2 for
171
+ # /slow/<id>) share one lock per client.
172
+ lock_path = matched_pattern if matched_pattern else path
173
+ lock_key = (lock_path, client_id)
170
174
  # Get or create lock for this client-path combination
171
175
  if lock_key not in self._path_locks:
172
176
  self._path_locks[lock_key] = threading.Lock()
@@ -33,6 +33,13 @@ class MessageReceivedData:
33
33
  server_addr: Optional[Tuple[str, int]] = None
34
34
  data_id: Optional[str] = None
35
35
  params: Dict[str, str] = field(default_factory=dict)
36
+ direct_response: bool = False
37
+
38
+
39
+ @dataclass
40
+ class ResponseData:
41
+ data: Any
42
+ data_id: Optional[str] = None
36
43
 
37
44
 
38
45
  @dataclass
@@ -13,6 +13,7 @@ from ..global_side.event import (
13
13
  ErrorData,
14
14
  EventType,
15
15
  MessageReceivedData,
16
+ ResponseData,
16
17
  ServerStartData,
17
18
  ServerStopData,
18
19
  )
@@ -86,14 +87,17 @@ class TcpServerProtocol:
86
87
  elif msg_type == "__user__":
87
88
  path = headers.get("path")
88
89
  data_id = headers.get("id")
90
+ wait_response = headers.get("wait_response", False)
89
91
  event_data = MessageReceivedData(
90
- data=body, client_addr=self.client_addr, data_id=data_id
92
+ data=body, client_addr=self.client_addr, data_id=data_id,
93
+ direct_response=wait_response
91
94
  )
92
95
 
96
+ # Resolve pending wait_response futures
93
97
  if data_id and data_id in self.server.pending_responses:
94
98
  future = self.server.pending_responses.pop(data_id)
95
99
  if hasattr(future, "set_result") and not future.done():
96
- future.set_result(body)
100
+ future.set_result(ResponseData(data=body, data_id=data_id))
97
101
  continue
98
102
 
99
103
  if path:
@@ -305,6 +309,7 @@ class TcpServer:
305
309
  "type": "__user__",
306
310
  "id": data_id,
307
311
  "path": path,
312
+ "wait_response": wait_response,
308
313
  }
309
314
 
310
315
  if self.compress:
@@ -400,8 +405,8 @@ class TcpServer:
400
405
  def event(self, event_type: str):
401
406
  return self.dispatcher.event(event_type)
402
407
 
403
- def path(self, path: str, middleware=None):
404
- return self.dispatcher.path(path, middleware)
408
+ def path(self, path: str, middleware=None, block: bool = False):
409
+ return self.dispatcher.path(path, middleware, block)
405
410
 
406
411
  def register_blueprint(self, blueprint):
407
412
  blueprint._server = self # Associate blueprint with this server
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketflow
3
- Version: 0.1.2
3
+ Version: 0.1.4
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