drifTech-lib 0.2.7__tar.gz → 0.3.0__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.

Potentially problematic release.


This version of drifTech-lib might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drifTech-lib
3
- Version: 0.2.7
3
+ Version: 0.3.0
4
4
  Summary: Common files/functions across most drifTech software
5
5
  Author: driftbluestone
6
6
  Author-email: driftbluestone <driftbluestone@gmail.com>
@@ -8,7 +8,7 @@ build-backend = "uv_build"
8
8
 
9
9
  [project]
10
10
  name = "drifTech-lib"
11
- version = "0.2.7"
11
+ version = "0.3.0"
12
12
  description = "Common files/functions across most drifTech software"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.14"
@@ -8,7 +8,7 @@ build-backend = "uv_build"
8
8
 
9
9
  [project]
10
10
  name = "drifTech-lib"
11
- version = "0.2.7"
11
+ version = "0.3.0"
12
12
  authors = [{name = "driftbluestone", email = "driftbluestone@gmail.com"}]
13
13
  description = "Common files/functions across most drifTech software"
14
14
  readme = "README.md"
@@ -4,9 +4,9 @@ __all__ = ["Connector"]
4
4
 
5
5
  class Connector:
6
6
  """
7
- Inherit this class to overwrite the on_message() function
7
+ Inherit this class to overwrite the on_message() function.
8
8
 
9
- run await Connector().start() to connect to DCM-core
9
+ Run `await Connector().start()` to initialize a connection.
10
10
  """
11
11
  def __init__(self, HOST, PORT):
12
12
  self.HOST = HOST
@@ -35,7 +35,7 @@ class Connector:
35
35
  """Background task to continuously read any data pushed by the server."""
36
36
  try:
37
37
  while True:
38
- data = await reader.read(1024)
38
+ data = await reader.readline()
39
39
  if not data:
40
40
  logger.error("[DISCONNECTED] Server closed the connection.")
41
41
  break
@@ -46,9 +46,7 @@ class Connector:
46
46
  logger.error(f"[ERROR] Reading error: {e}")
47
47
 
48
48
  async def on_message(self, message: bytes):
49
- """
50
- Override this function
51
- """
49
+ """Override this function."""
52
50
  pass
53
51
 
54
52
  async def send_message(self, message: str | bytes):
@@ -57,7 +55,6 @@ class Connector:
57
55
  await self.command_queue.put(message)
58
56
 
59
57
  async def _send(self, writer: asyncio.StreamWriter):
60
- loop = asyncio.get_running_loop()
61
58
  try:
62
59
  while True:
63
60
  user_input: bytes = await self.command_queue.get()
