Skip to content

Commit 80c1711

Browse files
committed
➕ Added average ETA (progress) + several tweaks
1 parent 78359b0 commit 80c1711

33 files changed

Lines changed: 810 additions & 221 deletions

LRFutils.egg-info/PKG-INFO

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Metadata-Version: 2.1
2+
Name: LRFutils
3+
Version: 0.0.11
4+
Summary: Just a custom library to share with some colleagues. Use it at your own risks.
5+
Home-page: https://github.com/LeiRoF/Utils
6+
Author: Leirof
7+
Author-email: vince.lrf@gmail.com
8+
Classifier: Programming Language :: Python :: 3
9+
Classifier: License :: OSI Approved :: MIT License
10+
Classifier: Operating System :: OS Independent
11+
Classifier: Intended Audience :: Developers
12+
Requires-Python: >=3.10.0
13+
License-File: LICENSE

LRFutils.egg-info/SOURCES.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
LICENSE
2+
README.md
3+
setup.py
4+
LRFutils/__init__.py
5+
LRFutils/archive.py
6+
LRFutils/color.py
7+
LRFutils/logs.py
8+
LRFutils/progress.py
9+
LRFutils/term.py
10+
LRFutils.egg-info/PKG-INFO
11+
LRFutils.egg-info/SOURCES.txt
12+
LRFutils.egg-info/dependency_links.txt
13+
LRFutils.egg-info/requires.txt
14+
LRFutils.egg-info/top_level.txt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

LRFutils.egg-info/requires.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
gitpython
2+
regex

LRFutils.egg-info/top_level.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
LRFutils

LRFutils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
from .color import *
33
from .term import *
44
from .progress import *
5-
from .log import *
5+
from .logs import *

LRFutils/color.py

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,53 @@
11
import re
22

3-
class Color:
4-
NC = '\033[0m' # No Color, reset all
5-
6-
Bold = '\033[1m'
7-
Underlined = '\033[4m'
8-
Blink = '\033[5m'
9-
Inverted = '\033[7m'
10-
Hidden = '\033[8m'
11-
12-
Black = '\033[30m'
13-
Red = '\033[31m'
14-
Green = '\033[32m'
15-
Yellow = '\033[33m'
16-
Blue = '\033[34m'
17-
Purple = '\033[35m'
18-
Cyan = '\033[36m'
19-
LightGray = '\033[37m'
20-
21-
Gray = "\u001b[30;1m"
22-
LightRed = "\u001b[31;1m"
23-
LightGreen = "\u001b[32;1m"
24-
LightYellow = "\u001b[33;1m"
25-
LightBlue = "\u001b[34;1m"
26-
LightMagenta = "\u001b[35;1m"
27-
LightCyan = "\u001b[36;1m"
28-
White = "\u001b[37;1m"
29-
30-
31-
on_Black = "\u001b[40m"
32-
on_Red = "\u001b[41m"
33-
on_Green = "\u001b[42m"
34-
on_Yellow = "\u001b[43m"
35-
on_Blue = "\u001b[44m"
36-
on_Magenta = "\u001b[45m"
37-
on_Cyan = "\u001b[46m"
38-
on_LightGray = "\u001b[47m"
39-
40-
41-
on_Gray = "\u001b[40;1m"
42-
on_LightRed = "\u001b[41;1m"
43-
on_LightGreen = "\u001b[42;1m"
44-
on_LightYellow = "\u001b[43;1m"
45-
on_LightBlue = "\u001b[44;1m"
46-
on_LightMagenta = "\u001b[45;1m"
47-
on_LightCyan = "\u001b[46;1m"
48-
on_White = "\u001b[47;1m"
49-
50-
def clear(txt):
51-
txt = re.sub("\033\[[0-9][0-9]?m", "", txt)
52-
txt = re.sub("\\u001b\[[0-9][0-9]?m", "", txt)
53-
txt = re.sub("\\u001b\[[0-9][0-9]?;1m", "", txt)
54-
return txt
3+
NC = '\033[0m' # No Color, reset all
4+
5+
Bold = '\033[1m'
6+
Underlined = '\033[4m'
7+
Blink = '\033[5m'
8+
Inverted = '\033[7m'
9+
Hidden = '\033[8m'
10+
11+
Black = '\033[30m'
12+
Red = '\033[31m'
13+
Green = '\033[32m'
14+
Yellow = '\033[33m'
15+
Blue = '\033[34m'
16+
Purple = '\033[35m'
17+
Cyan = '\033[36m'
18+
LightGray = '\033[37m'
19+
20+
Gray = "\u001b[30;1m"
21+
LightRed = "\u001b[31;1m"
22+
LightGreen = "\u001b[32;1m"
23+
LightYellow = "\u001b[33;1m"
24+
LightBlue = "\u001b[34;1m"
25+
LightMagenta = "\u001b[35;1m"
26+
LightCyan = "\u001b[36;1m"
27+
White = "\u001b[37;1m"
28+
29+
30+
on_Black = "\u001b[40m"
31+
on_Red = "\u001b[41m"
32+
on_Green = "\u001b[42m"
33+
on_Yellow = "\u001b[43m"
34+
on_Blue = "\u001b[44m"
35+
on_Magenta = "\u001b[45m"
36+
on_Cyan = "\u001b[46m"
37+
on_LightGray = "\u001b[47m"
38+
39+
40+
on_Gray = "\u001b[40;1m"
41+
on_LightRed = "\u001b[41;1m"
42+
on_LightGreen = "\u001b[42;1m"
43+
on_LightYellow = "\u001b[43;1m"
44+
on_LightBlue = "\u001b[44;1m"
45+
on_LightMagenta = "\u001b[45;1m"
46+
on_LightCyan = "\u001b[46;1m"
47+
on_White = "\u001b[47;1m"
48+
49+
def clear(txt):
50+
txt = re.sub("\033\[[0-9][0-9]?m", "", txt)
51+
txt = re.sub("\\u001b\[[0-9][0-9]?m", "", txt)
52+
txt = re.sub("\\u001b\[[0-9][0-9]?;1m", "", txt)
53+
return txt

