nercone-modern 1.0.0__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.
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
ModernLoggingLevels = ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]
|
|
4
|
+
MAX_LOG_LEVEL_WIDTH = max(len(level) for level in ModernLoggingLevels)
|
|
5
|
+
|
|
6
|
+
class ModernLogging:
|
|
7
|
+
def __init__(self, process_name):
|
|
8
|
+
self.process_name = process_name
|
|
9
|
+
|
|
10
|
+
def log(self, message, level="INFO"):
|
|
11
|
+
level_text = level.strip().upper()
|
|
12
|
+
if level_text in ["D", "DEBUG"]:
|
|
13
|
+
print(self._make(message, level="DEBUG", color=35))
|
|
14
|
+
elif level_text in ["I", "INFO", "INFORMATION"]:
|
|
15
|
+
print(self._make(message, level="INFO", color=34))
|
|
16
|
+
elif level_text in ["W", "WARN", "WARNING"]:
|
|
17
|
+
print(self._make(message, level="WARN", color=33))
|
|
18
|
+
elif level_text in ["E", "ERROR"]:
|
|
19
|
+
print(self._make(message, level="ERROR", color=31))
|
|
20
|
+
elif level_text in ["C", "CRITICAL"]:
|
|
21
|
+
print(self._make(message, level="CRITICAL", color=31))
|
|
22
|
+
else:
|
|
23
|
+
print(self._make(message, level=level, color=34))
|
|
24
|
+
|
|
25
|
+
def _make(self, message, level="INFO", color=34):
|
|
26
|
+
padded_level = level.ljust(max(MAX_LOG_LEVEL_WIDTH, len(level)))
|
|
27
|
+
return f"{self.process_name} {self._color(color)}{padded_level} | {self._color(0)}{message}"
|
|
28
|
+
|
|
29
|
+
def _color(self, color_code):
|
|
30
|
+
return f"\033[{color_code}m"
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
class ModernProgressBar:
|
|
6
|
+
_active_bars = []
|
|
7
|
+
_last_rendered = False
|
|
8
|
+
_lock = threading.RLock()
|
|
9
|
+
|
|
10
|
+
def __init__(self, total, process_name, process_color, spinner_mode=True):
|
|
11
|
+
self.total = total
|
|
12
|
+
self.spinner_mode = spinner_mode
|
|
13
|
+
self.current = 0
|
|
14
|
+
self.process_name = process_name.strip()
|
|
15
|
+
self.process_color = process_color
|
|
16
|
+
self.index = len(ModernProgressBar._active_bars)
|
|
17
|
+
ModernProgressBar._active_bars.append(self)
|
|
18
|
+
self.log_lines = 0
|
|
19
|
+
self.step = 0
|
|
20
|
+
self.spinner_step = 0
|
|
21
|
+
self.message = "No Message"
|
|
22
|
+
self._spinner_thread = None
|
|
23
|
+
self._spinner_stop_event = threading.Event()
|
|
24
|
+
self._spinner_ready = False
|
|
25
|
+
self._initial_render()
|
|
26
|
+
|
|
27
|
+
def _initial_render(self):
|
|
28
|
+
print()
|
|
29
|
+
|
|
30
|
+
def spinner(self, enabled: bool = True):
|
|
31
|
+
if self.spinner_mode == enabled:
|
|
32
|
+
return
|
|
33
|
+
self.spinner_mode = enabled
|
|
34
|
+
if not self._spinner_ready:
|
|
35
|
+
self._render(advance_spinner=False)
|
|
36
|
+
return
|
|
37
|
+
if enabled:
|
|
38
|
+
self._start_spinner_thread_if_needed()
|
|
39
|
+
else:
|
|
40
|
+
self._stop_spinner_thread()
|
|
41
|
+
self._render(advance_spinner=False)
|
|
42
|
+
|
|
43
|
+
def spin_start(self):
|
|
44
|
+
if self._spinner_ready and self.spinner_mode:
|
|
45
|
+
return
|
|
46
|
+
self._spinner_ready = True
|
|
47
|
+
self.spinner_mode = True
|
|
48
|
+
self.spinner_step = 0
|
|
49
|
+
self._start_spinner_thread_if_needed()
|
|
50
|
+
self._render(advance_spinner=False)
|
|
51
|
+
|
|
52
|
+
def setMessage(self, message):
|
|
53
|
+
self.message = message
|
|
54
|
+
|
|
55
|
+
def start(self):
|
|
56
|
+
self._render(advance_spinner=False)
|
|
57
|
+
self._start_spinner_thread_if_needed()
|
|
58
|
+
|
|
59
|
+
def update(self, amount=1):
|
|
60
|
+
if self._should_spin():
|
|
61
|
+
self._render(advance_spinner=False)
|
|
62
|
+
return
|
|
63
|
+
self.current += amount
|
|
64
|
+
if self.current > self.total:
|
|
65
|
+
self.current = self.total
|
|
66
|
+
self._render(advance_spinner=False)
|
|
67
|
+
|
|
68
|
+
def finish(self):
|
|
69
|
+
self.current = self.total
|
|
70
|
+
self.spinner_mode = False
|
|
71
|
+
self._spinner_ready = False
|
|
72
|
+
self._stop_spinner_thread()
|
|
73
|
+
self._render(final=True, advance_spinner=False)
|
|
74
|
+
|
|
75
|
+
def makeModernLogging(self, process_name):
|
|
76
|
+
from .ModernLogging import ModernLogging
|
|
77
|
+
return ModernLogging(process_name)
|
|
78
|
+
|
|
79
|
+
def logging(self, message, level="INFO", modernLogging=None):
|
|
80
|
+
with ModernProgressBar._lock:
|
|
81
|
+
self.log_lines = 0
|
|
82
|
+
if modernLogging is None:
|
|
83
|
+
modernLogging = self.makeModernLogging(self.process_name)
|
|
84
|
+
result = modernLogging._make(message, level, self.process_color)
|
|
85
|
+
if self.log_lines > 0:
|
|
86
|
+
move_up = self.log_lines
|
|
87
|
+
else:
|
|
88
|
+
move_up = len(ModernProgressBar._active_bars) - self.index
|
|
89
|
+
sys.stdout.write(f"\033[{move_up}A")
|
|
90
|
+
sys.stdout.write("\033[K")
|
|
91
|
+
print(result)
|
|
92
|
+
self.log_lines += 1
|
|
93
|
+
self._render(advance_spinner=False)
|
|
94
|
+
|
|
95
|
+
def _start_spinner_thread_if_needed(self):
|
|
96
|
+
if not self._should_spin():
|
|
97
|
+
return
|
|
98
|
+
if self._spinner_thread and self._spinner_thread.is_alive():
|
|
99
|
+
return
|
|
100
|
+
self._spinner_stop_event = threading.Event()
|
|
101
|
+
self._spinner_thread = threading.Thread(target=self._spinner_worker, daemon=True)
|
|
102
|
+
self._spinner_thread.start()
|
|
103
|
+
|
|
104
|
+
def _stop_spinner_thread(self):
|
|
105
|
+
if self._spinner_thread and self._spinner_thread.is_alive():
|
|
106
|
+
self._spinner_stop_event.set()
|
|
107
|
+
self._spinner_thread.join()
|
|
108
|
+
self._spinner_thread = None
|
|
109
|
+
|
|
110
|
+
def _spinner_worker(self):
|
|
111
|
+
while not self._spinner_stop_event.wait(0.05):
|
|
112
|
+
if not self._should_spin():
|
|
113
|
+
continue
|
|
114
|
+
self._render()
|
|
115
|
+
|
|
116
|
+
def _render(self, final=False, advance_spinner=True):
|
|
117
|
+
with ModernProgressBar._lock:
|
|
118
|
+
progress = self.current / self.total if self.total else 0
|
|
119
|
+
bar = self._progress_bar(progress, advance_spinner=advance_spinner and self._should_spin())
|
|
120
|
+
percentage_value = int(round(min(max(progress, 0), 1) * 100))
|
|
121
|
+
percentage = f"{percentage_value:3d}%"
|
|
122
|
+
total_width = max(len(str(self.total)), 1)
|
|
123
|
+
if final:
|
|
124
|
+
status = "(DONE)"
|
|
125
|
+
elif self.spinner_mode and self._spinner_ready:
|
|
126
|
+
status = "(RUNN)"
|
|
127
|
+
else:
|
|
128
|
+
status = f"({self.current:>{total_width}}/{self.total})"
|
|
129
|
+
line = f"({self._color(self.process_color)}{bar}{self._color('reset')}) {self.process_name} - {'....' if self.spinner_mode else percentage} {status} | {self.message}"
|
|
130
|
+
total_move_up = self.log_lines + (len(ModernProgressBar._active_bars) - self.index)
|
|
131
|
+
if total_move_up > 0:
|
|
132
|
+
sys.stdout.write(f"\033[{total_move_up}A")
|
|
133
|
+
sys.stdout.write("\r")
|
|
134
|
+
sys.stdout.write("\033[K")
|
|
135
|
+
sys.stdout.write(line)
|
|
136
|
+
sys.stdout.write("\n")
|
|
137
|
+
down_lines = max(total_move_up - 1, 0)
|
|
138
|
+
if down_lines > 0:
|
|
139
|
+
sys.stdout.write(f"\033[{down_lines}B")
|
|
140
|
+
sys.stdout.flush()
|
|
141
|
+
|
|
142
|
+
def _progress_bar(self, progress, advance_spinner=True):
|
|
143
|
+
bar_length = 20
|
|
144
|
+
if not self._should_spin():
|
|
145
|
+
empty_bar = "-"
|
|
146
|
+
if self.current == self.total:
|
|
147
|
+
center_bar = ""
|
|
148
|
+
else:
|
|
149
|
+
center_bar = "-"
|
|
150
|
+
filled_bar = "-"
|
|
151
|
+
if self.current <= 0 and not self._spinner_ready:
|
|
152
|
+
return f"{self._color('gray')}{empty_bar * (bar_length + 1)}"
|
|
153
|
+
if self.current == self.total:
|
|
154
|
+
filled_length = int(progress * bar_length) + 1
|
|
155
|
+
else:
|
|
156
|
+
filled_length = int(progress * bar_length)
|
|
157
|
+
return f"{self._color('blue')}{filled_bar * filled_length}{self._color('cyan')}{center_bar}{self._color('black')}{empty_bar * (bar_length - filled_length)}"
|
|
158
|
+
else:
|
|
159
|
+
if self.current <= 0 and not self._spinner_ready:
|
|
160
|
+
return f"{self._color('gray')}{'-' * (bar_length + 1)}"
|
|
161
|
+
spinner_symbol_length = 1
|
|
162
|
+
spinner_end_bar_length = bar_length - self.spinner_step
|
|
163
|
+
spinner_start_bar_length = bar_length - spinner_end_bar_length
|
|
164
|
+
if advance_spinner:
|
|
165
|
+
self.spinner_step = (self.spinner_step + 1) % (bar_length + 1)
|
|
166
|
+
return f"{self._color('black')}{'-' * spinner_start_bar_length}{self._color('blue')}{'-' * spinner_symbol_length}{self._color('black')}{'-' * spinner_end_bar_length}"
|
|
167
|
+
|
|
168
|
+
def _should_spin(self):
|
|
169
|
+
return self.spinner_mode and self._spinner_ready
|
|
170
|
+
|
|
171
|
+
def _color(self, color_name):
|
|
172
|
+
if color_name == "cyan":
|
|
173
|
+
return self._color_by_code(36)
|
|
174
|
+
elif color_name == "magenta":
|
|
175
|
+
return self._color_by_code(35)
|
|
176
|
+
elif color_name == "yellow":
|
|
177
|
+
return self._color_by_code(33)
|
|
178
|
+
elif color_name == "green":
|
|
179
|
+
return self._color_by_code(32)
|
|
180
|
+
elif color_name == "red":
|
|
181
|
+
return self._color_by_code(31)
|
|
182
|
+
elif color_name == "blue":
|
|
183
|
+
return self._color_by_code(34)
|
|
184
|
+
elif color_name == "white":
|
|
185
|
+
return self._color_by_code(37)
|
|
186
|
+
elif color_name == "black":
|
|
187
|
+
return self._color_by_code(30)
|
|
188
|
+
elif color_name in ("gray", "grey"):
|
|
189
|
+
return self._color_by_code(90)
|
|
190
|
+
elif color_name == "reset":
|
|
191
|
+
return self._color_by_code(0)
|
|
192
|
+
else:
|
|
193
|
+
return ""
|
|
194
|
+
|
|
195
|
+
def _color_by_code(self, color_code):
|
|
196
|
+
return f"\033[{color_code}m"
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
progress_bar = ModernProgressBar(1000, "Example Task", 31)
|
|
200
|
+
progress_bar.start()
|
|
201
|
+
progress_bar.busy()
|
|
202
|
+
for i in range(100):
|
|
203
|
+
progress_bar.update()
|
|
204
|
+
time.sleep(0.1)
|
|
205
|
+
progress_bar.notbusy()
|
|
206
|
+
for i in range(1000):
|
|
207
|
+
progress_bar.update()
|
|
208
|
+
time.sleep(0.1)
|
|
209
|
+
progress_bar.finish()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
class NerconeModern:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
def color(self, color_code):
|
|
8
|
+
return f"\033[{color_code}m"
|
|
9
|
+
|
|
10
|
+
def modernLogging(self, process_name):
|
|
11
|
+
from .ModernLogging import ModernLogging
|
|
12
|
+
return ModernLogging(process_name)
|
|
13
|
+
|
|
14
|
+
def modernProgressBar(self, total: int, process_name: str, process_color: int = 32, spinner_mode: bool = False):
|
|
15
|
+
from .ModernProgressBar import ModernProgressBar
|
|
16
|
+
return ModernProgressBar(total, process_name, process_color, spinner_mode)
|
|
17
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from nercone_modern import NerconeModern
|
|
2
|
+
|
|
3
|
+
if __name__ == "__main__":
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
logger = NerconeModern().modernLogging("Main")
|
|
7
|
+
logger.log("This is a debug message", "DEBUG")
|
|
8
|
+
logger.log("This is a test message", "INFO")
|
|
9
|
+
logger.log("This is a warning message", "WARNING")
|
|
10
|
+
logger.log("This is an error message", "ERROR")
|
|
11
|
+
logger.log("This is an error message", "CRITICAL")
|
|
12
|
+
|
|
13
|
+
progress_bar1 = NerconeModern().modernProgressBar(total=100, process_name="Task 1", process_color=32, spinner_mode=False)
|
|
14
|
+
progress_bar1.setMessage("WAITING")
|
|
15
|
+
progress_bar2 = NerconeModern().modernProgressBar(total=200, process_name="Task 2", process_color=34, spinner_mode=True)
|
|
16
|
+
progress_bar2.setMessage("WAITING")
|
|
17
|
+
|
|
18
|
+
progress_bar1.start()
|
|
19
|
+
progress_bar2.start()
|
|
20
|
+
|
|
21
|
+
progress_bar1.setMessage("RUNNING")
|
|
22
|
+
for i in range(100):
|
|
23
|
+
time.sleep(0.05)
|
|
24
|
+
progress_bar1.update()
|
|
25
|
+
progress_bar1.setMessage("DONE")
|
|
26
|
+
progress_bar1.finish()
|
|
27
|
+
|
|
28
|
+
progress_bar2.spin_start()
|
|
29
|
+
progress_bar2.setMessage("RUNNING (BACKGROUND)")
|
|
30
|
+
for i in range(100):
|
|
31
|
+
time.sleep(0.05)
|
|
32
|
+
progress_bar2.update(2)
|
|
33
|
+
progress_bar2.setMessage("DONE")
|
|
34
|
+
progress_bar2.finish()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nercone-modern
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Nercone Modern is a simple package for logging and progress bar and More!
|
|
5
|
+
Author: Nercone
|
|
6
|
+
Author-email: Nercone <nercone@diamondgotcat.net>
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Project-URL: Homepage, https://github.com/DiamondGotCat/nercone-modern
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# nercone-modern
|
|
16
|
+
Modern Logging and Progress Bar Library
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
nercone_modern/ModernLogging.py,sha256=dBy_GB9VBSabioRcRd-cGIHx5uIbLfws3_Zw4TFNsYQ,1277
|
|
2
|
+
nercone_modern/ModernProgressBar.py,sha256=Qn9nu3qxh-7pJ36ITPxqjPNtRztpuJWdmV4tLNAuLFM,8007
|
|
3
|
+
nercone_modern/__init__.py,sha256=z_1a2j2mWUlN88NLbCnER_2PPCCCoDNVCSkvgYcslOg,544
|
|
4
|
+
nercone_modern/__main__.py,sha256=kupXU8cBsmd_-M9A6uHdkK8OQ3GriXL4JqmcARvrdU8,1201
|
|
5
|
+
nercone_modern-1.0.0.dist-info/WHEEL,sha256=M6du7VZflc4UPsGphmOXHANdgk8zessdJG0DBUuoA-U,78
|
|
6
|
+
nercone_modern-1.0.0.dist-info/METADATA,sha256=Qr2sMP-IHRFkGsEYhXJn0Jeam8zAYpxuGdgad5lEUNo,558
|
|
7
|
+
nercone_modern-1.0.0.dist-info/RECORD,,
|