zexus 1.7.1 → 1.7.2

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 (159) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/src/__init__.py +7 -0
  4. package/src/zexus/__init__.py +1 -1
  5. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  6. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  7. package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
  8. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  9. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  10. package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
  11. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  12. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  13. package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
  14. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  15. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  16. package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
  17. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  18. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  19. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  20. package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
  22. package/src/zexus/advanced_types.py +17 -2
  23. package/src/zexus/blockchain/__init__.py +411 -0
  24. package/src/zexus/blockchain/accelerator.py +1160 -0
  25. package/src/zexus/blockchain/chain.py +660 -0
  26. package/src/zexus/blockchain/consensus.py +821 -0
  27. package/src/zexus/blockchain/contract_vm.py +1019 -0
  28. package/src/zexus/blockchain/crypto.py +79 -14
  29. package/src/zexus/blockchain/events.py +526 -0
  30. package/src/zexus/blockchain/loadtest.py +721 -0
  31. package/src/zexus/blockchain/monitoring.py +350 -0
  32. package/src/zexus/blockchain/mpt.py +716 -0
  33. package/src/zexus/blockchain/multichain.py +951 -0
  34. package/src/zexus/blockchain/multiprocess_executor.py +338 -0
  35. package/src/zexus/blockchain/network.py +886 -0
  36. package/src/zexus/blockchain/node.py +666 -0
  37. package/src/zexus/blockchain/rpc.py +1203 -0
  38. package/src/zexus/blockchain/rust_bridge.py +421 -0
  39. package/src/zexus/blockchain/storage.py +423 -0
  40. package/src/zexus/blockchain/tokens.py +750 -0
  41. package/src/zexus/blockchain/upgradeable.py +1004 -0
  42. package/src/zexus/blockchain/verification.py +1602 -0
  43. package/src/zexus/blockchain/wallet.py +621 -0
  44. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  45. package/src/zexus/cli/main.py +300 -20
  46. package/src/zexus/cli/zpm.py +1 -1
  47. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  48. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/lexer.py +10 -5
  53. package/src/zexus/concurrency_system.py +79 -0
  54. package/src/zexus/config.py +54 -0
  55. package/src/zexus/crypto_bridge.py +244 -8
  56. package/src/zexus/dap/__init__.py +10 -0
  57. package/src/zexus/dap/__main__.py +4 -0
  58. package/src/zexus/dap/dap_server.py +391 -0
  59. package/src/zexus/dap/debug_engine.py +298 -0
  60. package/src/zexus/environment.py +10 -1
  61. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  62. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  63. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  64. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  65. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  66. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  67. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/bytecode_compiler.py +441 -37
  70. package/src/zexus/evaluator/core.py +560 -49
  71. package/src/zexus/evaluator/expressions.py +122 -49
  72. package/src/zexus/evaluator/functions.py +417 -16
  73. package/src/zexus/evaluator/statements.py +521 -118
  74. package/src/zexus/evaluator/unified_execution.py +573 -72
  75. package/src/zexus/evaluator/utils.py +14 -2
  76. package/src/zexus/event_loop.py +186 -0
  77. package/src/zexus/lexer.py +742 -486
  78. package/src/zexus/lsp/__init__.py +1 -1
  79. package/src/zexus/lsp/definition_provider.py +163 -9
  80. package/src/zexus/lsp/server.py +22 -8
  81. package/src/zexus/lsp/symbol_provider.py +182 -9
  82. package/src/zexus/module_cache.py +237 -9
  83. package/src/zexus/object.py +64 -6
  84. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  85. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  86. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  87. package/src/zexus/parser/parser.py +786 -285
  88. package/src/zexus/parser/strategy_context.py +407 -66
  89. package/src/zexus/parser/strategy_structural.py +117 -19
  90. package/src/zexus/persistence.py +15 -1
  91. package/src/zexus/renderer/__init__.py +15 -0
  92. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  93. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  94. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  95. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  96. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  97. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  98. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  99. package/src/zexus/renderer/tk_backend.py +208 -0
  100. package/src/zexus/renderer/web_backend.py +260 -0
  101. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  103. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  104. package/src/zexus/runtime/file_flags.py +137 -0
  105. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  106. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  107. package/src/zexus/security.py +424 -34
  108. package/src/zexus/stdlib/fs.py +23 -18
  109. package/src/zexus/stdlib/http.py +289 -186
  110. package/src/zexus/stdlib/sockets.py +207 -163
  111. package/src/zexus/stdlib/websockets.py +282 -0
  112. package/src/zexus/stdlib_integration.py +369 -2
  113. package/src/zexus/strategy_recovery.py +6 -3
  114. package/src/zexus/type_checker.py +423 -0
  115. package/src/zexus/virtual_filesystem.py +189 -2
  116. package/src/zexus/vm/__init__.py +113 -3
  117. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  118. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  119. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  120. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  121. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  122. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  123. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  124. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  125. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  126. package/src/zexus/vm/async_optimizer.py +14 -1
  127. package/src/zexus/vm/binary_bytecode.py +659 -0
  128. package/src/zexus/vm/bytecode.py +28 -1
  129. package/src/zexus/vm/bytecode_converter.py +26 -12
  130. package/src/zexus/vm/cabi.c +1985 -0
  131. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  132. package/src/zexus/vm/cabi.h +127 -0
  133. package/src/zexus/vm/cache.py +557 -17
  134. package/src/zexus/vm/compiler.py +703 -5
  135. package/src/zexus/vm/fastops.c +15743 -0
  136. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  137. package/src/zexus/vm/fastops.pyx +288 -0
  138. package/src/zexus/vm/gas_metering.py +50 -9
  139. package/src/zexus/vm/jit.py +83 -2
  140. package/src/zexus/vm/native_jit_backend.py +1816 -0
  141. package/src/zexus/vm/native_runtime.cpp +1388 -0
  142. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  143. package/src/zexus/vm/optimizer.py +161 -11
  144. package/src/zexus/vm/parallel_vm.py +118 -42
  145. package/src/zexus/vm/peephole_optimizer.py +82 -4
  146. package/src/zexus/vm/profiler.py +38 -18
  147. package/src/zexus/vm/register_allocator.py +16 -5
  148. package/src/zexus/vm/register_vm.py +8 -5
  149. package/src/zexus/vm/vm.py +3411 -573
  150. package/src/zexus/vm/wasm_compiler.py +658 -0
  151. package/src/zexus/zexus_ast.py +63 -11
  152. package/src/zexus/zexus_token.py +13 -5
  153. package/src/zexus/zpm/installer.py +55 -15
  154. package/src/zexus/zpm/package_manager.py +1 -1
  155. package/src/zexus/zpm/registry.py +257 -28
  156. package/src/zexus.egg-info/PKG-INFO +7 -4
  157. package/src/zexus.egg-info/SOURCES.txt +116 -9
  158. package/src/zexus.egg-info/entry_points.txt +1 -0
  159. package/src/zexus.egg-info/requires.txt +4 -0