LRFutils/log.py renamed to LRFutils/logs.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from datetime import datetime
55
import os
66
import platform
7-
from LRFutils.color import Color
7+
from LRFutils import color as C
88
import sys
99
import traceback
1010

@@ -15,25 +15,25 @@ def now(human=False, color=False):
1515
time = str(datetime.now())
1616
if human:
1717
if color:
18-
return f"{Color.Blue}{time[8:10]}/{time[5:7]}/{time[0:4]}{Color.NC} at {Color.Purple}{time[11:13]}:{time[14:16]}:{time[17:19]}{Color.NC}"
18+
return f"{C.Blue}{time[8:10]}/{time[5:7]}/{time[0:4]}{C.NC} at {C.Purple}{time[11:13]}:{time[14:16]}:{time[17:19]}{C.NC}"
1919
else:
2020
return f"{time[8:10]}/{time[5:7]}/{time[0:4]} at {time[11:13]}:{time[14:16]}:{time[17:19]}"
2121
else:
2222
return time.replace(" ", "_").replace(":", ".")
2323

2424
startTime = now()
25-
filename = f"logs/{startTime}.log"
25+
filename = f"logs/{startTime}.logs"
2626
if not os.path.isdir("logs"): os.makedirs(f"logs/")
2727

2828
# Getting the current environment
29-
with open(filename, "a") as logFile:
30-
logFile.write(f"ENVIRONMENT: {platform.uname()}\n\n")
29+
with open(filename, "a") as logsFile:
30+
logsFile.write(f"ENVIRONMENT: {platform.uname()}\n\n")
3131

3232
# Print message in log file
3333
def logSave(message):
3434
currentTime = now(human = True, color=False)
35-
with open(filename, "a", encoding="utf-8") as logFile:
36-
logFile.write(f"{currentTime} | {message}\n")
35+
with open(filename, "a", encoding="utf-8") as logsFile:
36+
logsFile.write(f"{currentTime} | {message}\n")
3737

3838
# Print message in terminal
3939
def logPrint(message):
@@ -43,27 +43,27 @@ def logPrint(message):
4343
# Info-styled messages
4444
def info(message):
4545
logSave(f"[INFO] {message}")
46-
message = Color.Green + "[INFO] " + Color.NC + message
46+
message = C.Green + "[INFO] " + C.NC + message
4747
logPrint(message)
4848

4949
# Warning-styled messages
5050
def warn(message):
5151
message = f"[WARNING] {message}"
5252
logSave(message)
53-
message = Color.Yellow + message + Color.NC
53+
message = C.Yellow + message + C.NC
5454
logPrint(message)
5555