@@ -0,0 +1,100 @@
1
+ import asyncio, logging, sys
2
+ logger = logging.getLogger("ipc:server")
3
+ logging.basicConfig(stream=sys.stdout, level=logging.INFO)
4
+ __all__ = ["Server", "connections"]
5
+ Port = int
6
+
7
+ conn_count = 1
8
+ connections: dict[Port, Server] = {}
9
+
10
+ class Server:
11
+ """
12
+ Inherit this class to overwrite the on_message() function.
13
+
14
+ Run `await Server().start()` to initialize a server.
15
+ Do not apply class attributes when initializing,
16
+ they are only used for the connections,
17
+ which can be accessed within the `connections` dict.
18
+ """
19
+ def __init__(self, addr: str = None, port: int = None,
20
+ reader: asyncio.StreamReader = None,
21
+ writer: asyncio.StreamWriter = None):
22
+
23
+ self.addr = addr
24
+ self.port = port
25
+ self.reader = reader
26
+ self.writer = writer
27
+ self.command_queue: asyncio.Queue[bytes] = asyncio.Queue(10)
28
+
29
+ async def start(self, HOST = "127.0.0.1", PORT = 8000):
30
+ server = await asyncio.start_server(self._interface, HOST, PORT)
31
+ logger.info(f"[LISTENING] Server is running on {HOST}:{PORT}")
32
+ async with server:
33
+ await server.serve_forever()
34
+
35
+ async def _interface(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
36
+ global conn_count
37
+ addr, port = writer.get_extra_info("peername")
38
+ logger.info(f"[CONNECTION] Connection opened for {addr} #{conn_count}")
39
+ conn_count += 1
40
+ try:
41
+ conn = self.__class__(addr, port, reader, writer)
42
+ connections[port] = conn
43
+ await conn._run()
44
+ except (asyncio.CancelledError, ConnectionResetError, BrokenPipeError, OSError):
45
+ conn_count -= 1
46
+ logger.info(f"[DISCONNECTED] Connection closed for {addr} #{conn_count-1}")
47
+ finally:
48
+ conn_count -= 1
49
+ logger.info(f"[DISCONNECTED] Connection closed for {addr} #{conn_count-1}")
50
+
51
+ async def _run(self):
52
+ listen_task = asyncio.create_task(self._listen())
53
+
54
+ await self._send()
55
+
56
+ listen_task.cancel()
57
+ self.writer.close()
58
+ await self.writer.wait_closed()
59
+
60
+ async def _listen(self):
61
+ try:
62
+ while True:
63
+ data = await self.reader.readline()
64
+ if not data:
65
+ break
66
+ await self.on_message(data)
67
+ except asyncio.CancelledError:
68
+ pass
69
+ except Exception as e:
70
+ logger.error(f"[ERROR] Reading error: {e}")
71
+
72
+ async def on_message(self):
73
+ """Override this function."""
74
+ pass
75
+
76
+ async def send_message(self, message: bytes):
77
+ if isinstance(message, str):
78
+ message = message.encode()
79
+ await self.command_queue.put(message)
80
+
81
+ async def _send(self):
82
+ try:
83
+ while True:
84
+ user_input: bytes = await self.command_queue.get()
85
+ message = user_input.strip()
86
+
87
+ if not message:
88
+ continue
89
+
90
+ if message.lower() == b"exit":
91
+ logger.info("Closing connection...")
92
+ break
93
+
94
+ self.writer.write(message)
95
+ await self.writer.drain()
96
+
97
+ except Exception as e:
98
+ logger.error(f"Writing error: {e}")
99
+
100
+ asyncio.run(Server().start())
@@ -1,35 +0,0 @@
1
- import asyncio, logging
2
- logger = logging.getLogger("ipc:server")
3
- __all__ = ["start"]
4
-
5
- open_connections = 1
6
-
7
- async def start(HOST = "127.0.0.1", PORT = 8000):
8
- server = await asyncio.start_server(interface, HOST, PORT)
9
- logger.info(f"[LISTENING] Server is running on {HOST}:{PORT}")
10
- async with server:
11
- await server.serve_forever()
12
-
13
- async def interface(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
14
- global open_connections
15
- addr = writer.get_extra_info("peername")
16
- logger.info(f"[CONNECTION] Connection opened for {addr} #{open_connections}")
17
- open_connections += 1
18
- try:
19
- while True:
20
- data = await reader.read(1024)
21
- if not data:
22
- break
23
- logger.info(f"[{addr}] Received: {data.decode()}")
24
- writer.write(b"Message processed")
25
- await writer.drain() # Ensure data is flushed to the network buffer
26
- except (asyncio.CancelledError, ConnectionResetError, BrokenPipeError, OSError):
27
- open_connections -= 1
28
- logger.info(f"[DISCONNECTED] Connection closed for {addr} #{open_connections-1}")
29
- writer.close()
30
- await writer.wait_closed()
31
- finally:
32
- open_connections -= 1
33
- logger.info(f"[DISCONNECTED] Connection closed for {addr} #{open_connections-1}")
34
- writer.close()
35
- await writer.wait_closed()
File without changes
File without changes