@@ -1,11 +1,52 @@
1
- """Socket/TCP primitives module for Zexus standard library."""
1
+ """Socket/TCP primitives module for Zexus standard library.
2
2
 
3
+ Uses ``asyncio`` for non-blocking I/O instead of one-thread-per-connection.
4
+ A background event-loop thread is shared across all sockets so callers that
5
+ aren't themselves running inside an asyncio loop get synchronous-looking
6
+ wrappers automatically.
7
+ """
8
+
9
+ import asyncio
3
10
  import socket
4
11
  import threading
5
12
  import time
6
13
  from typing import Callable, Optional, Dict, Any
7
14
 
8
15
 
16
+ # ---------------------------------------------------------------------------
17
+ # Shared background event loop (lazily created, one per interpreter)
18
+ # ---------------------------------------------------------------------------
19
+ _BG_LOOP: Optional[asyncio.AbstractEventLoop] = None
20
+ _BG_LOOP_LOCK = threading.Lock()
21
+
22
+
23
+ def _get_bg_loop() -> asyncio.AbstractEventLoop:
24
+ """Return the shared background asyncio event loop, starting it if needed."""
25
+ global _BG_LOOP
26
+ if _BG_LOOP is not None and _BG_LOOP.is_running():
27
+ return _BG_LOOP
28
+ with _BG_LOOP_LOCK:
29
+ if _BG_LOOP is not None and _BG_LOOP.is_running():
30
+ return _BG_LOOP
31
+ loop = asyncio.new_event_loop()
32
+
33
+ def _run(l: asyncio.AbstractEventLoop):
34
+ asyncio.set_event_loop(l)
35
+ l.run_forever()
36
+
37
+ t = threading.Thread(target=_run, args=(loop,), daemon=True)
38
+ t.start()
39
+ _BG_LOOP = loop
40
+ return loop
41
+
42
+
43
+ def _run_async(coro, timeout=10):
44
+ """Submit *coro* to the background loop and block until it finishes."""
45
+ loop = _get_bg_loop()
46
+ future = asyncio.run_coroutine_threadsafe(coro, loop)
47
+ return future.result(timeout=timeout)
48
+
49
+
9
50
  class SocketModule:
