cli-ih 0.7.0__py3-none-any.whl → 0.7.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
@@ -16,6 +16,8 @@ class AsyncInputHandler:
16
16
  self.print_lock = threading.Lock()
17
17
  self.input_buffer = ""
18
18
  self.processing_command = False
19
+ self.history = []
20
+ self.history_index = 0
19
21
 
20
22
  if self.register_defaults:
21
23
  self.register_default_commands()
@@ -106,12 +108,46 @@ class AsyncInputHandler:
106
108
  if msvcrt.kbhit():
107
109
  char = msvcrt.getwch()
108
110
 
109
- if char == '\r': # Enter
111
+ if char == '\xe0' or char == '\x00': # Special keys (Arrows, etc)
112
+ try:
113
+ scancode = msvcrt.getwch()
114
+ if scancode == 'H': # Up Arrow
115
+ if self.history_index > 0:
116
+ self.history_index -= 1
117
+ self.input_buffer = self.history[self.history_index]
118
+ with self.print_lock:
119
+ sys.stdout.write('\r' + ' ' * (shutil.get_terminal_size().columns - 1) + '\r')
120
+ sys.stdout.write(self.cursor + self.input_buffer)
121
+ sys.stdout.flush()
122
+
123
+ elif scancode == 'P': # Down Arrow
124
+ if self.history_index < len(self.history):
125
+ self.history_index += 1
126
+
127
+ if self.history_index == len(self.history):
128
+ self.input_buffer = ""
129
+ else:
130
+ self.input_buffer = self.history[self.history_index]
131
+
132
+ with self.print_lock:
133
+ sys.stdout.write('\r' + ' ' * (shutil.get_terminal_size().columns - 1) + '\r')
134
+ sys.stdout.write(self.cursor + self.input_buffer)
135
+ sys.stdout.flush()
136
+ except Exception:
137
+ pass
138
+
139
+ elif char == '\r': # Enter
110
140
  with self.print_lock:
111
141
  sys.stdout.write('\n')
112
142
  sys.stdout.flush()
113
143
  text = self.input_buffer
114
144
  self.input_buffer = ""
145
+
146
+ if text:
147
+ if not self.history or self.history[-1] != text:
148
+ self.history.append(text)
149
+ self.history_index = len(self.history)
150
+
115
151
  loop.call_soon_threadsafe(input_queue.put_nowait, text)
116
152
 
117
153
  elif char == '\x08': # Backspace
cli_ih/client.py CHANGED
@@ -17,6 +17,8 @@ class InputHandler:
17
17
  self.print_lock = threading.Lock()
18
18
  self.input_buffer = ""
19
19
  self.processing_command = False
20
+ self.history = []
21
+ self.history_index = 0
20
22
 
21
23
  if self.register_defaults:
22
24
  self.register_default_commands()
@@ -145,7 +147,45 @@ class InputHandler:
145
147
  if msvcrt.kbhit():
146
148
  char = msvcrt.getwch()
147
149
 
148
- if char == '\r': # Enter
150
+ if char == '\xe0' or char == '\x00': # Special keys (Arrows, etc)
151
+ try:
152
+ scancode = msvcrt.getwch()
153
+ if scancode == 'H': # Up Arrow
154
+ if self.history_index > 0:
155
+ self.history_index -= 1
156
+ self.input_buffer = self.history[self.history_index]
157
+ 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
+ sys.stdout.write('\r' + ' ' * (shutil.get_terminal_size().columns - 1) + '\r')
169
+ sys.stdout.write(self.cursor + self.input_buffer)
170
+ sys.stdout.flush()
171
+
172
+ elif scancode == 'P': # Down Arrow
173
+ if self.history_index < len(self.history):
174
+ self.history_index += 1
175
+
176
+ if self.history_index == len(self.history):
177
+ self.input_buffer = ""
178
+ else:
179
+ self.input_buffer = self.history[self.history_index]
180
+
181
+ with self.print_lock:
182
+ sys.stdout.write('\r' + ' ' * (shutil.get_terminal_size().columns - 1) + '\r')
183
+ sys.stdout.write(self.cursor + self.input_buffer)
184
+ sys.stdout.flush()
185
+ except Exception:
186
+ pass
187
+
188
+ elif char == '\r': # Enter
149
189
  with self.print_lock:
150
190
  sys.stdout.write('\n')
151
191
  sys.stdout.flush()
