Fix KeyError 'cur' regression (new-task branch missed the field); live bars hard-disable unless the real stream is a tty (FORCE_COLOR env made rich claim terminal-ness on pipes -> refresh thread stalled the whole run)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
c6046dd02e
commit
9eb1f7e58b
@ -558,7 +558,10 @@ def _cmd_eval_run(args) -> int:
|
|||||||
# One reporter for the WHOLE run: overall bar (which
|
# One reporter for the WHOLE run: overall bar (which
|
||||||
# benchmark) + sample bar (which sample), reused per
|
# benchmark) + sample bar (which sample), reused per
|
||||||
# benchmark via reset_samples().
|
# benchmark via reset_samples().
|
||||||
if _shared_reporter is None:
|
if _shared_reporter is None and console.is_terminal:
|
||||||
|
# live bars only on a real terminal: through pipes
|
||||||
|
# (| grep, > log) rich's refresh thread misbehaves
|
||||||
|
# and stalls the run -- plain phases instead
|
||||||
_shared_reporter = RichTerminalProgress(console=console)
|
_shared_reporter = RichTerminalProgress(console=console)
|
||||||
_shared_reporter.owned_externally = True
|
_shared_reporter.owned_externally = True
|
||||||
progress_reporter = _shared_reporter
|
progress_reporter = _shared_reporter
|
||||||
|
|||||||
@ -18,6 +18,18 @@ class RichTerminalProgress:
|
|||||||
# accept an EXTERNAL console: CLI phase messages and the live bar must
|
# accept an EXTERNAL console: CLI phase messages and the live bar must
|
||||||
# share one console, or the two writers interleave and repaint wrongly
|
# share one console, or the two writers interleave and repaint wrongly
|
||||||
self.console = console or Console()
|
self.console = console or Console()
|
||||||
|
# NON-TERMINAL (pipes, file redirects): rich's live refresh thread
|
||||||
|
# misbehaves and can stall the whole run. Disable everything live;
|
||||||
|
# log() degrades to a plain console print. Checked HERE so every
|
||||||
|
# creation path is safe regardless of caller logic.
|
||||||
|
# double check: FORCE_COLOR/FORCE_TERMINAL env can make rich claim
|
||||||
|
# terminal-ness while stdout is actually a pipe/file -> live thread
|
||||||
|
# stalls. Require the REAL underlying stream to be a tty.
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
_tty = getattr(self.console.file, 'isatty', None)
|
||||||
|
self.disabled = not (self.console.is_terminal
|
||||||
|
and callable(_tty) and _tty())
|
||||||
self.overall_id = None
|
self.overall_id = None
|
||||||
self.overall_total = 0
|
self.overall_total = 0
|
||||||
self.progress = Progress(
|
self.progress = Progress(
|
||||||
@ -43,6 +55,8 @@ class RichTerminalProgress:
|
|||||||
self.heartbeat_task = None
|
self.heartbeat_task = None
|
||||||
|
|
||||||
def set_overall(self, total: int, done: int, label: str = 'benches'):
|
def set_overall(self, total: int, done: int, label: str = 'benches'):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
"""Multi-benchmark runs: a bar above the sample bar showing which
|
"""Multi-benchmark runs: a bar above the sample bar showing which
|
||||||
benchmark we are on (also covers loading/scoring phases)."""
|
benchmark we are on (also covers loading/scoring phases)."""
|
||||||
self.overall_total = total
|
self.overall_total = total
|
||||||
@ -58,10 +72,14 @@ class RichTerminalProgress:
|
|||||||
self.progress.update(self.overall_id, completed=min(done, total))
|
self.progress.update(self.overall_id, completed=min(done, total))
|
||||||
|
|
||||||
def advance_overall(self):
|
def advance_overall(self):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
if self.overall_id is not None:
|
if self.overall_id is not None:
|
||||||
self.progress.advance(self.overall_id)
|
self.progress.advance(self.overall_id)
|
||||||
|
|
||||||
def start(self, total: int, description: str, completed: int = 0):
|
def start(self, total: int, description: str, completed: int = 0):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
self.started = time.monotonic()
|
self.started = time.monotonic()
|
||||||
if self.task_id is not None:
|
if self.task_id is not None:
|
||||||
return # one live reporter at a time; reuse across benchmarks
|
return # one live reporter at a time; reuse across benchmarks
|
||||||
@ -74,12 +92,17 @@ class RichTerminalProgress:
|
|||||||
failed=0,
|
failed=0,
|
||||||
rate="0.00",
|
rate="0.00",
|
||||||
inflight=0,
|
inflight=0,
|
||||||
|
cur="0s",
|
||||||
|
elapsed="0s",
|
||||||
|
eta="-",
|
||||||
waiting="00:00",
|
waiting="00:00",
|
||||||
last_result="restored",
|
last_result="restored",
|
||||||
)
|
)
|
||||||
self.heartbeat_task = asyncio.create_task(self._heartbeat())
|
self.heartbeat_task = asyncio.create_task(self._heartbeat())
|
||||||
|
|
||||||
def reset_samples(self, total: int, description: str, completed: int = 0):
|
def reset_samples(self, total: int, description: str, completed: int = 0):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
"""Start (or re-target) the per-sample task for the next benchmark."""
|
"""Start (or re-target) the per-sample task for the next benchmark."""
|
||||||
self.started = time.monotonic()
|
self.started = time.monotonic()
|
||||||
self.inflight = 0
|
self.inflight = 0
|
||||||
@ -90,6 +113,7 @@ class RichTerminalProgress:
|
|||||||
self.task_id = self.progress.add_task(
|
self.task_id = self.progress.add_task(
|
||||||
desc, total=total, completed=min(completed, total),
|
desc, total=total, completed=min(completed, total),
|
||||||
success=completed, failed=0, rate='0.00', inflight=0,
|
success=completed, failed=0, rate='0.00', inflight=0,
|
||||||
|
cur='0s', elapsed='0s', eta='-',
|
||||||
waiting='00:00', last_result='restored')
|
waiting='00:00', last_result='restored')
|
||||||
self.heartbeat_task = asyncio.create_task(self._heartbeat())
|
self.heartbeat_task = asyncio.create_task(self._heartbeat())
|
||||||
else:
|
else:
|
||||||
@ -100,12 +124,16 @@ class RichTerminalProgress:
|
|||||||
last_result='restored')
|
last_result='restored')
|
||||||
|
|
||||||
def set_bench_tag(self, tag: str):
|
def set_bench_tag(self, tag: str):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
"""Persistent counter shown on the sample bar, e.g. '[1/6]'."""
|
"""Persistent counter shown on the sample bar, e.g. '[1/6]'."""
|
||||||
self.bench_tag = tag + ' ' if tag else ''
|
self.bench_tag = tag + ' ' if tag else ''
|
||||||
if self._last_phase:
|
if self._last_phase:
|
||||||
self.set_phase(self._last_phase)
|
self.set_phase(self._last_phase)
|
||||||
|
|
||||||
def set_phase(self, phase: str):
|
def set_phase(self, phase: str):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
self._last_phase = phase
|
self._last_phase = phase
|
||||||
"""Retag the sample bar with what is happening (generating/scoring/
|
"""Retag the sample bar with what is happening (generating/scoring/
|
||||||
writing) -- the bar alone does not say which stage we are in."""
|
writing) -- the bar alone does not say which stage we are in."""
|
||||||
@ -115,6 +143,8 @@ class RichTerminalProgress:
|
|||||||
description=f'[green]{self.bench_tag}{self.bench_name} · {phase}[/green]')
|
description=f'[green]{self.bench_tag}{self.bench_name} · {phase}[/green]')
|
||||||
|
|
||||||
def begin_sample(self, label: str):
|
def begin_sample(self, label: str):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
if self.task_id is None:
|
if self.task_id is None:
|
||||||
return
|
return
|
||||||
self.inflight += 1
|
self.inflight += 1
|
||||||
@ -123,6 +153,8 @@ class RichTerminalProgress:
|
|||||||
waiting="00:00", last_result=f"waiting {label}")
|
waiting="00:00", last_result=f"waiting {label}")
|
||||||
|
|
||||||
def rollback(self):
|
def rollback(self):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
"""Pair a begin_sample that will NOT reach advance (retry path):
|
"""Pair a begin_sample that will NOT reach advance (retry path):
|
||||||
just decrement the in-flight count, no success/fail bookkeeping."""
|
just decrement the in-flight count, no success/fail bookkeeping."""
|
||||||
self.inflight = max(0, self.inflight - 1)
|
self.inflight = max(0, self.inflight - 1)
|
||||||
@ -130,6 +162,8 @@ class RichTerminalProgress:
|
|||||||
self.progress.update(self.task_id, inflight=self.inflight, cur='0s')
|
self.progress.update(self.task_id, inflight=self.inflight, cur='0s')
|
||||||
|
|
||||||
def advance(self, success: bool = True):
|
def advance(self, success: bool = True):
|
||||||
|
if self.disabled:
|
||||||
|
return
|
||||||
if self.task_id is None:
|
if self.task_id is None:
|
||||||
return
|
return
|
||||||
task = self.progress.tasks[self.task_id]
|
task = self.progress.tasks[self.task_id]
|
||||||
@ -172,12 +206,10 @@ class RichTerminalProgress:
|
|||||||
interleaved/repainted output; Progress.print routes through the live
|
interleaved/repainted output; Progress.print routes through the live
|
||||||
region correctly.
|
region correctly.
|
||||||
"""
|
"""
|
||||||
# highlight=False: rich auto-colorizes numbers, which made narration
|
if self.disabled or self.task_id is None:
|
||||||
# lines look inconsistent (some numbers cyan, some plain)
|
|
||||||
if self.task_id is not None:
|
|
||||||
self.progress.print(message, highlight=False)
|
|
||||||
else:
|
|
||||||
self.console.print(message, highlight=False)
|
self.console.print(message, highlight=False)
|
||||||
|
else:
|
||||||
|
self.progress.print(message, highlight=False)
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
"""Temporarily stop the live display (e.g. while hub downloads print
|
"""Temporarily stop the live display (e.g. while hub downloads print
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user