10
51
  """Provides socket and TCP operations."""
11
52
 
@@ -40,214 +81,217 @@ class SocketModule:
40
81
 
41
82
 
42
83
  class TCPServer:
43
- """TCP server that accepts connections and handles them with a callback."""
44
-
84
+ """TCP server backed by ``asyncio.start_server``."""
85
+
45
86
  def __init__(self, host: str, port: int, handler: Callable, backlog: int = 5):
46
87
  self.host = host
47
88
  self.port = port
48
89
  self.handler = handler
49
90
  self.backlog = backlog
50
- self.socket: Optional[socket.socket] = None
51
91
  self.running = False
52
- self.thread: Optional[threading.Thread] = None
53
-
92
+ self._server: Optional[asyncio.AbstractServer] = None
93
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
94
+
54
95
  def start(self) -> None:
55
- """Start the server in a background thread."""
96
+ """Start the server on the background event loop."""
56
97
  if self.running:
57
98
  raise RuntimeError("Server is already running")
58
-
59
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
60
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
61
- self.socket.bind((self.host, self.port))
62
- self.socket.listen(self.backlog)
99
+ self._loop = _get_bg_loop()
100
+ _run_async(self._async_start())
101
+
102
+ def stop(self) -> None:
103
+ """Gracefully stop the server."""
104
+ if not self.running:
105
+ return
106
+ self.running = False
107
+ if self._server and self._loop:
108
+ asyncio.run_coroutine_threadsafe(self._async_stop(), self._loop).result(timeout=5)
109
+
110
+ def is_running(self) -> bool:
111
+ return self.running
112
+
113
+ def get_address(self) -> Dict[str, Any]:
114
+ return {'host': self.host, 'port': self.port, 'running': self.running}
115
+
116
+ # -- async internals ----------------------------------------------------
117
+
118
+ async def _async_start(self):
119
+ server = await asyncio.start_server(
120
+ self._async_handle,
121
+ self.host,
122
+ self.port,
123
+ backlog=self.backlog,
124
+ reuse_address=True,
125
+ )
126
+ self._server = server
63
127
  self.running = True
128
+
129
+ async def _async_stop(self):
130
+ if self._server:
131
+ self._server.close()
132
+ await self._server.wait_closed()
133
+ self.running = False
134
+
135
+ async def _async_handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
136
+ """Handle a single incoming connection.
64
137
 
65
- # Start accept loop in background thread
66
- self.thread = threading.Thread(target=self._accept_loop, daemon=True)
67
- self.thread.start()
68
-
69
- def _accept_loop(self):
70
- """Accept connections and spawn handler threads."""
71
- while self.running:
72
- try:
73
- self.socket.settimeout(1.0) # Allow checking self.running
74
- client_socket, address = self.socket.accept()
75
-
76
- # Spawn handler in new thread
77
- handler_thread = threading.Thread(
78
- target=self._handle_connection,
79
- args=(client_socket, address),
80
- daemon=True
81
- )
82
- handler_thread.start()
83
-
84
- except socket.timeout:
85
- continue
86
- except Exception as e:
87
- if self.running: # Only log if we're not shutting down
88
- print(f"Server accept error: {e}")
89
- break
90
-
91
- def _handle_connection(self, client_socket: socket.socket, address: tuple):
92
- """Handle a single client connection."""
138
+ The user-supplied handler is synchronous (Zexus Action), so we run it
139
+ in a thread executor. This avoids the send/receive → _run_async
140
+ deadlock because the handler thread is NOT the event-loop thread.
141
+ """
142
+ addr = writer.get_extra_info('peername') or ('unknown', 0)
143
+ conn = TCPConnection._from_streams(reader, writer, addr[0], addr[1])
144
+ loop = asyncio.get_running_loop()
93
145
  try:
94
- connection = TCPConnection.from_socket(client_socket, address)
95
- self.handler(connection)
146
+ await loop.run_in_executor(None, self.handler, conn)
96
147
  except Exception as e:
97
148
  print(f"Connection handler error: {e}")
98
149
  finally:
150
+ # Ensure connection is marked closed (handler may have already closed it)
151
+ conn.connected = False
99
152
  try:
100
- client_socket.close()
101
- except:
102
- pass
103
-
104
- def stop(self) -> None:
105
- """Stop the server."""
106
- self.running = False
107
- if self.socket:
108
- try:
109
- self.socket.close()
110
- except:
153
+ if not writer.is_closing():
154
+ writer.close()
155
+ except Exception:
111
156
  pass
112
- if self.thread:
113
- self.thread.join(timeout=2.0)
114
-
115
- def is_running(self) -> bool:
116
- """Check if server is running."""
117
- return self.running
118
-
119
- def get_address(self) -> Dict[str, Any]:
120
- """Get server address info."""
121
- return {
122
- 'host': self.host,
123
- 'port': self.port,
124
- 'running': self.running
125
- }
126
157
 
127
158
 
128
159
  class TCPConnection:
129
- """Represents a TCP connection (client or server-side)."""
130
-
160
+ """TCP connection using asyncio streams.
161
+
162
+ All public methods are **synchronous** (for Zexus evaluator compat)
163
+ but internally schedule asyncio coroutines on the background loop.
164
+ """
165
+
131
166
  def __init__(self, host: str, port: int, timeout: float = 5.0):
132
- """Create a new client connection."""
167
+ """Create a new client connection (blocking)."""
133
168
  self.host = host
134
169
  self.port = port
135
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
136
- self.socket.settimeout(timeout)
137
- self.socket.connect((host, port))
170
+ self.connected = False
171
+ self._reader: Optional[asyncio.StreamReader] = None
172
+ self._writer: Optional[asyncio.StreamWriter] = None
173
+ self._loop = _get_bg_loop()
174
+
175
+ async def _open():
176
+ return await asyncio.wait_for(
177
+ asyncio.open_connection(host, port), timeout=timeout)
178
+
179
+ self._reader, self._writer = _run_async(_open())
138
180
  self.connected = True
139
-
181
+
182
+ @classmethod
183
+ def _from_streams(cls, reader: asyncio.StreamReader, writer: asyncio.StreamWriter,
184
+ host: str, port: int) -> 'TCPConnection':
185
+ """Wrap existing asyncio streams (used by TCPServer for accepted conns)."""
186
+ conn = cls.__new__(cls)
187
+ conn.host = host
188
+ conn.port = port
189
+ conn._reader = reader
190
+ conn._writer = writer
191
+ conn._loop = _get_bg_loop()
192
+ conn.connected = True
193
+ return conn
194
+
140
195
  @classmethod
141
- def from_socket(cls, sock: socket.socket, address: tuple):
142
- """Create TCPConnection from existing socket (for server-side)."""
196
+ def from_socket(cls, sock: socket.socket, address: tuple) -> 'TCPConnection':
197
+ """Create from a raw socket (backward-compat shim)."""
143
198
  conn = cls.__new__(cls)
144
- conn.socket = sock
145
199
  conn.host = address[0]
146
200
  conn.port = address[1]
201
+ conn._reader = None
202
+ conn._writer = None
203
+ conn._loop = _get_bg_loop()
147
204
  conn.connected = True
205
+ conn._raw_sock = sock
206
+
207
+ async def _wrap():
208
+ return await asyncio.open_connection(sock=sock)
209
+
210
+ try:
211
+ conn._reader, conn._writer = _run_async(_wrap())
212
+ except Exception:
213
+ pass
148
214
  return conn
149
-
215
+
216
+ # -- send ---------------------------------------------------------------
217
+
150
218
  def send(self, data: bytes) -> int:
151
- """Send data over the connection.
152
-
153
- Args:
154
- data: Bytes to send
155
-
156
- Returns:
157
- Number of bytes sent
158
- """
159
219
  if not self.connected:
160
220
  raise RuntimeError("Connection is closed")