5656
# Error-styled messages
5757
def error(message, etype = None, value = None, tb=None):
5858
message = f"[ERROR] {message}"
5959
logSave(message)
60-
message = Color.on_Red + message + Color.NC
60+
message = C.on_Red + message + C.NC
6161

6262
if etype is None or value is None or tb is None: tb = traceback.format_exc()
6363
else: tb = ''.join(traceback.format_exception(etype, value, tb))
6464
logSave(f"Full traceback below.\n\n{tb}")
6565

66-
logPrint(message + f"\n -> Look at {Color.Green}{filename}{Color.NC} for more information.\n")
66+
logPrint(message + f"\n -> Look at {C.Green}{filename}{C.NC} for more information.\n")
6767

6868
# Catch unexpected crashes
6969
def myexcepthook(etype, value, tb):

LRFutils/progress.py

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,119 @@
1-
from .color import Color
1+
from . import color
22
from time import time
33
import datetime
44

55
class Bar():
6-
def __init__(self, max = 1, width = 80, prefix = "", eta = True, decimals = 0, duration = True):
6+
7+
8+
9+
# ________________________________________________________________________________
10+
# Create a progress bar
11+
12+
def __init__(self, max:float|int = 1, width:int = 80, prefix:str = "", eta:bool = True, decimals:int = 0, show_duration:bool = True, average_ETA:int = 10):
713
self.max = max
8-
self.width = width if type(width) == int else 80
14+
self.width = width if isinstance(width,int) else 80
915
self.prefix = prefix
1016
self.eta = eta
1117
self.decimals = decimals
12-
self.lastProgress = 0
13-
self.lastUpdate = None
18+
self.previous_progress = [0]
19+
self.previous_update = []
1420
self.lastETA = "-"
1521
self.start_at = time()
16-
self.duration = duration
22+
self.duration = show_duration
23+
self.average_ETA = average_ETA
24+
25+
26+
# ________________________________________________________________________________
27+
# Compute the ETA
1728

1829
def update_eta(self, progress):
19-
if self.lastUpdate is not None and time()-self.lastUpdate < 1: return self.lastETA
2030

21-
progression = progress - self.lastProgress
22-
left = (self.max-progress)
31+
# Get index of the i-th revious update to average the ETA on i updates
32+
if len(self.previous_progress) <= self.average_ETA:
33+
i = 0
34+
else:
35+
i = -self.average_ETA
36+
37+
# Compute progression since the i-th previous update
38+
progression = progress - self.previous_progress[i]
39+
todo = self.max - progress
2340

24-
if progression == 0 or self.lastUpdate is None:
25-
self.lastUpdate = time()
26-
self.lastProgress = progress
41+
# If there is no progression or the bar was just created, it update the data but return the same ETA
42+
if progression == 0 or self.previous_update == []:
43+
self.previous_update.append(time())
44+
self.previous_progress.append(progress)
2745
self.lastETA = "-"
2846
return self.lastETA
2947

30-
seconds = left/progression * (time() - self.lastUpdate)
31-
self.lastUpdate = time()
32-
self.lastProgress = progress
33-
if seconds < 0: self.lastETA = "-"
34-
else: self.lastETA = str(datetime.timedelta(seconds=seconds)).split(".")[0]
48+
# If the bar was updated less than one second before,
49+
# we don't update it again to not slow the program
50+
if time()-self.previous_update[-1] < 1:
51+
return self.lastETA
52+
53+
# Compute the ETA in seconds
54+
try:
55+
seconds = todo / progression * (time() - self.previous_update[i])
56+
except:
57+
print(len(self.previous_update), i)
58+
59+
# Update bar data
60+
self.previous_update.append(time())
61+
self.previous_progress.append(progress)
62+
63+
# If the ETA is negative, then we show an undefined ETA
64+
if seconds < 0:
65+
self.lastETA = "-"
66+
# Else we return the number of seconds
67+
else:
68+
self.lastETA = str(datetime.timedelta(seconds=seconds)).split(".")[0]
69+
3570
return self.lastETA
3671

72+
73+
74+
# ________________________________________________________________________________
75+
# Update the progress bar
76+
3777
def __call__(self, progress: float, prefix=None, stop=False):
3878
if prefix is None: prefix = self.prefix
3979
else: prefix = str(prefix)
4080