@@ -153,6 +193,13 @@ class InputHandler:
153
193
  self.input_buffer = ""
154
194
 
155
195
  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
+ if not self.history or self.history[-1] != text:
200
+ self.history.append(text)
201
+ self.history_index = len(self.history)
202
+
156
203
  self.processing_command = True
157
204
  cmdargs = text.split(' ')
158
205
  command_name = cmdargs[0].lower()
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.4
2
+ Name: cli_ih
3
+ Version: 0.7.1
4
+ Summary: A background command handler for python's command-line interface.
5
+ Author-email: Hotment <michatchuplay@gmail.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+
9
+ # InputHandler Library
10
+
11
+ A lightweight Python library for creating interactive command-line interfaces with custom command registration, input handling, and clean log output. It supports synchronous and asynchronous modes, threaded input processing, and enhanced logging.
12
+
13
+ ## Features
14
+
15
+ - **Command Registration**: Register commands with decorators and descriptions.
16
+ - **Threaded Input**: Non-blocking input handling by default.
17
+ - **Safe Printing**: Logs appear above the input line, preserving your typed text and cursor position.
18
+ - **Command History**: Navigate recent commands with Up/Down arrow keys.
19
+ - **Sync & Async**: Support for both synchronous and asynchronous (asyncio) applications.
20
+ - **Colored Logging**: Built-in support for colored log messages.
21
+
22
+ ## Installation
23
+
24
+ `pip install cli_ih`
25
+
26
+ ## Quick Start (Synchronous)
27
+
28
+ ```python
29
+ from cli_ih import InputHandler, safe_print
30
+
31
+ handler = InputHandler(cursor="> ")
32
+
33
+ # Use safe_print instead of print to keep the input line clean!
34
+ @handler.command(name="greet", description="Greets the user.")
35
+ def greet(name):
36
+ safe_print(f"Hello, {name}!")
37
+
38
+ @handler.command(name="add", description="Adds two numbers.")
39
+ def add(a, b):
40
+ safe_print(int(a) + int(b))
41
+
42
+ handler.start()
43
+
44
+ # Using safe_print allows you to print logs in the background
45
+ # without messing up the user's current input line.
46
+ ```
47
+
48
+ ## Async Client Example
49
+
50
+ The `AsyncInputHandler` integrates with `asyncio`. The `start()` method is non-blocking when `thread_mode=True` (default).
51
+
52
+ ```python
53
+ import asyncio
54
+ from cli_ih import AsyncInputHandler, safe_print
55
+
56
+ handler = AsyncInputHandler(cursor="Async> ")
57
+
58
+ @handler.command(name="greet", description="Greets the user asynchronously.")
59
+ async def greet(name):
60
+ await asyncio.sleep(1)
61
+ safe_print(f"Hello, {name}")
62
+
63
+ @handler.command(name="add", description="Adds two numbers.")
64
+ async def add(a, b):
65
+ safe_print(int(a) + int(b))
66
+
67
+ # Start the handler (runs in a separate thread by default)
68
+ handler.start()
69
+
70
+ # Keep the main thread alive or run your main event loop
71
+ async def main():
72
+ while handler.is_running:
73
+ await asyncio.sleep(1)
74
+
75
+ if __name__ == "__main__":
76
+ asyncio.run(main())
77
+ ```
78
+
79
+ ## Key Considerations
80
+
81
+ ### Safe Printing
82
+ Always use `from cli_ih import safe_print` for outputting text to the console. This utility automatically detects the active input handler and ensures that your log message is printed *above* the current input line, preserving the user's cursor and any text they are currently typing.
83
+
84
+ ```python
85
+ from cli_ih import safe_print
86
+
87
+ # Good
88
+ safe_print("Log message")
89
+
90
+ # Avoid (might disrupt input line)
91
+ print("Log message")
92
+ ```
93
+
94
+ ### Thread Mode
95
+ Both `InputHandler` and `AsyncInputHandler` accept a `thread_mode` parameter (default `True`).
96
+ - `thread_mode=True`: The input loop runs in a separate thread. `start()` returns immediately.
97
+ - `thread_mode=False`: The input loop runs in the current thread. `start()` blocks until exit.
98
+
99
+ ### Command History
100
+ Use the **Up** and **Down** arrow keys to cycle through your previously entered commands, just like in a standard terminal.
@@ -0,0 +1,9 @@
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,,
@@ -1,86 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: cli_ih
3
- Version: 0.7.0
4
- Summary: A background command handler for python's command-line interface.
5
- Author-email: Hotment <michatchuplay@gmail.com>
6
- Requires-Python: >=3.8
7
- Description-Content-Type: text/markdown
8
-
9
- # InputHandler Library
10
-
11
- A lightweight Python library for creating interactive command-line interfaces with custom command registration and input handling. It supports threaded input processing and includes enhanced logging with color-coded output.
12
-
13
- ## Features
14
-
15
- - Command registration system with descriptions
16
- - Threaded or non-threaded input handling
17
- - Colored logging with support for debug mode
18
- - Built-in `help`, `debug`, and `exit` commands
19
- - Error handling for missing or invalid command arguments
20
- - NEW: Register commands with decorators
21
-
22
- ## Installation
23
-
24
- `pip install cli_ih`
25
-
26
- ## Quick Start
27
-
28
- ```python
29
- from cli_ih import InputHandler
30
-
31
- def greet(args):
32
- print(f"Hello, {' '.join(args)}!")
33
-
34
- handler = InputHandler(cursor="> ")
35
- # NEW
36
- @handler.command(name="add", description="Performs the `+` operator on the first 2 arguments.") # The name param will use the func name if its not provided
37
- def add(args):
38
- print(int(args[0])+int(args[1]))
39
-
40
- handler.register_command("greet", greet, "Greets the user. Usage: greet [name]")
41
- handler.start()
42
-
43
- # Now type commands like:
44
- # > greet world
45
- # Hello, world!
46
- # > add 1 2
47
- # 3
48
- # > help
49
- # Available commands:
50
- # help: Displays all the available commands
51
- # debug: If a logger is present changes the logging level to DEBUG.
52
- # exit: Exits the Input Handler irreversibly.
53
- # add: Performs the `+` operator on the first 2 arguments.
54
- # greet: Greets the user. Usage: greet [name]
55
- #
56
- # > debug
57
- # > exit
58
- ```
59
-
60
- ## New Async client
61
- ```python
62
- import asyncio
63
- from cli_ih import AsyncInputHandler
64
-
65
- print(cli_ih.__version__)
66
-
67
- handler = AsyncInputHandler(cursor="> ")
68
-
69
- @handler.command(name="greet", description="Greets the user. Usage: greet [name]")
70
- async def greet(name, *args):
71
- await asyncio.sleep(1)
72
- print(f"Hello, {name}{" " if args else ""}{' '.join(args)}!")
73
- # NEW
74
- @handler.command(name="add", description="Performs the `+` operator on the first 2 arguments.")
75
- async def add(a, b):
76
- print(a+b)
77
-
78
- asyncio.run(handler.start())
79
- ```
80
-
81
- ## Additional Info
82
-
83
- - You can provide a valid logger `logger=logger` to the `InputHandler` to enable logging (this will be removed soon)
84
- - You can provide the `thread_mode` param to the `InputHandler` class to set if it shoud run in a thread or no.
85
- (If you are using the `cli-ih` module on its own without any other background task set `thread_mode=False` to false)
86
- - You can also provide a `cursor` param to the `InputHandler` class to set the cli cursor (default cusor is empty)
@@ -1,9 +0,0 @@
1
- cli_ih/__init__.py,sha256=wCwv9grczxpu7EXqunCdFgAWlx3XEZVw4O4U3fs45PY,278
2
- cli_ih/asyncClient.py,sha256=L7nCURM15Y9BIrSyfdeLB8PPddr1OF3IA8NWY26HyQQ,11171
3
- cli_ih/client.py,sha256=GWZH5YX2dDV7A2usgnX9xDm8xVjwLSj09sh5vfHSW-Y,10573
4
- cli_ih/exceptions.py,sha256=0xqaMCVu-yEURIrB6wEhbf6kfdsRmODJBoDsQTOp29s,156
5
- cli_ih/utils.py,sha256=HstJPjjlTfVKz68sHU_whSBsOoBKhxizCcycNyQeK2k,1685
6
- cli_ih-0.7.0.dist-info/METADATA,sha256=yu19a6SGAo79STCAgcxBVU4cqbvcYgIZ_KE7hyl_OcY,2789
7
- cli_ih-0.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
- cli_ih-0.7.0.dist-info/top_level.txt,sha256=Ve1CRLNXhPyPSkpN0xLu26roh30LQCpNzkF61BZYfk0,7
9
- cli_ih-0.7.0.dist-info/RECORD,,
File without changes