cli-ih 0.5.2.2__py3-none-any.whl → 0.5.2.3__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/InputHandler.py +122 -0
- cli_ih/__init__.py +1 -0
- {cli_ih-0.5.2.2.dist-info → cli_ih-0.5.2.3.dist-info}/METADATA +2 -2
- cli_ih-0.5.2.3.dist-info/RECORD +8 -0
- cli_ih-0.5.2.3.dist-info/top_level.txt +1 -0
- cli_ih-0.5.2.2.dist-info/RECORD +0 -6
- cli_ih-0.5.2.2.dist-info/top_level.txt +0 -1
- {cli_ih-0.5.2.2.dist-info → cli_ih-0.5.2.3.dist-info}/WHEEL +0 -0
cli_ih/InputHandler.py
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
from typing import Callable
|
2
|
+
import logging
|
3
|
+
|
4
|
+
class HandlerClosed(Exception): ...
|
5
|
+
class MissingParameter(Exception): ...
|
6
|
+
|
7
|
+
class CustomFormatter(logging.Formatter):
|
8
|
+
"""Custom formatter to add colors to log levels."""
|
9
|
+
|
10
|
+
LEVEL_COLORS = {
|
11
|
+
logging.DEBUG: "\033[34m",
|
12
|
+
logging.INFO: "\033[0m",
|
13
|
+
logging.WARNING: "\033[33m",
|
14
|
+
logging.ERROR: "\033[31m",
|
15
|
+
logging.CRITICAL: "\033[37;41m"
|
16
|
+
}
|
17
|
+
|
18
|
+
def format(self, record):
|
19
|
+
log_color = self.LEVEL_COLORS.get(record.levelno, "\033[0m")
|
20
|
+
log_message = super().format(record)
|
21
|
+
return f"{log_color}{log_message}\033[0m"
|
22
|
+
|
23
|
+
def setup_logging():
|
24
|
+
log_format = '[%(asctime)s | %(levelname)s]: %(message)s'
|
25
|
+
handler = logging.StreamHandler()
|
26
|
+
handler.setFormatter(CustomFormatter(log_format))
|
27
|
+
logging.basicConfig(level=logging.INFO, handlers=[handler], datefmt='%B %d %H:%M:%S')
|
28
|
+
|
29
|
+
class InputHandler:
|
30
|
+
#logging.basicConfig(level=logging.INFO, format='[%(asctime)s | %(levelname)s]: %(message)s', datefmt='%B %d %H:%M:%S')
|
31
|
+
def __init__(self, thread_mode = True, cursor = ""):
|
32
|
+
self.commands = {}
|
33
|
+
self.is_running = False
|
34
|
+
self.thread_mode = thread_mode
|
35
|
+
self.cursor = f"{cursor.strip()} "
|
36
|
+
self.thread = None
|
37
|
+
self.register_default_commands()
|
38
|
+
|
39
|
+
def register_command(self, name: str, func: Callable, description: str = ""):
|
40
|
+
"""Registers a command with its associated function."""
|
41
|
+
if not description:
|
42
|
+
description = "A command"
|
43
|
+
if ' ' in name:
|
44
|
+
raise SyntaxError("Command name must not have spaces")
|
45
|
+
self.commands[name] = {"cmd": func, "description": description}
|
46
|
+
|
47
|
+
def start(self):
|
48
|
+
"""Starts the input handler loop in a separate thread if thread mode is enabled."""
|
49
|
+
import threading, inspect
|
50
|
+
self.is_running = True
|
51
|
+
|
52
|
+
def run_command(commands: dict, name: str, args: list):
|
53
|
+
"""Executes a command from the command dictionary if it exists."""
|
54
|
+
command = commands.get(name)
|
55
|
+
if command:
|
56
|
+
func = command.get("cmd")
|
57
|
+
if callable(func):
|
58
|
+
if str(inspect.signature(func)) == "()":
|
59
|
+
raise MissingParameter(f"Command '{name}' must accept an 'args' parameter")
|
60
|
+
try:
|
61
|
+
func(args)
|
62
|
+
except Exception as e:
|
63
|
+
raise e
|
64
|
+
else:
|
65
|
+
raise ValueError(f"The command '{name}' is not callable.")
|
66
|
+
else:
|
67
|
+
logging.warning(f"Command '{name}' not found.")
|
68
|
+
|
69
|
+
|
70
|
+
def _thread():
|
71
|
+
"""Continuously listens for user input and processes commands."""
|
72
|
+
while self.is_running:
|
73
|
+
try:
|
74
|
+
user_input = input(self.cursor).strip()
|
75
|
+
if not user_input:
|
76
|
+
continue
|
77
|
+
|
78
|
+
cmdargs = user_input.split(' ')
|
79
|
+
command_name = cmdargs[0]
|
80
|
+
args = cmdargs[1:]
|
81
|
+
if command_name in self.commands:
|
82
|
+
run_command(self.commands, command_name, args)
|
83
|
+
else:
|
84
|
+
logging.warning(f"Unknown command: '{command_name}'")
|
85
|
+
except EOFError:
|
86
|
+
logging.error("Input ended unexpectedly.")
|
87
|
+
break
|
88
|
+
except KeyboardInterrupt:
|
89
|
+
logging.error("Input interrupted.")
|
90
|
+
break
|
91
|
+
except HandlerClosed:
|
92
|
+
logging.info("Input Handler exited.")
|
93
|
+
break
|
94
|
+
self.is_running = False
|
95
|
+
if self.thread_mode:
|
96
|
+
self.thread = threading.Thread(target=_thread, daemon=True)
|
97
|
+
self.thread.start()
|
98
|
+
else:
|
99
|
+
_thread()
|
100
|
+
|
101
|
+
def register_default_commands(self):
|
102
|
+
def help(commands):
|
103
|
+
str_out = ""
|
104
|
+
for command in commands:
|
105
|
+
str_out += f"{command}: {commands[command]['description']}\n"
|
106
|
+
print(str_out)
|
107
|
+
|
108
|
+
def debug_mode(args):
|
109
|
+
logger = logging.getLogger()
|
110
|
+
if logger.getEffectiveLevel() == logging.DEBUG:
|
111
|
+
logger.setLevel(logging.INFO)
|
112
|
+
logging.info("Debug mode is now off")
|
113
|
+
else:
|
114
|
+
logger.setLevel(logging.DEBUG)
|
115
|
+
logging.debug("Debug mode is now on")
|
116
|
+
|
117
|
+
def exit_thread(args):
|
118
|
+
raise HandlerClosed
|
119
|
+
self.register_command("help", lambda args: help(self.commands), "Displays all the available commands")
|
120
|
+
self.register_command("debug", debug_mode, "Changes the logging level to DEBUG.")
|
121
|
+
self.register_command("exit", exit_thread, "Exits the Input Handler irreversibly.")
|
122
|
+
setup_logging()
|
cli_ih/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from . import InputHandler
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cli_ih
|
3
|
-
Version: 0.5.2.
|
3
|
+
Version: 0.5.2.3
|
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
|
@@ -20,7 +20,7 @@ A lightweight Python library for creating interactive command-line interfaces wi
|
|
20
20
|
|
21
21
|
## Installation
|
22
22
|
|
23
|
-
`pip install
|
23
|
+
`pip install cli_ih`
|
24
24
|
|
25
25
|
## Quick Start
|
26
26
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
InputHandler/InputHandler.py,sha256=_OUI_BT6EN1wfWmLtm94bFLlHLvK01TKCzifEg2uNYQ,4932
|
2
|
+
InputHandler/__init__.py,sha256=biHQ9fiomeNJ4G6h3KvdJlCca8r7l89mkKlDCrckNUY,26
|
3
|
+
cli_ih/InputHandler.py,sha256=_OUI_BT6EN1wfWmLtm94bFLlHLvK01TKCzifEg2uNYQ,4932
|
4
|
+
cli_ih/__init__.py,sha256=biHQ9fiomeNJ4G6h3KvdJlCca8r7l89mkKlDCrckNUY,26
|
5
|
+
cli_ih-0.5.2.3.dist-info/METADATA,sha256=rHjveRyRt3xkH-gQhocLGEE9uoGUy-ixUc8PKG-eQ3E,1604
|
6
|
+
cli_ih-0.5.2.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
7
|
+
cli_ih-0.5.2.3.dist-info/top_level.txt,sha256=Ve1CRLNXhPyPSkpN0xLu26roh30LQCpNzkF61BZYfk0,7
|
8
|
+
cli_ih-0.5.2.3.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
cli_ih
|
cli_ih-0.5.2.2.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
InputHandler/InputHandler.py,sha256=_OUI_BT6EN1wfWmLtm94bFLlHLvK01TKCzifEg2uNYQ,4932
|
2
|
-
InputHandler/__init__.py,sha256=biHQ9fiomeNJ4G6h3KvdJlCca8r7l89mkKlDCrckNUY,26
|
3
|
-
cli_ih-0.5.2.2.dist-info/METADATA,sha256=b6KQuipjMh1u27LjVlywzyO4mYkIBFrw6qlntnLQ8Uc,1604
|
4
|
-
cli_ih-0.5.2.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
5
|
-
cli_ih-0.5.2.2.dist-info/top_level.txt,sha256=1wOYpDwNGBjhQWhD3dRvyWH1Hh4U6HWbAqJAq-p4Izg,13
|
6
|
-
cli_ih-0.5.2.2.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
InputHandler
|
File without changes
|