waldiez 0.1.7__py3-none-any.whl → 0.1.8__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.

Potentially problematic release.


This version of waldiez might be problematic. Click here for more details.

@@ -1,438 +0,0 @@
1
- """Simple TCP server using twisted.
2
-
3
- It listens for connections from an input provider and an input consumer,
4
- and forwards messages between them.
5
- """
6
-
7
- # pylint: disable=import-outside-toplevel,no-member,reimported,unused-import,redefined-outer-name,invalid-name # noqa
8
- import logging
9
- import sys
10
- import time
11
- from threading import Thread
12
- from types import TracebackType
13
- from typing import Dict, Optional, Type, cast
14
-
15
- from twisted.internet.error import ReactorNotRestartable
16
- from twisted.internet.interfaces import IReactorCore
17
- from twisted.internet.protocol import Factory, Protocol, connectionDone
18
- from twisted.internet.tcp import Port
19
- from twisted.python.failure import Failure
20
-
21
- LOGGER = logging.getLogger("tcp::server")
22
- END_OF_MESSAGE = b"\r\n"
23
-
24
-
25
- class ServerProtocol(Protocol):
26
- """Server protocol."""
27
-
28
- factory: "ServerFactory"
29
-
30
- def set_factory(self, factory: "ServerFactory") -> None:
31
- """Set the factory.
32
-
33
- Parameters
34
- ----------
35
- factory : ServerFactory
36
- The factory to set.
37
- """
38
- self.factory = factory
39
-
40
- def connectionLost(self, reason: Failure = connectionDone) -> None:
41
- """Handle connection lost event.
42
-
43
- Parameters
44
- ----------
45
- reason : Failure, optional
46
- The reason for the connection loss, by default connectionDone
47
- """
48
- if self.factory.clients["provider"] == self:
49
- self.factory.clients["provider"] = None
50
- LOGGER.info("Input provider disconnected.")
51
- elif self.factory.clients["consumer"] == self:
52
- self.factory.clients["consumer"] = None
53
- LOGGER.info("Input consumer disconnected.")
54
- super().connectionLost(reason)
55
-
56
- def message_received(self, message: str) -> None:
57
- """Handle a message received event.
58
-
59
- Parameters
60
- ----------
61
- message : str
62
- The message received.
63
- """
64
- if message.startswith("REQUEST:"):
65
- prompt = message[len("REQUEST:") :]
66
- if prompt.endswith("\r\n"):
67
- prompt = prompt[: -(len("\r\n"))]
68
- prompt = prompt.strip()
69
- LOGGER.debug("Received request: %s", prompt)
70
- if self.factory.clients["provider"]:
71
- msg = f"PROVIDE:{prompt}" + "\r\n"
72
- transport = self.factory.clients["provider"].transport
73
- transport.write(msg.encode("utf-8")) # type: ignore
74
- else:
75
- LOGGER.error("No provider connected.")
76
- elif message.startswith("USE:"):
77
- response = message[len("USE:") :]
78
- if response.endswith("\r\n"):
79
- response = response[: -(len("\r\n"))]
80
- response = response.strip()
81
- LOGGER.debug("Received response: %s", response)
82
- if self.factory.clients["consumer"]:
83
- msg = f"INPUT:{response}" + "\r\n"
84
- transport = self.factory.clients["consumer"].transport
85
- transport.write(msg.encode("utf-8")) # type: ignore
86
- else:
87
- LOGGER.error("No consumer connected.")
88
-
89
- def dataReceived(self, data: bytes) -> None:
90
- """Handle a data received event.
91
-
92
- Parameters
93
- ----------
94
- data : bytes
95
- The data received.
96
- """
97
- # we might get multiple messages in one chunk
98
- # i.e. CONSUMER\r\nREQUEST:prompt\r\n
99
- message = data.decode("utf-8")
100
- if message in ("PROVIDER\r\n", "PROVIDER\n", "PROVIDER"):
101
- LOGGER.debug("Input provider connected.")
102
- self.factory.clients["provider"] = self
103
- return
104
- if message.startswith("CONSUMER\r\n"):
105
- LOGGER.debug("Input consumer connected.")
106
- self.factory.clients["consumer"] = self
107
- rest = message[len("CONSUMER\r\n") :]
108
- if rest:
109
- self.message_received(rest)
110
- return
111
- self.message_received(message)
112
-
113
-
114
- class ServerFactory(Factory):
115
- """Server factory."""
116
-
117
- protocol: "ServerProtocol" # type: ignore
118
- clients: Dict[str, Optional["ServerProtocol"]]
119
-
120
- def __init__(self) -> None:
121
- """Initialize the factory."""
122
- super().__init__()
123
- self.clients = {
124
- "provider": None,
125
- "consumer": None,
126
- }
127
-
128
- def buildProtocol(self, addr: str) -> "ServerProtocol":
129
- """Build the protocol.
130
-
131
- Parameters
132
- ----------
133
- addr : str
134
- The address (ignored)
135
-
136
- Returns
137
- -------
138
- ServerProtocol
139
- The factory's protocol.
140
- """
141
- self.protocol = ServerProtocol()
142
- self.protocol.set_factory(self)
143
- return self.protocol
144
-
145
-
146
- def get_reactor() -> IReactorCore:
147
- """Get the reactor from twisted.
148
-
149
- Returns
150
- -------
151
- IReactorCore
152
- The twisted's reactor
153
- """
154
- # dummy hack to allow restarting the reactor
155
- if "twisted.internet.reactor" in sys.modules:
156
- del sys.modules["twisted.internet.reactor"]
157
- import twisted.internet.error
158
- from twisted.internet import reactor # noqa
159
- from twisted.internet import default
160
-
161
- try:
162
- default.install()
163
- # pylint: disable=line-too-long
164
- except (
165
- twisted.internet.error.ReactorAlreadyInstalledError
166
- ): # pragma: no cover
167
- pass
168
- # cast it so mypy doesn't complain a lot
169
- reactor_cast = cast(IReactorCore, reactor)
170
- return reactor_cast
171
-
172
-
173
- class TCPServerThread(Thread):
174
- """Threaded TCP server."""
175
-
176
- reactor: Optional[IReactorCore] = None # noqa
177
- factory: Optional[Factory] = None # noqa
178
-
179
- def __init__(
180
- self,
181
- interface: str,
182
- port: int,
183
- ) -> None:
184
- """Create a new TCP server.
185
-
186
- Parameters
187
- ----------
188
- interface : str
189
- Interface to listen on. Defaults to '' (all interfaces)
190
- port : int
191
- Port to listen on.
192
- """
193
- super().__init__(
194
- name="TCPServerThread",
195
- daemon=False,
196
- target=self.run,
197
- )
198
- self.reactor = get_reactor()
199
- self._port = port
200
- self._interface = interface
201
- self._initialize()
202
-
203
- def _initialize(self) -> None:
204
- """Initialize the server."""
205
- from twisted.internet.endpoints import TCP4ServerEndpoint
206
-
207
- self.endpoint = TCP4ServerEndpoint( # type: ignore[no-untyped-call]
208
- self.reactor,
209
- self._port,
210
- interface=self._interface,
211
- )
212
- server_factory = ServerFactory()
213
- deferred = self.endpoint.listen(server_factory) # type: ignore
214
- deferred.addCallback(callback=self.on_start)
215
- deferred.addErrback(errback=self.on_error)
216
-
217
- @property
218
- def port(self) -> int:
219
- """Get the port."""
220
- return self._port
221
-
222
- def on_error(self, failure: Failure) -> None: # pragma: no cover
223
- """On error callback.
224
-
225
- Parameters
226
- ----------
227
- failure : Failure
228
- The failure.
229
-
230
- Raises
231
- ------
232
- RuntimeError
233
- If the failure is not handled.
234
- """
235
- LOGGER.error(failure.getErrorMessage())
236
- del self.endpoint
237
- self._initialize()
238
-
239
- def on_start(self, port: Port) -> None:
240
- """On connect callback.
241
-
242
- Parameters
243
- ----------
244
- port : Port
245
- The port to connect to.
246
- """
247
- socket = port.getHost() # type: ignore[no-untyped-call]
248
- LOGGER.debug(
249
- "listening on %s:%s",
250
- socket.host,
251
- socket.port,
252
- )
253
- self._port = socket.port
254
- self.factory = port.factory
255
-
256
- def run(self) -> None:
257
- """Start the server.
258
-
259
- Raises
260
- ------
261
- RuntimeError
262
- If reactor is not initialized
263
- """
264
- if self.reactor is None: # pragma: no cover (just for the linter)
265
- raise RuntimeError("reactor is not running")
266
- if not self.reactor.running:
267
- try:
268
- self.reactor.run(installSignalHandlers=False) # type: ignore
269
- except ReactorNotRestartable: # pragma: no cover
270
- self.reactor = get_reactor()
271
- self.reactor.run(installSignalHandlers=False) # type: ignore
272
-
273
-
274
- class ServerWrapper:
275
- """Server Wrapper."""
276
-
277
- server: TCPServerThread
278
- timeout: float
279
-
280
- def __init__(self, interface: str, port: int) -> None:
281
- """Create a new TCP server.
282
-
283
- Parameters
284
- ----------
285
- interface : str
286
- Interface to listen on. Defaults to '' (all interfaces)
287
- port : int
288
- Port to listen on.
289
- """
290
- self._interface = interface
291
- self._port = port
292
- self._init_server()
293
-
294
- def _init_server(self) -> None:
295
- """Initialize the server."""
296
- self.server = TCPServerThread(
297
- interface=self._interface, port=self._port
298
- )
299
- retries = 0
300
- while self.server.factory is None and retries < 30: # pragma: no cover
301
- retries += 1
302
- time.sleep(1)
303
- if self.server.factory is None: # pragma: no cover
304
- raise RuntimeError("Server not started")
305
-
306
- @property
307
- def port(self) -> int:
308
- """Get the port.
309
-
310
- Raises
311
- ------
312
- RuntimeError
313
- If the server is not running
314
- """
315
- if self.server.factory is None: # pragma: no cover
316
- raise RuntimeError("server is not running")
317
- return self.server.port
318
-
319
- def start(self) -> None:
320
- """Start the server.
321
-
322
- Raises
323
- ------
324
- RuntimeError
325
- If the server is not running
326
- """
327
- if self.server.factory is None: # pragma: no cover
328
- del self.server
329
- self._init_server()
330
- self.server.start()
331
-
332
- def stop(self) -> None:
333
- """Stop the server."""
334
- # pylint: disable=line-too-long
335
- self.server.reactor.callFromThread(self.server.reactor.stop) # type: ignore # noqa
336
- self.server.join()
337
-
338
-
339
- class TCPServer:
340
- """TCP Server."""
341
-
342
- _wrapper: Optional[ServerWrapper] = None
343
-
344
- def __init__(
345
- self,
346
- port: int,
347
- timeout: Optional[float] = None,
348
- interface: str = "",
349
- ) -> None:
350
- """Create a new server.
351
-
352
- Parameters
353
- ----------
354
- port : int
355
- Port to listen on.
356
- timeout : Optional[float]
357
- Timeout for the server.
358
- interface : str
359
- Interface to listen on. Defaults to '' (all interfaces)
360
- """
361
- self._port = port
362
- self._timeout = timeout
363
- self._interface = interface
364
- self._init_wrapper()
365
- self._running = False
366
-
367
- @property
368
- def port(self) -> int:
369
- """Get the port."""
370
- if self._wrapper is None: # pragma: no cover
371
- return self._port
372
- return self._wrapper.port
373
-
374
- def _init_wrapper(self) -> None:
375
- """Initialize the wrapper."""
376
- self._wrapper = ServerWrapper(
377
- port=self._port,
378
- interface=self._interface,
379
- )
380
-
381
- def start(self) -> None:
382
- """Start the server.
383
-
384
- Raises
385
- ------
386
- RuntimeError
387
- If the wrapper is not initialized
388
- """
389
- if self._running:
390
- return
391
- if not self._wrapper:
392
- self._init_wrapper()
393
- if not self._wrapper: # pragma: no cover (just for the linter)
394
- raise RuntimeError("Server wrapper is not initialized")
395
- self._wrapper.start()
396
- self._port = self._wrapper.port
397
- self._running = True
398
-
399
- def stop(self) -> None:
400
- """Stop the server."""
401
- if not self._running:
402
- return
403
- if not self._wrapper: # pragma: no cover (just for the linter)
404
- return
405
- self._wrapper.stop()
406
- self._running = False
407
- del self._wrapper
408
- self._wrapper = None
409
-
410
- def is_running(self) -> bool:
411
- """Check if the server is running.
412
-
413
- Returns
414
- -------
415
- bool
416
- True if the server is running, else False.
417
- """
418
- return self._running
419
-
420
- def __enter__(self) -> "TCPServer":
421
- """Enter the context manager."""
422
- self.start()
423
- return self
424
-
425
- def __exit__(
426
- self,
427
- exc_type: Optional[Type[BaseException]],
428
- exc_value: Optional[BaseException],
429
- traceback: Optional[TracebackType],
430
- ) -> None:
431
- """Exit the context manager."""
432
- self.stop()
433
-
434
- def restart(self) -> None:
435
- """Restart the server."""
436
- self.stop()
437
- self._init_wrapper()
438
- self.start()