161
- return self.socket.sendall(data) or len(data)
162
-
221
+
222
+ async def _send():
223
+ self._writer.write(data)
224
+ await self._writer.drain()
225
+
226
+ _run_async(_send())
227
+ return len(data)
228
+
163
229
  def send_string(self, text: str, encoding: str = 'utf-8') -> int:
164
- """Send string over the connection.
165
-
166
- Args:
167
- text: String to send
168
- encoding: Text encoding
169
-
170
- Returns:
171
- Number of bytes sent
172
- """
173
230
  return self.send(text.encode(encoding))
174
-
231
+
232
+ # -- receive ------------------------------------------------------------
233
+
175
234
  def receive(self, buffer_size: int = 4096) -> bytes:
176
- """Receive data from the connection.
177
-
178
- Args:
179
- buffer_size: Maximum bytes to receive
180
-
181
- Returns:
182
- Received bytes (empty if connection closed)
183
- """
184
235
  if not self.connected:
185
236
  raise RuntimeError("Connection is closed")
186
-
187
- try:
188
- data = self.socket.recv(buffer_size)
189
- if not data:
190
- self.connected = False
191
- return data
192
- except socket.timeout:
193
- return b''
194
-
237
+
238
+ async def _recv():
239
+ try:
240
+ return await asyncio.wait_for(self._reader.read(buffer_size), timeout=5.0)
241
+ except asyncio.TimeoutError:
242
+ return b''
243
+
244
+ data = _run_async(_recv())
245
+ if not data:
246
+ self.connected = False
247
+ return data
248
+
195
249
  def receive_string(self, buffer_size: int = 4096, encoding: str = 'utf-8') -> str:
196
- """Receive string from the connection.
197
-
198
- Args:
199
- buffer_size: Maximum bytes to receive
200
- encoding: Text encoding
201
-
202
- Returns:
203
- Received string
204
- """
205
250
  data = self.receive(buffer_size)
206
251
  return data.decode(encoding) if data else ''
207
-
252
+
208
253
  def receive_all(self, timeout: float = 5.0) -> bytes:
209
- """Receive all available data until connection closes or timeout.
210
-
211
- Args:
212
- timeout: Maximum time to wait
213
-
214
- Returns:
215
- All received bytes
216
- """
217
- chunks = []
218
- start_time = time.time()
219
- self.socket.settimeout(0.1) # Small timeout for checking
220
-
221
- while time.time() - start_time < timeout:
254
+ async def _recv_all():
255
+ chunks = []
222
256
  try:
223
- chunk = self.socket.recv(4096)
224
- if not chunk:
225
- break
226
- chunks.append(chunk)
227
- except socket.timeout:
228
- if chunks: # If we got some data, we're done
229
- break
230
- continue
231
-
232
- return b''.join(chunks)
233
-
257
+ while True:
258
+ chunk = await asyncio.wait_for(self._reader.read(4096), timeout=0.1)
259
+ if not chunk:
260
+ break
261
+ chunks.append(chunk)
262
+ except asyncio.TimeoutError:
263
+ pass
264
+ return b''.join(chunks)
265
+
266
+ return _run_async(_recv_all())
267
+
268
+ # -- close --------------------------------------------------------------
269
+
234
270
  def close(self) -> None:
235
- """Close the connection."""
236
- if self.connected:
271
+ if not self.connected:
272
+ return
273
+ self.connected = False
274
+ if self._writer:
237
275
  try:
238
- self.socket.close()
239
- except:
276
+ async def _close():
277
+ if not self._writer.is_closing():
278
+ self._writer.close()
279
+ try:
280
+ await asyncio.wait_for(self._writer.wait_closed(), timeout=2.0)
281
+ except (asyncio.TimeoutError, Exception):
282
+ pass
283
+ _run_async(_close(), timeout=3)
284
+ except Exception:
240
285
  pass
241
- self.connected = False
242
-
286
+ raw = getattr(self, '_raw_sock', None)
287
+ if raw:
288
+ try:
289
+ raw.close()
290
+ except Exception:
291
+ pass
292
+
243
293
  def is_connected(self) -> bool:
244
- """Check if connection is still open."""
245
294
  return self.connected
246
-
295
+
247
296
  def get_address(self) -> Dict[str, Any]:
248
- """Get connection address info."""
249
- return {
250
- 'host': self.host,
251
- 'port': self.port,
252
- 'connected': self.connected
253
- }
297
+ return {'host': self.host, 'port': self.port, 'connected': self.connected}