Add an example render to test/demo how the json-seq based progress
works. It needs the python `tqdm` package for the actual rendering.
See the output with:
```
$ sudo OSBUILD_TEST_STORE=/var/tmp/osbuild-test-store \
python3 -m osbuild --libdir=. --monitor=JSONSeqMonitor --export image \
--output-dir=/tmp/output-dir ./test/data/manifests/fedora-boot.json | ./tools/osbuild-json-seq-progress-example-renderer
```
91 lines
2.8 KiB
Python
Executable file
91 lines
2.8 KiB
Python
Executable file
#!/usr/bin/python3
|
|
#
|
|
# example how to use the json-seq rendering
|
|
#
|
|
|
|
import json
|
|
import sys
|
|
|
|
import tqdm
|
|
|
|
|
|
class TqdmProgressRenderer:
|
|
BAR_FMT = "{desc} ({n_fmt}/{total_fmt}): {percentage:3.0f}%|{bar}|{elapsed}"
|
|
|
|
def __init__(self, inf, outf):
|
|
self._pbar = None
|
|
self._sub_pbar = None
|
|
self._inf = inf
|
|
self._outf = outf
|
|
self._last_done = 0
|
|
self._last_sub_done = 0
|
|
|
|
def _read_json_seq_rec(self):
|
|
# *sigh* we really should be using a proper json-seq reader here
|
|
while True:
|
|
line = self._inf.readline()
|
|
if not line:
|
|
return None
|
|
try:
|
|
payload = json.loads(line.strip("\x1e"))
|
|
except json.JSONDecodeError:
|
|
self.warn(f"WARN: invalid json: {line}")
|
|
continue
|
|
return payload
|
|
|
|
def warn(self, warn):
|
|
if self._pbar:
|
|
self._pbar.write(warn)
|
|
else:
|
|
print(warn, file=self._outf)
|
|
|
|
def _init_pbar(self, pbar_name, total, pos):
|
|
pbar = getattr(self, pbar_name, None)
|
|
if pbar is not None:
|
|
return
|
|
pbar = tqdm.tqdm(total=total, position=pos, bar_format=self.BAR_FMT)
|
|
setattr(self, pbar_name, pbar)
|
|
|
|
def render(self):
|
|
while True:
|
|
js = self._read_json_seq_rec()
|
|
if js is None:
|
|
return
|
|
|
|
# main progress
|
|
main_progress = js.get("progress", {})
|
|
total = main_progress.get("total", 0)
|
|
self._init_pbar("_pbar", total, pos=0)
|
|
|
|
ctx = js["context"]
|
|
pipeline_name = ctx.get("pipeline", {}).get("name")
|
|
if pipeline_name:
|
|
self._pbar.set_description(f"Pipeline {pipeline_name}")
|
|
done = main_progress.get("done", 0)
|
|
if self._last_done < done:
|
|
self._pbar.update()
|
|
self._last_done = done
|
|
# reset sub-progress
|
|
self._last_sub_done = 0
|
|
self._sub_pbar = None
|
|
|
|
# sub progress
|
|
sub_progress = main_progress.get("progress")
|
|
if sub_progress:
|
|
total = sub_progress.get("total")
|
|
self._init_pbar("_sub_pbar", total, pos=1)
|
|
stage_name = ctx.get("pipeline", {}).get("stage", {}).get("name")
|
|
if stage_name:
|
|
self._sub_pbar.set_description(f"Stage {stage_name}")
|
|
sub_done = sub_progress.get("done", 0)
|
|
if self._last_sub_done < sub_done:
|
|
self._sub_pbar.update()
|
|
self._last_sub_done = sub_done
|
|
|
|
# (naively) handle messages (could decorate with origin)
|
|
self._pbar.write(js.get("message", "").strip())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
prg = TqdmProgressRenderer(sys.stdin, sys.stdout)
|
|
prg.render()
|