cli-ih 0.7.1__py3-none-any.whl → 0.7.1.1__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.
cli_ih/asyncClient.py CHANGED
@@ -1,6 +1,6 @@
1
- from typing import Coroutine, Callable, Any
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 None
14
- self.logger = logger.getChild("InputHandler") if logger else None
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
- if self.logger:
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
- if self.logger:
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
- if self.logger:
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
- if self.logger:
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
- if self.logger:
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': # Special keys (Arrows, etc)
95
+ if char == '\xe0' or char == '\x00':
112
96
  try:
113
97
  scancode = msvcrt.getwch()
114
- if scancode == 'H': # Up Arrow
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': # Down Arrow
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': # Enter
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': # Backspace
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': # Ctrl+C
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:
cli_ih/client.py CHANGED
@@ -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, sys, shutil, threading, msvcrt
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 None
15
- self.logger = logger.getChild("InputHandler") if logger else None
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
- if self.logger:
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
- if self.logger:
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
- if self.logger:
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
- if self.logger:
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
- if self.logger:
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': # Special keys (Arrows, etc)
134
+ if char == '\xe0' or char == '\x00':
151
135
  try:
152
136
  scancode = msvcrt.getwch()
153
- if scancode == 'H': # Up Arrow
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': # Down Arrow
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': # Enter
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': # Backspace
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': # Ctrl+C
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:
cli_ih/utils.py CHANGED
@@ -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
- def safe_print(msg: str, cursor: str | None = None, input_buffer: str | None = None):
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
- # Fallback or explicit arguments
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli_ih
3
- Version: 0.7.1
3
+ Version: 0.7.1.1
4
4
  Summary: A background command handler for python's command-line interface.
5
5
  Author-email: Hotment <michatchuplay@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -0,0 +1,9 @@
1
+ cli_ih/__init__.py,sha256=wCwv9grczxpu7EXqunCdFgAWlx3XEZVw4O4U3fs45PY,278
2
+ cli_ih/asyncClient.py,sha256=56RLucih5qVNuxFR0EefExyohSheOh0BjomSoTMBEkU,12633
3
+ cli_ih/client.py,sha256=7jrFFrLdXln5VS4EG_YEse9oaa6n4FnUll5rvZNFiTc,12284
4
+ cli_ih/exceptions.py,sha256=0xqaMCVu-yEURIrB6wEhbf6kfdsRmODJBoDsQTOp29s,156
5
+ cli_ih/utils.py,sha256=3WcqvHrwwsiBYlkzIV51_SLsbtqpqEcN5v4Hu3E48Uw,2501
6
+ cli_ih-0.7.1.1.dist-info/METADATA,sha256=LO2NUmmDsuOAGtVEQsXuJZ7Cm3FuF45-1LyXPK7Nx7U,3413
7
+ cli_ih-0.7.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
+ cli_ih-0.7.1.1.dist-info/top_level.txt,sha256=Ve1CRLNXhPyPSkpN0xLu26roh30LQCpNzkF61BZYfk0,7
9
+ cli_ih-0.7.1.1.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- cli_ih/__init__.py,sha256=wCwv9grczxpu7EXqunCdFgAWlx3XEZVw4O4U3fs45PY,278
2
- cli_ih/asyncClient.py,sha256=v7JaKi50xCEiqFUHlxhf83EoXzAwQ-r8VCjdlYz3q4c,13399
3
- cli_ih/client.py,sha256=EKgVCfC-O-Xxdxb7ZqG6DgYvti37wtpjthYns6ghssA,14158
4
- cli_ih/exceptions.py,sha256=0xqaMCVu-yEURIrB6wEhbf6kfdsRmODJBoDsQTOp29s,156
5
- cli_ih/utils.py,sha256=HstJPjjlTfVKz68sHU_whSBsOoBKhxizCcycNyQeK2k,1685
6
- cli_ih-0.7.1.dist-info/METADATA,sha256=rd-ysIOjWIYi8SgIGLbf8TGogLorPQziQCmOej_xQaA,3411
7
- cli_ih-0.7.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
- cli_ih-0.7.1.dist-info/top_level.txt,sha256=Ve1CRLNXhPyPSkpN0xLu26roh30LQCpNzkF61BZYfk0,7
9
- cli_ih-0.7.1.dist-info/RECORD,,