Skip to content

Commit d1ab585

Browse files
committed
feat: throttle progress display to 1s and only refresh on content change
1 parent d7330d3 commit d1ab585

File tree

1 file changed

+25
-6
lines changed

1 file changed

+25
-6
lines changed

ktoolbox/progress.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ class ProgressState:
179179
unit_scale: bool = False
180180
rate: Optional[float] = None
181181
last_update: float = field(default_factory=time.time)
182+
# Tracks the last state timestamp that was actually rendered.
183+
# Used to avoid advancing animation frames or forcing redraws
184+
# when nothing meaningful has changed.
185+
_last_render_seen: float = 0.0
182186
finished: bool = False
183187
failed: bool = False
184188
paused: bool = False
@@ -191,7 +195,8 @@ class ProgressManager:
191195
"""
192196

193197
def __init__(self, max_workers: int = 5, file: Optional[TextIO] = None,
194-
use_colors: bool = True, use_emojis: bool = True):
198+
use_colors: bool = True, use_emojis: bool = True,
199+
update_interval: float = 0.2):
195200
"""
196201
Initialize the progress manager.
197202
@@ -212,29 +217,35 @@ def __init__(self, max_workers: int = 5, file: Optional[TextIO] = None,
212217
self._total_jobs = 0
213218
self._completed_jobs = 0
214219
self._failed_jobs = 0
220+
self._existed_jobs = 0
215221

216222
# Terminal control
217223
self._lines_written = 0
218224
self._last_display_time = 0
219-
self._update_interval = 0.1 # Update every 100ms
225+
# Update interval (seconds). When downloads change, refresh at most
226+
# once per `update_interval`. Default is 1.0s to avoid excessive redraws.
227+
self._update_interval = float(update_interval)
220228

221229
# Display deduplication
222230
self._last_display_content = ""
223231

224-
def set_job_totals(self, total: int, completed: int = 0, failed: int = 0):
232+
def set_job_totals(self, total: int, completed: int = 0, failed: int = 0, existed: int = 0):
225233
"""Set the total number of jobs for overall progress tracking"""
226234
with self._lock:
227235
self._total_jobs = total
228236
self._completed_jobs = completed
229237
self._failed_jobs = failed
238+
self._existed_jobs = existed
230239

231-
def update_job_progress(self, completed: int = None, failed: int = None):
240+
def update_job_progress(self, completed: int = None, failed: int = None, existed: int = None):
232241
"""Update overall job progress"""
233242
with self._lock:
234243
if completed is not None:
235244
self._completed_jobs = completed
236245
if failed is not None:
237246
self._failed_jobs = failed
247+
if existed is not None:
248+
self._existed_jobs = existed
238249

239250
def create_progress_bar(self, desc: str, total: Optional[int] = None,
240251
unit: str = "B", unit_scale: bool = True) -> 'ManagedTqdm':
@@ -434,11 +445,13 @@ def _render_overall_progress(self) -> List[str]:
434445
total_colored = ColorTheme.colorize(str(self._total_jobs), ColorTheme.BRIGHT_WHITE)
435446
running_colored = ColorTheme.colorize(str(running), ColorTheme.BRIGHT_CYAN)
436447
waiting_colored = ColorTheme.colorize(str(waiting), ColorTheme.BRIGHT_YELLOW)
448+
existed_colored = ColorTheme.colorize(str(self._existed_jobs), ColorTheme.BRIGHT_WHITE)
437449
else:
438450
completed_colored = str(self._completed_jobs)
439451
total_colored = str(self._total_jobs)
440452
running_colored = str(running)
441453
waiting_colored = str(waiting)
454+
existed_colored = str(self._existed_jobs)
442455

443456
# Calculate overall download speed from active progress bars
444457
total_rate = 0
@@ -468,7 +481,8 @@ def _render_overall_progress(self) -> List[str]:
468481
pct_colored,
469482
f"| Jobs: {completed_colored}/{total_colored}",
470483
f"| {running_colored} running",
471-
f"| {waiting_colored} waiting"
484+
f"| {waiting_colored} waiting",
485+
f"| {existed_colored} existed"
472486
])
473487

474488
if speed_str:
@@ -521,10 +535,15 @@ def _render_single_progress_bar(self, state: ProgressState) -> str:
521535
status_emoji = ColorTheme.WAITING
522536
else:
523537
# Animated spinner for active downloads
538+
# Advance spinner only when this progress state's content changed
539+
# since the last render to avoid continuous terminal refreshes
540+
# caused solely by animation frames.
524541
current_time = time.time()
525-
if current_time - _animation_state['last_update'] > 0.1:
542+
last_seen = getattr(state, '_last_render_seen', 0.0)
543+
if state.last_update != last_seen:
526544
_animation_state['frame'] = (_animation_state['frame'] + 1) % len(ColorTheme.SPINNER_FRAMES)
527545
_animation_state['last_update'] = current_time
546+
state._last_render_seen = state.last_update
528547
status_emoji = ColorTheme.SPINNER_FRAMES[_animation_state['frame']]
529548
else:
530549
status_emoji = ""

0 commit comments

Comments
 (0)