drifTech-lib 0.2.7__tar.gz → 0.4.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.
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/PKG-INFO +1 -1
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/pyproject.toml +1 -1
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/pyproject.toml.orig +1 -1
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/src/driftech_lib/ipc/client.py +4 -7
- driftech_lib-0.4.0/src/driftech_lib/ipc/server.py +100 -0
- driftech_lib-0.4.0/src/driftech_lib/logging.py +123 -0
- driftech_lib-0.2.7/src/driftech_lib/ipc/server.py +0 -35
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/LICENSE +0 -0
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/README.md +0 -0
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/src/driftech_lib/__init__.py +0 -0
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/src/driftech_lib/db.py +0 -0
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/src/driftech_lib/ipc/__init__.py +0 -0
- {driftech_lib-0.2.7 → driftech_lib-0.4.0}/src/driftech_lib/jsonIO.py +0 -0
|
@@ -8,7 +8,7 @@ build-backend = "uv_build"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "drifTech-lib"
|
|
11
|
-
version = "0.
|
|
11
|
+
version = "0.4.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
|
-
|
|
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.
|
|
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())
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import sys, time, inspect
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
|
|
5
|
+
default_LoSs = {
|
|
6
|
+
"UNSET": 0,
|
|
7
|
+
"DEBUG": 10,
|
|
8
|
+
"INFO": 20,
|
|
9
|
+
"WARN": 30,
|
|
10
|
+
"ERROR": 40,
|
|
11
|
+
"FATAL": 50
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class Logger:
|
|
15
|
+
def __init__(self,
|
|
16
|
+
name: str,
|
|
17
|
+
file: str | Path = None,
|
|
18
|
+
reset_file_on_restart: bool = True,
|
|
19
|
+
console = sys.stdout,
|
|
20
|
+
min_console_severity: int = 0,
|
|
21
|
+
min_file_severity: int = 0,
|
|
22
|
+
time_format: str = "%Y-%m-%d %H:%M:%S",
|
|
23
|
+
LoSs: dict[str, int] = default_LoSs,
|
|
24
|
+
):
|
|
25
|
+
self.reset_file_on_restart = reset_file_on_restart
|
|
26
|
+
self.name = name
|
|
27
|
+
self.console = console
|
|
28
|
+
self.file = file
|
|
29
|
+
self.time_format = time_format
|
|
30
|
+
self.LoSs = LoSs
|
|
31
|
+
self.RLoSs = {v: k for k, v in LoSs.items()}
|
|
32
|
+
self.min_console_severity = min_console_severity
|
|
33
|
+
self.min_file_severity = min_file_severity
|
|
34
|
+
|
|
35
|
+
if reset_file_on_restart and file:
|
|
36
|
+
with open(file, "w") as f:
|
|
37
|
+
f.write("")
|
|
38
|
+
|
|
39
|
+
def add_LoS(self, name: str, severity: int):
|
|
40
|
+
self.LoSs[name] = severity
|
|
41
|
+
self.RLoSs[severity] = name
|
|
42
|
+
|
|
43
|
+
def _premessage(self, severity):
|
|
44
|
+
tm = time.strftime(self.time_format)
|
|
45
|
+
return f"{tm} [{self.name}/{self.RLoSs[severity]}]: "
|
|
46
|
+
|
|
47
|
+
async def alog(self, severity: int, content: str, secondary: Callable = None, kwarg: str = None):
|
|
48
|
+
"""Async compatible implementation of `log`"""
|
|
49
|
+
if isinstance(severity, str):
|
|
50
|
+
severity = self.LoSs[severity]
|
|
51
|
+
|
|
52
|
+
content = f"{self._premessage(severity)}{content}\n"
|
|
53
|
+
|
|
54
|
+
if secondary and inspect.iscoroutinefunction(secondary):
|
|
55
|
+
if kwarg:
|
|
56
|
+
await secondary(**{kwarg: content})
|
|
57
|
+
else:
|
|
58
|
+
await secondary(content)
|
|
59
|
+
elif secondary:
|
|
60
|
+
if kwarg:
|
|
61
|
+
secondary(**{kwarg: content})
|
|
62
|
+
else:
|
|
63
|
+
secondary(content)
|
|
64
|
+
|
|
65
|
+
if (severity >= self.min_console_severity) and (self.console):
|
|
66
|
+
self.console.write(content)
|
|
67
|
+
if inspect.iscoroutinefunction(self.console.flush):
|
|
68
|
+
await self.console.flush()
|
|
69
|
+
else:
|
|
70
|
+
self.console.flush()
|
|
71
|
+
|
|
72
|
+
if (severity >= self.min_file_severity) and (self.file):
|
|
73
|
+
with open(self.file, "a") as file:
|
|
74
|
+
file.write(content)
|
|
75
|
+
|
|
76
|
+
def log(self, severity: int, content: str, secondary: Callable = None, kwarg: str = None):
|
|
77
|
+
if isinstance(severity, str):
|
|
78
|
+
severity = self.LoSs[severity]
|
|
79
|
+
|
|
80
|
+
content = f"{self._premessage(severity)}{content}\n"
|
|
81
|
+
|
|
82
|
+
if secondary:
|
|
83
|
+
if kwarg:
|
|
84
|
+
secondary(**{kwarg: content})
|
|
85
|
+
else:
|
|
86
|
+
secondary(content)
|
|
87
|
+
if (severity >= self.min_console_severity) and (self.console):
|
|
88
|
+
self.console.write(content)
|
|
89
|
+
self.console.flush()
|
|
90
|
+
|
|
91
|
+
if (severity >= self.min_file_severity) and (self.file):
|
|
92
|
+
with open(self.file, "a") as file:
|
|
93
|
+
file.write(content)
|
|
94
|
+
|
|
95
|
+
def debug(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
96
|
+
self.log(self.LoSs["DEBUG"], content, secondary, kwarg)
|
|
97
|
+
|
|
98
|
+
def info(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
99
|
+
self.log(self.LoSs["INFO"], content, secondary, kwarg)
|
|
100
|
+
|
|
101
|
+
def warn(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
102
|
+
self.log(self.LoSs["WARN"], content, secondary, kwarg)
|
|
103
|
+
|
|
104
|
+
def error(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
105
|
+
self.log(self.LoSs["ERROR"], content, secondary, kwarg)
|
|
106
|
+
|
|
107
|
+
def fatal(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
108
|
+
self.log(self.LoSs["FATAL"], content, secondary, kwarg)
|
|
109
|
+
|
|
110
|
+
async def adebug(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
111
|
+
await self.alog(self.LoSs["DEBUG"], content, secondary, kwarg)
|
|
112
|
+
|
|
113
|
+
async def ainfo(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
114
|
+
await self.alog(self.LoSs["INFO"], content, secondary, kwarg)
|
|
115
|
+
|
|
116
|
+
async def awarn(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
117
|
+
await self.alog(self.LoSs["WARN"], content, secondary, kwarg)
|
|
118
|
+
|
|
119
|
+
async def aerror(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
120
|
+
await self.alog(self.LoSs["ERROR"], content, secondary, kwarg)
|
|
121
|
+
|
|
122
|
+
async def afatal(self, content: str, secondary: Callable = None, kwarg: str = None):
|
|
123
|
+
await self.alog(self.LoSs["FATAL"], content, secondary, kwarg)
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|