4181
progress_normed = progress / self.max
4282
if progress == self.max : stop = True
43-
color = Color.Yellow if stop and progress_normed != 1 else Color.LightGreen
83+
color_bar = color.Yellow if stop and progress_normed != 1 else color.LightGreen
4484

4585
if stop: end = "\n"
4686
else: end = "\r"
4787

4888
if type(progress) == float: progress = round(progress, self.decimals)
49-
percent = f" {color}{round(progress_normed*100,self.decimals) if self.decimals > 0 else int(progress_normed*100)}%"
50-
frac = f" {Color.LightRed}{progress}/{self.max}" if self.max is not None else ''
51-
eta = f" {Color.NC}eta {Color.Blue}{self.update_eta(progress)}" if not stop else ''
52-
duration = f" {Color.Purple}{str(datetime.timedelta(seconds=time() - self.start_at)).split('.')[0]}" if self.duration else ''
89+
percent = f" {color_bar}{round(progress_normed*100,self.decimals) if self.decimals > 0 else int(progress_normed*100)}%"
90+
frac = f" {color.LightRed}{progress}/{self.max}" if self.max is not None else ''
91+
eta = f" {color.NC}eta {color.Blue}{self.update_eta(progress)}" if not stop else ''
92+
duration = f" {color.Purple}{str(datetime.timedelta(seconds=time() - self.start_at)).split('.')[0]}" if self.duration else ''
5393

54-
prefix = '' if prefix == '' else Color.NC + prefix + ' '
94+
prefix = '' if prefix == '' else color.NC + prefix + ' '
5595
suffix = f"{percent}{frac}{duration}{eta}"
5696

57-
barwidth = self.width - len(Color.clear(suffix)) - len(Color.clear(prefix))
97+
barwidth = self.width - len(color.clear(suffix)) - len(color.clear(prefix))
5898
barwidth = max(barwidth,10)
5999

60100
currentBar = int(round(min(progress_normed*barwidth,barwidth)))
61101
minBar = int(min(progress_normed*barwidth,barwidth))
62102

63-
if progress_normed == 0: bar = Color.White + '━' * barwidth
64-
elif progress_normed == 1: bar = Color.LightGreen + '━' * barwidth
65-
elif currentBar == minBar: bar = color + '━' * currentBar + Color.White + '╺' + Color.White + '━' * (barwidth - currentBar - 1)
66-
else: bar = color + '━' * (currentBar - 1) + color + '╸' + Color.White + '━' * (barwidth - currentBar)
103+
if progress_normed == 0: bar = color.White + '━' * barwidth
104+
elif progress_normed == 1: bar = color.LightGreen + '━' * barwidth
105+
elif currentBar == minBar: bar = color_bar + '━' * currentBar + color.White + '╺' + color.White + '━' * (barwidth - currentBar - 1)
106+
else: bar = color_bar + '━' * (currentBar - 1) + color_bar + '╸' + color.White + '━' * (barwidth - currentBar)
67107

68-
msg = f"{prefix}{bar}{suffix}{Color.NC}"
108+
msg = f"{prefix}{bar}{suffix}{color.NC}"
69109
print(msg, end=end)
70110

111+
112+
113+
# ________________________________________________________________________________
114+
# Stop the progress bar
115+
71116
def stop(self):
72-
self(self.lastProgress, stop=True)
73-
self.lastProgress = 0
117+
self(self.previous_progress[-1], stop=True)
118+
self.previous_progress = [0]
74119
self.lastUpdate = None

Leirof.egg-info/PKG-INFO

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Metadata-Version: 2.1
2+
Name: Leirof
3+
Version: 0.0.11
4+
Summary: Just a custom library to share with some colleagues. Use it at your own risks.
5+
Home-page: https://github.com/LeiRoF/LRFutils
6+
License: MIT
7+
Project-URL: Bug Tracker, https://github.com/LeiRoF/LRFutils/issues
8+
Classifier: [
9+
Classifier: "Programming Language :: Python :: 3",
10+
Classifier: "License :: OSI Approved :: MIT License",
11+
Classifier: "Operating System :: OS Independent",
12+
Classifier: 'Intended Audience :: Developers'
13+
Classifier: ]
14+
Requires-Python: >=3.10
15+
License-File: LICENSE

0 commit comments

Comments
 (0)