Pull progress: overall byte summary per tick

Per-layer events gave no sense of how much is LEFT. Downloading lines
are parsed per-layer and summed; every 3s the snapshot becomes
' 3.14/9.27 GB (34%) · 最大层 3f4a2b1c9d0e: 0.51/1.2 GB' instead of
a bare layer line. Key events (Pulling from / Status / errors) still
print immediately.

Co-ached-By: Claude <noreply@anthropic.com>
This commit is contained in:
sora 2026-09-17 03:42:44 +00:00
parent cf9a3f6d3f
commit e41cd48272

View File

@ -117,16 +117,44 @@ def _pull_one(image: str, verbose: bool = True) -> None:
text=True, bufsize=1) text=True, bufsize=1)
last_line = '' last_line = ''
last_show = 0.0 last_show = 0.0
layer_prog = {} # layer id -> (done_bytes, total_bytes): the raw
# per-layer events give no sense of OVERALL progress -- sum them
import os as _os import os as _os
import re as _re
def _bytes(s):
s = s.strip()
mult = {'kB': 1e3, 'KB': 1e3, 'MB': 1e6, 'GB': 1e9, 'B': 1}
for suf, m in mult.items():
if s.endswith(suf):
return int(float(s[:-len(suf)]) * m)
return int(float(s or 0))
for line in proc.stdout: for line in proc.stdout:
last_line = line.strip() last_line = line.strip()
m = _re.match(r'^([0-9a-f]{12}): Downloading.*?([\d.]+[kKMG]?B)/([\d.]+[kKMG]?B)', line)
if m:
layer_prog[m.group(1)] = (_bytes(m.group(2)), _bytes(m.group(3)))
if 'Pull complete' in line:
lid = line.split(':')[0]
if lid in layer_prog:
layer_prog[lid] = (layer_prog[lid][1], layer_prog[lid][1])
now = _time.monotonic() now = _time.monotonic()
if verbose and (any(k in line for k in key_evt = any(k in line for k in
('Pulling from', 'Pull complete', 'Status', ('Pulling from', 'Status', 'error', 'denied'))
'error', 'denied', 'Extracting', 'Verifying')) if verbose and key_evt:
or now - last_show > 3): # Downloading snapshots
print(f' {last_line[:100]}', file=_sys.stderr, flush=True) print(f' {last_line[:100]}', file=_sys.stderr, flush=True)
last_show = now last_show = now
elif verbose and now - last_show > 3 and layer_prog:
done = sum(v[0] for v in layer_prog.values())
tot = sum(v[1] for v in layer_prog.values())
if tot:
pct = done / tot * 100
big = max(layer_prog.items(), key=lambda kv: kv[1][1] - kv[1][0])
print(f'{done / 1e9:.2f}/{tot / 1e9:.2f} GB ({pct:.0f}%)'
f' · 最大层 {big[0]}: {big[1][0] / 1e9:.2f}/{big[1][1] / 1e9:.2f} GB',
file=_sys.stderr, flush=True)
last_show = now
proc.wait(timeout=3600) proc.wait(timeout=3600)
r = subprocess.CompletedProcess(proc.args, proc.returncode, r = subprocess.CompletedProcess(proc.args, proc.returncode,
stdout='', stderr=last_line) stdout='', stderr=last_line)