Files
2026-07-13 12:37:18 +08:00

247 lines
6.6 KiB
Python

# -*- coding:utf-8 -*-
# Author: hankcs
# Date: 2019-08-27 00:01
import datetime
import logging
import sys
import time
from typing import Union
from hanlp.utils.log_util import ErasablePrinter, color_format, color_format_len
def human_time_delta(days, hours, minutes, seconds, delimiter=' ') -> str:
units = locals().copy()
units.pop('delimiter')
non_zero = False
result = []
for key, val in sorted(units.items()):
append = False
if non_zero:
append = True
elif val:
non_zero = True
append = True
if append:
result.append('{} {}'.format(val, key[0]))
if not non_zero:
return '0 s'
return delimiter.join(result)
def seconds_to_time_delta(seconds):
seconds = round(seconds)
days = seconds // 86400
hours = seconds // 3600 % 24
minutes = seconds // 60 % 60
seconds = seconds % 60
return days, hours, minutes, seconds
def report_time_delta(seconds, human=True):
days, hours, minutes, seconds = seconds_to_time_delta(seconds)
if human:
return human_time_delta(days, hours, minutes, seconds)
return days, hours, minutes, seconds
class HumanTimeDelta(object):
def __init__(self, delta_seconds) -> None:
super().__init__()
self.delta_seconds = delta_seconds
def report(self, human=True):
return report_time_delta(self.delta_seconds, human)
def __str__(self) -> str:
return self.report(human=True)
def __truediv__(self, scalar):
return HumanTimeDelta(self.delta_seconds / scalar)
class CountdownTimer(ErasablePrinter):
def __init__(self, total: int, out=sys.stdout) -> None:
super().__init__(out=out)
self.total = total
self.current = 0
self.start = time.time()
self.finished_in = None
self.last_log_time = 0
def update(self, n=1):
self.current += n
self.current = min(self.total, self.current)
if self.current == self.total:
self.finished_in = time.time() - self.start
@property
def ratio(self) -> str:
return f'{self.current}/{self.total}'
@property
def ratio_percentage(self) -> str:
return f'{self.current / self.total:.2%}'
@property
def eta(self) -> float:
elapsed = self.elapsed
if self.finished_in:
eta = 0
else:
eta = elapsed / max(self.current, 0.1) * (self.total - self.current)
return eta
@property
def elapsed(self) -> float:
if self.finished_in:
elapsed = self.finished_in
else:
elapsed = time.time() - self.start
return elapsed
@property
def elapsed_human(self) -> str:
return human_time_delta(*seconds_to_time_delta(self.elapsed))
@property
def elapsed_average(self) -> float:
return self.elapsed / self.current
@property
def elapsed_average_human(self) -> str:
return human_time_delta(*seconds_to_time_delta(self.elapsed_average))
@property
def eta_human(self) -> str:
return human_time_delta(*seconds_to_time_delta(self.eta))
@property
def total_time(self) -> float:
elapsed = self.elapsed
if self.finished_in:
t = self.finished_in
else:
t = elapsed / max(self.current, 1) * self.total
return t
@property
def total_time_human(self) -> str:
return human_time_delta(*seconds_to_time_delta(self.total_time))
def stop(self, total=None):
if not self.finished_in or total:
self.finished_in = time.time() - self.start
if not total:
self.total = self.current
else:
self.current = total
self.total = total
@property
def et_eta(self):
_ = self.elapsed
if self.finished_in:
return self.elapsed
else:
return self.eta
@property
def et_eta_human(self):
text = human_time_delta(*seconds_to_time_delta(self.et_eta))
if self.finished_in:
return f'ET: {text}'
else:
return f'ETA: {text}'
@property
def finished(self):
return self.total == self.current
def log(self, info=None, ratio_percentage=True, ratio=True, step=1, interval=0.5, erase=True,
logger: Union[logging.Logger, bool] = None, newline=False, ratio_width=None):
self.update(step)
now = time.time()
if now - self.last_log_time > interval or self.finished:
cells = []
if ratio_percentage:
cells.append(self.ratio_percentage)
if ratio:
ratio = self.ratio
if not ratio_width:
ratio_width = self.ratio_width
ratio = ratio.rjust(ratio_width)
cells.append(ratio)
cells += [info, self.et_eta_human]
cells = [x for x in cells if x]
msg = f'{" ".join(cells)}'
self.last_log_time = now
self.print(msg, newline, erase, logger)
@property
def ratio_width(self) -> int:
return len(f'{self.total}') * 2 + 1
def print(self, msg, newline=False, erase=True, logger=None):
self.erase()
msg_len = 0 if newline else len(msg)
if self.finished and logger:
sys.stdout.flush()
if isinstance(logger, logging.Logger):
logger.info(msg)
else:
msg, msg_len = color_format_len(msg)
sys.stdout.write(msg)
if newline:
sys.stdout.write('\n')
msg_len = 0
self._last_print_width = msg_len
if self.finished and not logger:
if erase:
self.erase()
else:
sys.stdout.write("\n")
self._last_print_width = 0
sys.stdout.flush()
class Timer(object):
def __init__(self) -> None:
self.last = time.time()
def start(self):
self.last = time.time()
def stop(self) -> HumanTimeDelta:
now = time.time()
seconds = now - self.last
self.last = now
return HumanTimeDelta(seconds)
def now_human(year='y'):
now = datetime.datetime.now()
return now.strftime(f"%{year}-%m-%d %H:%M:%S")
def now_datetime():
return now_human('Y')
def now_filename(fmt="%y%m%d_%H%M%S"):
"""Generate filename using current datetime, in 20180102_030405 format
Args:
fmt: (Default value = "%y%m%d_%H%M%S")
Returns:
"""
now = datetime.datetime.now()
return now.strftime(fmt)