cli-ih 0.7.1__tar.gz → 0.7.1.1__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.
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/PKG-INFO +1 -1
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih/asyncClient.py +16 -37
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih/client.py +19 -50
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih/utils.py +45 -8
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih.egg-info/PKG-INFO +1 -1
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/pyproject.toml +1 -1
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/README.md +0 -0
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih/__init__.py +0 -0
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih/exceptions.py +0 -0
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih.egg-info/SOURCES.txt +0 -0
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih.egg-info/dependency_links.txt +0 -0
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/cli_ih.egg-info/top_level.txt +0 -0
- {cli_ih-0.7.1 → cli_ih-0.7.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Callable, Any
|
|
2
2
|
from .exceptions import HandlerClosed
|
|
3
|
-
from .utils import safe_print as print, register_handler
|
|
3
|
+
from .utils import safe_print as print, register_handler, SafeLogger
|
|
4
4
|
import logging, warnings, asyncio, inspect, threading, sys, shutil, msvcrt
|
|
5
5
|
|
|
6
6
|
class AsyncInputHandler:
|
|
@@ -10,8 +10,8 @@ class AsyncInputHandler:
|
|
|
10
10
|
self.is_running = False
|
|
11
11
|
self.thread_mode = thread_mode
|
|
12
12
|
self.cursor = f"{cursor.strip()} " if cursor else ""
|
|
13
|
-
self.global_logger = logger if logger else
|
|
14
|
-
self.logger = logger.getChild("InputHandler") if logger else
|
|
13
|
+
self.global_logger = logger if logger else SafeLogger()
|
|
14
|
+
self.logger = logger.getChild("InputHandler") if logger else self.global_logger
|
|
15
15
|
self.register_defaults = register_defaults
|
|
16
16
|
self.print_lock = threading.Lock()
|
|
17
17
|
self.input_buffer = ""
|
|
@@ -28,34 +28,19 @@ class AsyncInputHandler:
|
|
|
28
28
|
return self.logger
|
|
29
29
|
|
|
30
30
|
def __debug(self, msg: str):
|
|
31
|
-
|
|
32
|
-
self.logger.debug(msg)
|
|
33
|
-
else:
|
|
34
|
-
print(f"[DEBUG]: {msg}")
|
|
31
|
+
self.logger.debug(msg)
|
|
35
32
|
|
|
36
33
|
def __info(self, msg: str):
|
|
37
|
-
|
|
38
|
-
self.logger.info(msg)
|
|
39
|
-
else:
|
|
40
|
-
print(f"[INFO]: {msg}")
|
|
34
|
+
self.logger.info(msg)
|
|
41
35
|
|
|
42
36
|
def __warning(self, msg: str):
|
|
43
|
-
|
|
44
|
-
self.logger.warning(msg)
|
|
45
|
-
else:
|
|
46
|
-
print(f"[WARNING]: {msg}")
|
|
37
|
+
self.logger.warning(msg)
|
|
47
38
|
|
|
48
39
|
def __error(self, msg: str):
|
|
49
|
-
|
|
50
|
-
self.logger.error(msg)
|
|
51
|
-
else:
|
|
52
|
-
print(f"[ERROR]: {msg}")
|
|
40
|
+
self.logger.error(msg)
|
|
53
41
|
|
|
54
42
|
def __exeption(self, msg: str, e: Exception):
|
|
55
|
-
|
|
56
|
-
self.logger.exception(f"{msg}: {e}")
|
|
57
|
-
else:
|
|
58
|
-
print(f"[EXEPTION]: {msg}: {e}")
|
|
43
|
+
self.logger.exception(f"{msg}: {e}")
|
|
59
44
|
|
|
60
45
|
def __register_cmd(self, name: str, func: Callable[..., Any], description: str = "", legacy=False):
|
|
61
46
|
name = name.lower()
|
|
@@ -98,7 +83,6 @@ class AsyncInputHandler:
|
|
|
98
83
|
input_queue = asyncio.Queue()
|
|
99
84
|
|
|
100
85
|
def _input_worker():
|
|
101
|
-
# Initial prompt
|
|
102
86
|
with self.print_lock:
|
|
103
87
|
sys.stdout.write(self.cursor)
|
|
104
88
|
sys.stdout.flush()
|
|
@@ -108,10 +92,10 @@ class AsyncInputHandler:
|
|
|
108
92
|
if msvcrt.kbhit():
|
|
109
93
|
char = msvcrt.getwch()
|
|
110
94
|
|
|
111
|
-
if char == '\xe0' or char == '\x00':
|
|
95
|
+
if char == '\xe0' or char == '\x00':
|
|
112
96
|
try:
|
|
113
97
|
scancode = msvcrt.getwch()
|
|
114
|
-
if scancode == 'H':
|
|
98
|
+
if scancode == 'H':
|
|
115
99
|
if self.history_index > 0:
|
|
116
100
|
self.history_index -= 1
|
|
117
101
|
self.input_buffer = self.history[self.history_index]
|
|
@@ -120,7 +104,7 @@ class AsyncInputHandler:
|
|
|
120
104
|
sys.stdout.write(self.cursor + self.input_buffer)
|
|
121
105
|
sys.stdout.flush()
|
|
122
106
|
|
|
123
|
-
elif scancode == 'P':
|
|
107
|
+
elif scancode == 'P':
|
|
124
108
|
if self.history_index < len(self.history):
|
|
125
109
|
self.history_index += 1
|
|
126
110
|
|
|
@@ -136,7 +120,7 @@ class AsyncInputHandler:
|
|
|
136
120
|
except Exception:
|
|
137
121
|
pass
|
|
138
122
|
|
|
139
|
-
elif char == '\r':
|
|
123
|
+
elif char == '\r':
|
|
140
124
|
with self.print_lock:
|
|
141
125
|
sys.stdout.write('\n')
|
|
142
126
|
sys.stdout.flush()
|
|
@@ -150,29 +134,24 @@ class AsyncInputHandler:
|
|
|
150
134
|
|
|
151
135
|
loop.call_soon_threadsafe(input_queue.put_nowait, text)
|
|
152
136
|
|
|
153
|
-
elif char == '\x08':
|
|
137
|
+
elif char == '\x08':
|
|
154
138
|
if len(self.input_buffer) > 0:
|
|
155
139
|
self.input_buffer = self.input_buffer[:-1]
|
|
156
140
|
with self.print_lock:
|
|
157
141
|
sys.stdout.write('\b \b')
|
|
158
142
|
sys.stdout.flush()
|
|
159
143
|
|
|
160
|
-
elif char == '\x03':
|
|
144
|
+
elif char == '\x03':
|
|
161
145
|
loop.call_soon_threadsafe(input_queue.put_nowait, KeyboardInterrupt)
|
|
162
146
|
break
|
|
163
147
|
|
|
164
148
|
else:
|
|
165
|
-
# Verify printable
|
|
166
149
|
if char.isprintable():
|
|
167
150
|
self.input_buffer += char
|
|
168
151
|
with self.print_lock:
|
|
169
152
|
sys.stdout.write(char)
|
|
170
153
|
sys.stdout.flush()
|
|
171
154
|
else:
|
|
172
|
-
pass
|
|
173
|
-
# Small sleep to prevent high CPU usage,
|
|
174
|
-
# but we are in a dedicated thread so it's fine-ish,
|
|
175
|
-
# actually explicit sleep is good.
|
|
176
155
|
import time
|
|
177
156
|
time.sleep(0.01)
|
|
178
157
|
|
|
@@ -182,7 +161,7 @@ class AsyncInputHandler:
|
|
|
182
161
|
thread = threading.Thread(target=_input_worker, daemon=True)
|
|
183
162
|
thread.start()
|
|
184
163
|
|
|
185
|
-
async def _run_command(commands: dict, name: str, args: list):
|
|
164
|
+
async def _run_command(commands: dict[str, dict[str, Any]], name: str, args: list[str]):
|
|
186
165
|
"""Executes a command from the command dictionary if it exists."""
|
|
187
166
|
command = commands.get(name)
|
|
188
167
|
if not command:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from typing import Callable
|
|
1
|
+
from typing import Callable, Any
|
|
2
2
|
from .exceptions import HandlerClosed
|
|
3
|
-
from .utils import safe_print as print, register_handler
|
|
4
|
-
import logging, warnings,
|
|
3
|
+
from .utils import safe_print as print, register_handler, SafeLogger
|
|
4
|
+
import logging, sys, threading, warnings, inspect, shutil, msvcrt
|
|
5
5
|
|
|
6
6
|
class InputHandler:
|
|
7
7
|
def __init__(self, thread_mode = True, cursor = "", *, logger: logging.Logger | None = None, register_defaults: bool = True):
|
|
@@ -11,8 +11,8 @@ class InputHandler:
|
|
|
11
11
|
self.thread_mode = thread_mode
|
|
12
12
|
self.cursor = f"{cursor.strip()} " if cursor else ""
|
|
13
13
|
self.thread = None
|
|
14
|
-
self.global_logger = logger if logger else
|
|
15
|
-
self.logger = logger.getChild("InputHandler") if logger else
|
|
14
|
+
self.global_logger = logger if logger else SafeLogger()
|
|
15
|
+
self.logger = logger.getChild("InputHandler") if logger else self.global_logger
|
|
16
16
|
self.register_defaults = register_defaults
|
|
17
17
|
self.print_lock = threading.Lock()
|
|
18
18
|
self.input_buffer = ""
|
|
@@ -29,36 +29,21 @@ class InputHandler:
|
|
|
29
29
|
return self.logger
|
|
30
30
|
|
|
31
31
|
def __debug(self, msg: str):
|
|
32
|
-
|
|
33
|
-
self.logger.debug(msg)
|
|
34
|
-
else:
|
|
35
|
-
print(f"[DEBUG]: {msg}")
|
|
32
|
+
self.logger.debug(msg)
|
|
36
33
|
|
|
37
34
|
def __info(self, msg: str):
|
|
38
|
-
|
|
39
|
-
self.logger.info(msg)
|
|
40
|
-
else:
|
|
41
|
-
print(f"[INFO]: {msg}")
|
|
35
|
+
self.logger.info(msg)
|
|
42
36
|
|
|
43
37
|
def __warning(self, msg: str):
|
|
44
|
-
|
|
45
|
-
self.logger.warning(msg)
|
|
46
|
-
else:
|
|
47
|
-
print(f"[WARNING]: {msg}")
|
|
38
|
+
self.logger.warning(msg)
|
|
48
39
|
|
|
49
40
|
def __error(self, msg: str):
|
|
50
|
-
|
|
51
|
-
self.logger.error(msg)
|
|
52
|
-
else:
|
|
53
|
-
print(f"[ERROR]: {msg}")
|
|
41
|
+
self.logger.error(msg)
|
|
54
42
|
|
|
55
43
|
def __exeption(self, msg: str, e: Exception):
|
|
56
|
-
|
|
57
|
-
self.logger.exception(f"{msg}: {e}")
|
|
58
|
-
else:
|
|
59
|
-
print(f"[EXEPTION]: {msg}: {e}")
|
|
44
|
+
self.logger.exception(f"{msg}: {e}")
|
|
60
45
|
|
|
61
|
-
def __register_cmd(self, name: str, func: Callable, description: str = "", legacy=False):
|
|
46
|
+
def __register_cmd(self, name: str, func: Callable[..., Any], description: str = "", legacy=False):
|
|
62
47
|
name = name.lower()
|
|
63
48
|
if not description:
|
|
64
49
|
description = "A command"
|
|
@@ -68,14 +53,14 @@ class InputHandler:
|
|
|
68
53
|
raise SyntaxError(f"Command '{name}' is already registered. If theese commands have a different case and they need to stay the same, downgrade the package version to 0.5.x")
|
|
69
54
|
self.commands[name] = {"cmd": func, "description": description, "legacy": legacy}
|
|
70
55
|
|
|
71
|
-
def register_command(self, name: str, func: Callable, description: str = ""):
|
|
56
|
+
def register_command(self, name: str, func: Callable[..., Any], description: str = ""):
|
|
72
57
|
"""(DEPRECATED) Registers a command with its associated function."""
|
|
73
58
|
warnings.warn("Registering commands with `register_command` is deprecated, and should not be used.", DeprecationWarning, 2)
|
|
74
59
|
self.__register_cmd(name, func, description, legacy=True)
|
|
75
60
|
|
|
76
61
|
def command(self, *, name: str = "", description: str = ""):
|
|
77
62
|
"""Registers a command with its associated function as a decorator."""
|
|
78
|
-
def decorator(func: Callable):
|
|
63
|
+
def decorator(func: Callable[..., Any]):
|
|
79
64
|
lname = name or func.__name__
|
|
80
65
|
self.__register_cmd(lname, func, description)
|
|
81
66
|
return func
|
|
@@ -138,7 +123,6 @@ class InputHandler:
|
|
|
138
123
|
"""Continuously listens for user input and processes commands."""
|
|
139
124
|
while self.is_running:
|
|
140
125
|
try:
|
|
141
|
-
# Initial prompt
|
|
142
126
|
with self.print_lock:
|
|
143
127
|
sys.stdout.write(self.cursor)
|
|
144
128
|
sys.stdout.flush()
|
|
@@ -147,29 +131,19 @@ class InputHandler:
|
|
|
147
131
|
if msvcrt.kbhit():
|
|
148
132
|
char = msvcrt.getwch()
|
|
149
133
|
|
|
150
|
-
if char == '\xe0' or char == '\x00':
|
|
134
|
+
if char == '\xe0' or char == '\x00':
|
|
151
135
|
try:
|
|
152
136
|
scancode = msvcrt.getwch()
|
|
153
|
-
if scancode == 'H':
|
|
137
|
+
if scancode == 'H':
|
|
154
138
|
if self.history_index > 0:
|
|
155
139
|
self.history_index -= 1
|
|
156
140
|
self.input_buffer = self.history[self.history_index]
|
|
157
141
|
with self.print_lock:
|
|
158
|
-
# Clear current line visually
|
|
159
|
-
# We don't have columns info here easily, but safe_print logic uses clearing.
|
|
160
|
-
# Let's try to just use \r and spaces? No, let's just use \r and overwrite.
|
|
161
|
-
# To properly clear, we need to know the length of what was there.
|
|
162
|
-
# A reuse of _safe_print logic might be good but avoiding circularity.
|
|
163
|
-
# Simple clear strategy: \r, print many spaces, \r, print prompt + buffer
|
|
164
|
-
# Or better: \r + prompt + buffer + spaces to clear rest?
|
|
165
|
-
|
|
166
|
-
# We'll use a crude clear for now or simple overwrite if shorter?
|
|
167
|
-
# Best to clear the line.
|
|
168
142
|
sys.stdout.write('\r' + ' ' * (shutil.get_terminal_size().columns - 1) + '\r')
|
|
169
143
|
sys.stdout.write(self.cursor + self.input_buffer)
|
|
170
144
|
sys.stdout.flush()
|
|
171
145
|
|
|
172
|
-
elif scancode == 'P':
|
|
146
|
+
elif scancode == 'P':
|
|
173
147
|
if self.history_index < len(self.history):
|
|
174
148
|
self.history_index += 1
|
|
175
149
|
|
|
@@ -185,7 +159,7 @@ class InputHandler:
|
|
|
185
159
|
except Exception:
|
|
186
160
|
pass
|
|
187
161
|
|
|
188
|
-
elif char == '\r':
|
|
162
|
+
elif char == '\r':
|
|
189
163
|
with self.print_lock:
|
|
190
164
|
sys.stdout.write('\n')
|
|
191
165
|
sys.stdout.flush()
|
|
@@ -193,9 +167,6 @@ class InputHandler:
|
|
|
193
167
|
self.input_buffer = ""
|
|
194
168
|
|
|
195
169
|
if text:
|
|
196
|
-
# Add to history if unique or last one different?
|
|
197
|
-
# Usually standard behavior: always add, or add if different from last.
|
|
198
|
-
# Let's add if not empty.
|
|
199
170
|
if not self.history or self.history[-1] != text:
|
|
200
171
|
self.history.append(text)
|
|
201
172
|
self.history_index = len(self.history)
|
|
@@ -210,23 +181,21 @@ class InputHandler:
|
|
|
210
181
|
self.__warning(f"Unknown command: '{command_name}'")
|
|
211
182
|
self.processing_command = False
|
|
212
183
|
|
|
213
|
-
# Break inner loop to reprompt
|
|
214
184
|
break
|
|
215
185
|
|
|
216
|
-
elif char == '\x08':
|
|
186
|
+
elif char == '\x08':
|
|
217
187
|
if len(self.input_buffer) > 0:
|
|
218
188
|
self.input_buffer = self.input_buffer[:-1]
|
|
219
189
|
with self.print_lock:
|
|
220
190
|
sys.stdout.write('\b \b')
|
|
221
191
|
sys.stdout.flush()
|
|
222
192
|
|
|
223
|
-
elif char == '\x03':
|
|
193
|
+
elif char == '\x03':
|
|
224
194
|
self.__error("Input interrupted.")
|
|
225
195
|
self.is_running = False
|
|
226
196
|
return
|
|
227
197
|
|
|
228
198
|
else:
|
|
229
|
-
# Verify printable
|
|
230
199
|
if char.isprintable():
|
|
231
200
|
self.input_buffer += char
|
|
232
201
|
with self.print_lock:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shutil
|
|
2
2
|
import sys
|
|
3
|
+
import threading
|
|
3
4
|
|
|
4
5
|
_HANDLER = None
|
|
5
6
|
|
|
@@ -7,15 +8,55 @@ def register_handler(handler):
|
|
|
7
8
|
global _HANDLER
|
|
8
9
|
_HANDLER = handler
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
class SafeLogger:
|
|
12
|
+
"""A dummy logger that uses safe_print to output logs to the console."""
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.handlers = []
|
|
15
|
+
|
|
16
|
+
def debug(self, msg: str):
|
|
17
|
+
safe_print(f"[DEBUG]: {msg}")
|
|
18
|
+
|
|
19
|
+
def info(self, msg: str):
|
|
20
|
+
safe_print(f"[INFO]: {msg}")
|
|
21
|
+
|
|
22
|
+
def warning(self, msg: str):
|
|
23
|
+
safe_print(f"[WARNING]: {msg}")
|
|
24
|
+
|
|
25
|
+
def error(self, msg: str):
|
|
26
|
+
safe_print(f"[ERROR]: {msg}")
|
|
27
|
+
|
|
28
|
+
def critical(self, msg: str):
|
|
29
|
+
safe_print(f"[CRITICAL]: {msg}")
|
|
30
|
+
|
|
31
|
+
def exception(self, msg: str):
|
|
32
|
+
safe_print(f"[EXCEPTION]: {msg}")
|
|
33
|
+
|
|
34
|
+
def log(self, level, msg: str):
|
|
35
|
+
safe_print(f"[LOG {level}]: {msg}")
|
|
36
|
+
|
|
37
|
+
def getChild(self, name):
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
def setLevel(self, level):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def getEffectiveLevel(self):
|
|
44
|
+
return 0
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def safe_print(msg: object, cursor: str | None = None, input_buffer: str | None = None):
|
|
11
48
|
"""
|
|
12
49
|
Prints a message safely while preserving the current input buffer and cursor.
|
|
13
50
|
This ensures logs appear above the input line appropriately.
|
|
14
51
|
"""
|
|
52
|
+
try:
|
|
53
|
+
msg = str(msg)
|
|
54
|
+
except:
|
|
55
|
+
msg = "<Unprintable Object>"
|
|
56
|
+
|
|
15
57
|
if cursor is None and input_buffer is None and _HANDLER is not None:
|
|
16
|
-
lock = None
|
|
58
|
+
lock: threading.Lock | None = None
|
|
17
59
|
try:
|
|
18
|
-
# Try to acquire lock if available to prevent race conditions
|
|
19
60
|
lock = getattr(_HANDLER, "print_lock", None)
|
|
20
61
|
if lock:
|
|
21
62
|
lock.acquire()
|
|
@@ -33,8 +74,7 @@ def safe_print(msg: str, cursor: str | None = None, input_buffer: str | None = N
|
|
|
33
74
|
if lock:
|
|
34
75
|
lock.release()
|
|
35
76
|
else:
|
|
36
|
-
|
|
37
|
-
_do_safe_print(msg, cursor or "", input_buffer or "")
|
|
77
|
+
_do_safe_print(msg, str(cursor or ""), str(input_buffer or ""))
|
|
38
78
|
|
|
39
79
|
def _do_safe_print(msg: str, cursor: str, input_buffer: str):
|
|
40
80
|
try:
|
|
@@ -42,10 +82,7 @@ def _do_safe_print(msg: str, cursor: str, input_buffer: str):
|
|
|
42
82
|
except:
|
|
43
83
|
columns = 80
|
|
44
84
|
|
|
45
|
-
# Clear line
|
|
46
85
|
sys.stdout.write('\r' + ' ' * (columns - 1) + '\r')
|
|
47
|
-
# Print message
|
|
48
86
|
sys.stdout.write(f"{msg}\n")
|
|
49
|
-
# Reprint cursor and buffer
|
|
50
87
|
sys.stdout.write(f"{cursor}{input_buffer}")
|
|
51
88
|
sys.stdout.flush()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|