nercone-modern 1.4.5__tar.gz → 1.6.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nercone-modern
3
- Version: 1.4.5
3
+ Version: 1.6.0
4
4
  Summary: Modern CLI Library
5
5
  Author: Nercone
6
6
  Author-email: Nercone <nercone@diamondgotcat.net>
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "nercone-modern"
7
- version = "1.4.5"
7
+ version = "1.6.0"
8
8
  description = "Modern CLI Library"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  authors = [
@@ -22,8 +22,11 @@ try:
22
22
  logger1.log("This is a warning message", "WARNING")
23
23
  logger1.log("This is a error message", "ERROR")
24
24
  logger1.log("This is a critical error message", "CRITICAL")
25
- prompt_result = logger1.prompt("This is a Prompt. Let's try it: ")
25
+ prompt_result = logger1.prompt("Continue demo?", choices=["Y", "n"])
26
26
  logger1.log(f"Answer is: {prompt_result}", "INFO")
27
+ if prompt_result == "n":
28
+ print("Exiting demo. See you!")
29
+ raise SystemExit(0)
27
30
 
28
31
  progress_bar1 = ModernProgressBar(total=100, process_name="Task 1", spinner_mode=False)
29
32
  progress_bar1.setMessage("WAITING")
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # -- nercone-modern --------------------------------------------- #
4
+ # logging.py on nercone-modern #
5
+ # Made by DiamondGotCat, Licensed under MIT License #
6
+ # Copyright (c) 2025 DiamondGotCat #
7
+ # ---------------------------------------------- DiamondGotCat -- #
8
+
9
+ ModernLoggingLevels = ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]
10
+ MAX_LOG_LEVEL_WIDTH = max(len(level) for level in ModernLoggingLevels)
11
+ LEVEL_ALIASES = {
12
+ "D": "DEBUG",
13
+ "DEBUG": "DEBUG",
14
+ "I": "INFO",
15
+ "INFO": "INFO",
16
+ "INFORMATION": "INFO",
17
+ "W": "WARN",
18
+ "WARN": "WARN",
19
+ "WARNING": "WARN",
20
+ "E": "ERROR",
21
+ "ERROR": "ERROR",
22
+ "C": "CRITICAL",
23
+ "CRITICAL": "CRITICAL"
24
+ }
25
+
26
+ _last_process = None
27
+ _last_level = None
28
+ _max_proc_width = 0
29
+
30
+ def normalize_level(level: str) -> str:
31
+ level = level.strip().upper()
32
+ return LEVEL_ALIASES.get(level, level)
33
+
34
+ def is_higher_priority(level_a: str, level_b: str) -> bool:
35
+ a = normalize_level(level_a)
36
+ b = normalize_level(level_b)
37
+ try:
38
+ return ModernLoggingLevels.index(a) >= ModernLoggingLevels.index(b)
39
+ except ValueError:
40
+ raise ValueError(f"Unknown log level: {level_a} or {level_b}")
41
+
42
+ class ModernLogging:
43
+ def __init__(self, process_name: str = "App", display_level: str = "INFO", filepath: str | None = None):
44
+ self.process_name = process_name
45
+ self.display_level = display_level
46
+ self.filepath = filepath
47
+ global _max_proc_width
48
+ _max_proc_width = max(_max_proc_width, len(process_name))
49
+
50
+ def log(self, message: str = "", level_text: str = "INFO", level_color: str | None = None):
51
+ if not is_higher_priority(level_text, self.display_level):
52
+ return
53
+ global _last_process, _last_level
54
+ log_line = self.make(message=message, level_text=level_text, level_color=level_color)
55
+ print(log_line)
56
+ _last_process = self.process_name
57
+ _last_level = normalize_level(level_text.strip().upper())
58
+ if self.filepath:
59
+ with open(self.filepath, "a") as f:
60
+ f.write(f"{log_line}\n")
61
+
62
+ def prompt(self, message: str = "", level_text: str = "INFO", level_color: str | None = None, ignore_kbdinterrupt: bool = True, default: str | None = None, choices: list[str] | None = None, show_choices: bool = True) -> str:
63
+ if not is_higher_priority(level_text, self.display_level):
64
+ return
65
+ global _last_process, _last_level
66
+ if default:
67
+ message += f" ({default})"
68
+ if choices and show_choices:
69
+ message += f" [{'/'.join(choices)}]"
70
+ if not message.endswith(" "):
71
+ message += " "
72
+ log_line = self.make(message=message, level_text=level_text, level_color=level_color)
73
+ print(log_line, end="")
74
+ _last_process = self.process_name
75
+ _last_level = normalize_level(level_text.strip().upper())
76
+ answer = ""
77
+ try:
78
+ answer = input()
79
+ except KeyboardInterrupt:
80
+ if ignore_kbdinterrupt:
81
+ print()
82
+ else:
83
+ raise
84
+ if answer.strip() == "" and default is not None:
85
+ if choices:
86
+ selected_default = self._select_choice(default, choices)
87
+ if selected_default is not None:
88
+ answer = default
89
+ else:
90
+ answer = default
91
+ if self.filepath:
92
+ with open(self.filepath, "a") as f:
93
+ f.write(f"{log_line}{answer}\n")
94
+ if choices:
95
+ selected = self._select_choice(answer, choices)
96
+ if selected is not None:
97
+ return selected
98
+ else:
99
+ while True:
100
+ log_line = self.make(message=f"Invalid selection. Please select from: {'/'.join(choices)}", level_text=level_text, level_color=level_color)
101
+ print(log_line, end="")
102
+ if self.filepath:
103
+ with open(self.filepath, "a") as f:
104
+ f.write(f"{log_line}{answer}\n")
105
+ log_line = self.make(message=message, level_text=level_text, level_color=level_color)
106
+ print(log_line, end="")
107
+ try:
108
+ answer = input()
109
+ except KeyboardInterrupt:
110
+ if ignore_kbdinterrupt:
111
+ print()
112
+ else:
113
+ raise
114
+ if self.filepath:
115
+ with open(self.filepath, "a") as f:
116
+ f.write(f"{log_line}{answer}\n")
117
+ if answer.strip() == "" and default is not None:
118
+ if choices:
119
+ selected_default = self._select_choice(default, choices)
120
+ if selected_default is not None:
121
+ return default
122
+ else:
123
+ return default
124
+ selected = self._select_choice(answer, choices)
125
+ if selected is not None:
126
+ return selected
127
+
128
+ def _select_choice(self, answer: str, choices: list[str]) -> str | None:
129
+ if answer in choices:
130
+ return answer
131
+ stripped = answer.strip()
132
+ if stripped in choices:
133
+ return stripped
134
+ lower_map = {c.lower(): c for c in choices}
135
+ if answer.lower() in lower_map:
136
+ return lower_map[answer.lower()]
137
+ if stripped.lower() in lower_map:
138
+ return lower_map[stripped.lower()]
139
+ return None
140
+
141
+ def make(self, message: str = "", level_text: str = "INFO", level_color: str | None = None):
142
+ level_text = normalize_level(level_text.strip().upper())
143
+ show_proc = (self.process_name != _last_process)
144
+ show_level = show_proc or (level_text != _last_level)
145
+
146
+ if not level_color:
147
+ if level_text == "DEBUG":
148
+ level_color = 'gray'
149
+ elif level_text == "INFO":
150
+ level_color = 'blue'
151
+ elif level_text == "WARN":
152
+ level_color = 'yellow'
153
+ elif level_text == "ERROR":
154
+ level_color = 'red'
155
+ elif level_text == "CRITICAL":
156
+ level_color = 'red'
157
+ else:
158
+ level_color = 'blue'
159
+
160
+ return self._make(message, level_text, level_color, show_proc, show_level)
161
+
162
+ def _make(self, message: str, level_text: str, level_color: str, show_proc: bool, show_level: bool):
163
+ global _max_proc_width
164
+ level_width = max(MAX_LOG_LEVEL_WIDTH, len(level_text))
165
+
166
+ proc_part = self.process_name if show_proc else ""
167
+ proc_part = proc_part.ljust(_max_proc_width) if proc_part else " " * _max_proc_width
168
+
169
+ if show_level:
170
+ level_part = f"{self._color(level_color)}{level_text.ljust(level_width)} |{self._color('reset')}"
171
+ else:
172
+ level_part = (" " * level_width) + f"{self._color(level_color)} |{self._color('reset')}"
173
+
174
+ return f"{proc_part} {level_part} {str(message)}"
175
+
176
+ def _color(self, color_name: str = "reset"):
177
+ if color_name == "cyan":
178
+ return self._color_by_code(36)
179
+ elif color_name == "magenta":
180
+ return self._color_by_code(35)
181
+ elif color_name == "yellow":
182
+ return self._color_by_code(33)
183
+ elif color_name == "green":
184
+ return self._color_by_code(32)
185
+ elif color_name == "red":
186
+ return self._color_by_code(31)
187
+ elif color_name == "blue":
188
+ return self._color_by_code(34)
189
+ elif color_name == "white":
190
+ return self._color_by_code(37)
191
+ elif color_name == "black":
192
+ return self._color_by_code(30)
193
+ elif color_name in ("gray", "grey"):
194
+ return self._color_by_code(90)
195
+ elif color_name == "reset":
196
+ return self._color_by_code(0)
197
+ else:
198
+ return ""
199
+
200
+ def _color_by_code(self, color_code: int | str = 0):
201
+ return f"\033[{color_code}m"
@@ -1,144 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # -- nercone-modern --------------------------------------------- #
4
- # logging.py on nercone-modern #
5
- # Made by DiamondGotCat, Licensed under MIT License #
6
- # Copyright (c) 2025 DiamondGotCat #
7
- # ---------------------------------------------- DiamondGotCat -- #
8
-
9
- ModernLoggingLevels = ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]
10
- MAX_LOG_LEVEL_WIDTH = max(len(level) for level in ModernLoggingLevels)
11
- LEVEL_ALIASES = {
12
- "D": "DEBUG",
13
- "DEBUG": "DEBUG",
14
- "I": "INFO",
15
- "INFO": "INFO",
16
- "INFORMATION": "INFO",
17
- "W": "WARN",
18
- "WARN": "WARN",
19
- "WARNING": "WARN",
20
- "E": "ERROR",
21
- "ERROR": "ERROR",
22
- "C": "CRITICAL",
23
- "CRITICAL": "CRITICAL"
24
- }
25
-
26
- _last_process = None
27
- _last_level = None
28
- _max_proc_width = 0
29
-
30
- def normalize_level(level: str) -> str:
31
- level = level.strip().upper()
32
- return LEVEL_ALIASES.get(level, level)
33
-
34
- def is_higher_priority(level_a: str, level_b: str) -> bool:
35
- a = normalize_level(level_a)
36
- b = normalize_level(level_b)
37
- try:
38
- return ModernLoggingLevels.index(a) >= ModernLoggingLevels.index(b)
39
- except ValueError:
40
- raise ValueError(f"Unknown log level: {level_a} or {level_b}")
41
-
42
- class ModernLogging:
43
- def __init__(self, process_name: str, display_level: str = "INFO"):
44
- self.process_name = process_name
45
- self.display_level = display_level
46
-
47
- global _max_proc_width
48
- _max_proc_width = max(_max_proc_width, len(process_name))
49
-
50
- def log(self, message: str = "", level: str = "INFO"):
51
- if not is_higher_priority(level, self.display_level):
52
- return
53
-
54
- global _last_process, _last_level
55
- level_text = normalize_level(level.strip().upper())
56
- show_proc = (self.process_name != _last_process)
57
- show_level = show_proc or (level_text != _last_level)
58
-
59
- if level_text == "DEBUG":
60
- color = 'gray'
61
- elif level_text == "INFO":
62
- color = 'blue'
63
- elif level_text == "WARN":
64
- color = 'yellow'
65
- elif level_text == "ERROR":
66
- color = 'red'
67
- elif level_text == "CRITICAL":
68
- color = 'red'
69
- else:
70
- color = 'blue'
71
-
72
- print(self._make(message, level_text, color, show_proc, show_level))
73
-
74
- _last_process = self.process_name
75
- _last_level = level_text
76
-
77
- def prompt(self, message: str = "", level: str = "INFO") -> str:
78
- if not is_higher_priority(level, self.display_level):
79
- return
80
-
81
- global _last_process, _last_level
82
- level_text = normalize_level(level.strip().upper())
83
- show_proc = (self.process_name != _last_process)
84
- show_level = show_proc or (level_text != _last_level)
85
-
86
- if level_text == "DEBUG":
87
- color = 'gray'
88
- elif level_text == "INFO":
89
- color = 'blue'
90
- elif level_text == "WARN":
91
- color = 'yellow'
92
- elif level_text == "ERROR":
93
- color = 'red'
94
- elif level_text == "CRITICAL":
95
- color = 'red'
96
- else:
97
- color = 'blue'
98
-
99
- print(self._make(message, level_text, color, show_proc, show_level), end="")
100
-
101
- _last_process = self.process_name
102
- _last_level = level_text
103
- return input()
104
-
105
- def _make(self, message: str, level_text: str, color: str, show_proc: bool, show_level: bool):
106
- global _max_proc_width
107
- level_width = max(MAX_LOG_LEVEL_WIDTH, len(level_text))
108
-
109
- proc_part = self.process_name if show_proc else ""
110
- proc_part = proc_part.ljust(_max_proc_width) if proc_part else " " * _max_proc_width
111
-
112
- if show_level:
113
- level_part = f"{self._color(color)}{level_text.ljust(level_width)} |{self._color('reset')}"
114
- else:
115
- level_part = (" " * level_width) + f"{self._color(color)} |{self._color('reset')}"
116
-
117
- return f"{proc_part} {level_part} {str(message)}"
118
-
119
- def _color(self, color_name: str = "reset"):
120
- if color_name == "cyan":
121
- return self._color_by_code(36)
122
- elif color_name == "magenta":
123
- return self._color_by_code(35)
124
- elif color_name == "yellow":
125
- return self._color_by_code(33)
126
- elif color_name == "green":
127
- return self._color_by_code(32)
128
- elif color_name == "red":
129
- return self._color_by_code(31)
130
- elif color_name == "blue":
131
- return self._color_by_code(34)
132
- elif color_name == "white":
133
- return self._color_by_code(37)
134
- elif color_name == "black":
135
- return self._color_by_code(30)
136
- elif color_name in ("gray", "grey"):
137
- return self._color_by_code(90)
138
- elif color_name == "reset":
139
- return self._color_by_code(0)
140
- else:
141
- return ""
142
-
143
- def _color_by_code(self, color_code: int | str = 0):
144
- return f"\033[{color_code}m"
File without changes