Skip to content

k4bench.remote

k4bench.remote

Discover and download benchmark run data from a WebEOS HTTP endpoint.

The WebEOS site serves an Apache-style directory listing at K4BENCH_DATA_URL. Expected layout::

{base_url}/
  {detector}/                                   e.g. ALLEGRO_o1_v03/
    {platform}/                                 e.g. x86_64-almalinux9-gcc14.2.0-opt/
      {stack}/                                  e.g. key4hep-2026-05-19/
        {sample}/                               e.g. single_e-_10GeV/
          {YYYY-MM-DD}/                         e.g. 2026-05-23/
            run_info.json
            machine_info.json
            {config}_results.csv
            {config}_events.json
            {config}_regions.json
            {config}.log

IncompleteFetch

IncompleteFetch(partial: object, failures: list[str])

Bases: RequestException

A read that could not be completed because the server did not answer.

Raised only to callers passing strict=True. By default those functions skip what they could not read (logged) and return the rest, or None; :attr:partial is what had been read when the strict call stopped, so a caller that can use an incomplete result still may — deliberately. The dashboard caches every result, and one missing items only because WebEOS stalled must be read again, not remembered as the answer.

A :class:requests.RequestException, so a caller that already falls back on a failed request falls back on this too, rather than mistaking a partial listing for a complete one.

Source code in k4bench/remote.py
def __init__(self, partial: object, failures: list[str]):
    shown = "; ".join(failures[:3])
    more = f" (+{len(failures) - 3} more)" if len(failures) > 3 else ""
    super().__init__(f"{len(failures)} read(s) failed: {shown}{more}")
    self.partial = partial
    self.failures = failures

list_detectors

list_detectors(base_url: str) -> list[str]

Return available detector names.

Underscore-prefixed directories at the top level (e.g. _reports/, where the nightly regression reports live) are reserved for non-detector data and skipped.

Source code in k4bench/remote.py
def list_detectors(base_url: str) -> list[str]:
    """Return available detector names.

    Underscore-prefixed directories at the top level (e.g. ``_reports/``, where
    the nightly regression reports live) are reserved for non-detector data and
    skipped.
    """
    return [d for d in _list_subdirs(base_url) if not d.startswith("_")]

list_platforms

list_platforms(base_url: str, detector: str) -> list[str]

Return available platforms for detector.

Source code in k4bench/remote.py
def list_platforms(base_url: str, detector: str) -> list[str]:
    """Return available platforms for *detector*."""
    return _list_subdirs(f"{base_url.rstrip('/')}/{detector}")

list_samples

list_samples(base_url: str, detector: str, platform: str, stack: str) -> list[str]

Return available physics samples for (detector, platform, stack).

Source code in k4bench/remote.py
def list_samples(base_url: str, detector: str, platform: str, stack: str) -> list[str]:
    """Return available physics samples for *(detector, platform, stack)*."""
    return sorted(_list_subdirs(
        f"{base_url.rstrip('/')}/{detector}/{platform}/{stack}"
    ))

scan_stack_samples

scan_stack_samples(base_url: str, detector: str, platform: str, *, strict: bool = False) -> dict[str, list[str]]

Return {stack: [samples]} for (detector, platform), newest stack first.

Single source of truth for both the cross-release sample union and the per-sample stack list, so the sidebar scans the release tree only once (one listing per stack) instead of twice. A sample may be added or dropped between Key4hep releases; callers derive the union and the per-sample stacks from this map. Stacks whose listing fails are skipped (logged); with strict, the first that fails without an answer stops the scan and raises :class:IncompleteFetch carrying what was listed.

Source code in k4bench/remote.py
def scan_stack_samples(
    base_url: str, detector: str, platform: str, *, strict: bool = False,
) -> dict[str, list[str]]:
    """Return ``{stack: [samples]}`` for *(detector, platform)*, newest stack first.

    Single source of truth for both the cross-release sample union and the
    per-sample stack list, so the sidebar scans the release tree only once
    (one listing per stack) instead of twice. A sample may be added or dropped
    between Key4hep releases; callers derive the union and the per-sample stacks
    from this map. Stacks whose listing fails are skipped (logged); with
    *strict*, the first that fails without an answer stops the scan and raises
    :class:`IncompleteFetch` carrying what was listed.
    """
    root = f"{base_url.rstrip('/')}/{detector}/{platform}"
    stacks = sorted(_list_subdirs(root), reverse=True)
    listed, failures = _list_each(
        {stack: f"{root}/{stack}" for stack in stacks},
        strict=strict, caller="scan_stack_samples",
    )
    out = {stack: sorted(samples) for stack, samples in listed.items()}
    if strict and failures:
        raise IncompleteFetch(out, failures)
    return out

list_runs

list_runs(base_url: str, detector: str, platform: str, stack: str, sample: str) -> list[str]

Return available run dates for a (detector, platform, stack, sample) combination, newest first.

Source code in k4bench/remote.py
def list_runs(
    base_url: str,
    detector: str,
    platform: str,
    stack: str,
    sample: str,
) -> list[str]:
    """Return available run dates for a *(detector, platform, stack, sample)* combination, newest first."""
    runs = _list_subdirs(
        f"{base_url.rstrip('/')}/{detector}/{platform}/{stack}/{sample}"
    )
    return sorted(runs, reverse=True)

list_run_dates_all_stacks

list_run_dates_all_stacks(base_url: str, detector: str, platform: str, sample: str, *, strict: bool = False) -> dict[str, list[str]]

Return {stack: [run_dates]} for (detector, platform, sample).

Discovery only — issues directory listings and no file downloads. The run-date directory names are YYYY-MM-DD, so the full set of available dates per stack is obtained cheaply; this populates the trend-window control and lets the caller download only the runs inside the selected window. Stacks that do not contain sample (a 404) or whose listing fails are skipped; with strict, the first that fails without an answer stops the scan and raises :class:IncompleteFetch carrying what was listed.

Source code in k4bench/remote.py
def list_run_dates_all_stacks(
    base_url: str,
    detector: str,
    platform: str,
    sample: str,
    *,
    strict: bool = False,
) -> dict[str, list[str]]:
    """Return ``{stack: [run_dates]}`` for *(detector, platform, sample)*.

    Discovery only — issues directory listings and **no** file downloads. The
    run-date directory names are ``YYYY-MM-DD``, so the full set of available
    dates per stack is obtained cheaply; this populates the trend-window control
    and lets the caller download only the runs inside the selected window.
    Stacks that do not contain *sample* (a 404) or whose listing fails are
    skipped; with *strict*, the first that fails without an answer stops the
    scan and raises :class:`IncompleteFetch` carrying what was listed.
    """
    root = f"{base_url.rstrip('/')}/{detector}/{platform}"
    stacks = _list_subdirs(root)
    listed, failures = _list_each(
        {stack: f"{root}/{stack}/{sample}" for stack in stacks},
        strict=strict, caller="list_run_dates_all_stacks",
    )
    out = {stack: sorted(runs) for stack, runs in listed.items() if runs}
    if strict and failures:
        raise IncompleteFetch(out, failures)
    return out

ensure_run_cached

ensure_run_cached(base_url: str, detector: str, platform: str, stack: str, sample: str, date: str, cache_root: str | None = None) -> Path

Download one run into a stable cache path and return it.

Cache layout: {cache_root}/{detector}/{platform}/{stack}/{sample}/{date}/. Historical runs are immutable, so a run whose .complete sentinel exists is returned without any HTTP. To stay correct across concurrent reruns and processes, files are downloaded into a private temp dir and the finished run is published with a single atomic rename: a reader therefore never sees a half-written run_dir, and an interrupted download leaves no partial run behind (the temp dir is discarded) rather than a dir that looks cached.

Source code in k4bench/remote.py
def ensure_run_cached(
    base_url: str,
    detector: str,
    platform: str,
    stack: str,
    sample: str,
    date: str,
    cache_root: str | None = None,
) -> Path:
    """Download one run into a stable cache path and return it.

    Cache layout: ``{cache_root}/{detector}/{platform}/{stack}/{sample}/{date}/``.
    Historical runs are immutable, so a run whose ``.complete`` sentinel exists is
    returned without any HTTP. To stay correct across concurrent reruns and
    processes, files are downloaded into a private temp dir and the finished run
    is published with a single atomic ``rename``: a reader therefore never sees a
    half-written ``run_dir``, and an interrupted download leaves no partial run
    behind (the temp dir is discarded) rather than a dir that looks cached.
    """
    root = Path(cache_root) if cache_root else _default_cache_root()
    run_dir = root / detector / platform / stack / sample / date
    sentinel = run_dir / ".complete"
    if sentinel.exists():
        return run_dir

    run_url = f"{base_url.rstrip('/')}/{detector}/{platform}/{stack}/{sample}/{date}"
    run_dir.parent.mkdir(parents=True, exist_ok=True)
    # Stage in a sibling temp dir (same filesystem, so the publish rename is atomic).
    tmp_dir = Path(tempfile.mkdtemp(prefix=f".{date}.tmp-", dir=run_dir.parent))
    session = _get_session()
    try:
        for fname in _list_files(run_url):
            # Validate the *decoded* name: a percent-encoded separator (e.g.
            # "%2e%2e%2fevil.csv" → "../evil.csv") slips past a raw-name check but
            # escapes the run dir once written, so decode first, then reject any
            # name that is not a single, plain path component.
            decoded = unquote(fname)
            if (
                not decoded
                or decoded in (".", "..")
                or "/" in decoded
                or "\\" in decoded
                or Path(decoded).name != decoded
            ):
                raise ValueError(f"Unsafe filename in listing: {fname!r}")
            resp = session.get(f"{run_url}/{fname}", timeout=_TIMEOUT)
            resp.raise_for_status()
            (tmp_dir / decoded).write_bytes(resp.content)
        (tmp_dir / ".complete").write_text("")
        _publish_run_dir(tmp_dir, run_dir, sentinel)
    except BaseException:
        shutil.rmtree(tmp_dir, ignore_errors=True)
        raise
    return run_dir

fetch_runs_windowed

fetch_runs_windowed(base_url: str, detector: str, platform: str, sample: str, stacks_dates: dict[str, list[str]], cache_root: str | None = None, *, strict: bool = False) -> list[dict]

Fetch every (stack, date) in stacks_dates in parallel, returning a list of {"stack", "date", "run_dir"} for the runs successfully cached.

Each run is fetched at most once (see :func:ensure_run_cached); callers pass an already date-windowed stacks_dates so only in-window runs are downloaded. Runs that fail to download are logged and skipped rather than aborting the load.

With strict, no download starts after the first failure that makes the result incomplete, and the call raises once those in flight have finished — so every run that landed stays in the on-disk cache and a retry fetches only what is missing. A run the server did not answer for raises :class:IncompleteFetch carrying the runs that landed; a local error — the cache directory full or unwritable — raises that OSError itself, since it is neither EOS's fault nor fixed by asking EOS again. A run whose listing names an unsafe file is skipped either way: it will be just as unsafe next time.

At most :data:_POOL_MAXSIZE downloads run at once — the connection pool's own ceiling (see :func:_get_session). More workers would add no throughput, only threads blocked waiting for a connection, and a blocked worker's download already counts as started: it could no longer be cancelled, and would run into the same failing server once a connection freed up. Work beyond the ceiling therefore waits in the executor's queue, where cancelling it stops it.

Source code in k4bench/remote.py
def fetch_runs_windowed(
    base_url: str,
    detector: str,
    platform: str,
    sample: str,
    stacks_dates: dict[str, list[str]],
    cache_root: str | None = None,
    *,
    strict: bool = False,
) -> list[dict]:
    """Fetch every ``(stack, date)`` in *stacks_dates* in parallel, returning a
    list of ``{"stack", "date", "run_dir"}`` for the runs successfully cached.

    Each run is fetched at most once (see :func:`ensure_run_cached`); callers pass
    an already date-windowed *stacks_dates* so only in-window runs are downloaded.
    Runs that fail to download are logged and skipped rather than aborting the load.

    With *strict*, no download starts after the first failure that makes the
    result incomplete, and the call raises once those in flight have finished —
    so every run that landed stays in the on-disk cache and a retry fetches
    only what is missing. A run the server did not answer for raises
    :class:`IncompleteFetch` carrying the runs that landed; a local error — the
    cache directory full or unwritable — raises that ``OSError`` itself, since
    it is neither EOS's fault nor fixed by asking EOS again. A run whose listing
    names an unsafe file is skipped either way: it will be just as unsafe next
    time.

    At most :data:`_POOL_MAXSIZE` downloads run at once — the connection
    pool's own ceiling (see :func:`_get_session`). More workers would add no
    throughput, only threads blocked waiting for a connection, and a blocked
    worker's download already counts as started: it could no longer be
    cancelled, and would run into the same failing server once a connection
    freed up. Work beyond the ceiling therefore waits in the executor's queue,
    where cancelling it stops it.
    """
    tasks = [(stack, date) for stack, dates in stacks_dates.items() for date in dates]
    if not tasks:
        return []

    results: list[dict] = []
    failures: list[str] = []
    local_error: OSError | None = None
    with ThreadPoolExecutor(max_workers=_POOL_MAXSIZE) as pool:
        futures = {
            pool.submit(
                ensure_run_cached,
                base_url, detector, platform, stack, sample, date, cache_root,
            ): (stack, date)
            for stack, date in tasks
        }
        for fut in as_completed(futures):
            if fut.cancelled():
                continue
            stack, date = futures[fut]
            try:
                run_dir = fut.result()
            # A RequestException is an OSError too, so it must be caught first.
            except requests.RequestException as exc:
                _log.warning("fetch_runs_windowed: failed %s/%s — %s", stack, date, exc)
                if _is_absent(exc):
                    continue
                failures.append(f"{stack}/{date}: {exc}")
            except ValueError as exc:
                _log.warning("fetch_runs_windowed: failed %s/%s — %s", stack, date, exc)
                continue
            except OSError as exc:
                _log.warning("fetch_runs_windowed: failed %s/%s — %s", stack, date, exc)
                local_error = local_error or exc
            else:
                results.append({"stack": stack, "date": date, "run_dir": str(run_dir)})
                continue
            if strict:
                for other in futures:
                    other.cancel()
    if strict and local_error is not None:
        raise local_error
    if strict and failures:
        raise IncompleteFetch(results, failures)
    return results

list_stacks

list_stacks(base_url: str, detector: str, platform: str) -> list[str]

Return the Key4hep releases benchmarked for (detector, platform), newest first.

Directory names as stored (key4hep-{YYYY-MM-DD}). Discovery only — one listing, no downloads.

Source code in k4bench/remote.py
def list_stacks(base_url: str, detector: str, platform: str) -> list[str]:
    """Return the Key4hep releases benchmarked for *(detector, platform)*, newest first.

    Directory names as stored (``key4hep-{YYYY-MM-DD}``). Discovery only — one
    listing, no downloads.
    """
    return sorted(
        _list_subdirs(f"{base_url.rstrip('/')}/{detector}/{platform}"), reverse=True
    )

fetch_stack_packages

fetch_stack_packages(base_url: str, detector: str, platform: str, stack: str, *, strict: bool = False) -> dict | None

Return the k4h_packages map of a release, or None.

Every detector benchmarked against a given release sourced the same stack, so any one run under it answers the question — this walks to the first run it finds and reads only that run_info.json (two listings and one small GET), rather than downloading a run directory.

None covers both "no run found" and "that run predates provenance capture": in either case the release's packages are unknown, which a caller must not confuse with an empty stack. With strict, the first read the server does not answer stops the walk and raises :class:IncompleteFetch instead: the None it would otherwise end in might not be the answer.

Source code in k4bench/remote.py
def fetch_stack_packages(
    base_url: str, detector: str, platform: str, stack: str, *, strict: bool = False,
) -> dict | None:
    """Return the ``k4h_packages`` map of a release, or ``None``.

    Every detector benchmarked against a given release sourced the *same*
    stack, so any one run under it answers the question — this walks to the
    first run it finds and reads only that ``run_info.json`` (two listings and
    one small GET), rather than downloading a run directory.

    ``None`` covers both "no run found" and "that run predates provenance
    capture": in either case the release's packages are unknown, which a caller
    must not confuse with an empty stack. With *strict*, the first read the
    server does not answer stops the walk and raises :class:`IncompleteFetch`
    instead: the ``None`` it would otherwise end in might not be the answer.
    """
    root = f"{base_url.rstrip('/')}/{detector}/{platform}/{stack}"

    def _stop_if_strict(where: str, exc: requests.RequestException) -> None:
        if strict and not _is_absent(exc):
            raise IncompleteFetch(None, [f"{where}: {exc}"]) from exc

    try:
        samples = sorted(_list_subdirs(root))
    except requests.RequestException as exc:
        _log.debug("fetch_stack_packages: no samples under %s — %s", root, exc)
        _stop_if_strict(root, exc)
        return None

    for sample in samples:
        try:
            dates = sorted(_list_subdirs(f"{root}/{sample}"), reverse=True)
        except requests.RequestException as exc:
            _stop_if_strict(f"{root}/{sample}", exc)
            continue
        for date in dates:
            url = f"{root}/{sample}/{date}/run_info.json"
            try:
                resp = _get_session().get(url, timeout=_TIMEOUT)
                resp.raise_for_status()
                packages = resp.json().get("k4h_packages")
            except ValueError as exc:
                # Malformed JSON (requests' decode error is also a
                # RequestException, so this clause must come first).
                _log.debug("fetch_stack_packages: %s — %s", url, exc)
                continue
            except requests.RequestException as exc:
                _log.debug("fetch_stack_packages: %s — %s", url, exc)
                _stop_if_strict(url, exc)
                continue
            if packages:
                return packages
    return None

fetch_run_info

fetch_run_info(base_url: str, detector: str, platform: str, stack: str, sample: str, run_id: str) -> dict | None

Return one exact run's parsed run_info.json, or None.

There is deliberately no directory walk or same-release shortcut: manual dispatches and partial re-runs can leave neighbouring run directories with different workloads and harness commits.

Source code in k4bench/remote.py
def fetch_run_info(
    base_url: str, detector: str, platform: str, stack: str, sample: str,
    run_id: str,
) -> dict | None:
    """Return one exact run's parsed ``run_info.json``, or ``None``.

    There is deliberately no directory walk or same-release shortcut: manual
    dispatches and partial re-runs can leave neighbouring run directories with
    different workloads and harness commits.
    """
    url = (
        f"{base_url.rstrip('/')}/{detector}/{platform}/{stack}/{sample}/"
        f"{run_id}/run_info.json"
    )
    try:
        resp = _get_session().get(url, timeout=_TIMEOUT)
        resp.raise_for_status()
        info = resp.json()
    except (requests.RequestException, ValueError) as exc:
        _log.debug("fetch_run_info: %s — %s", url, exc)
        return None
    return info if isinstance(info, dict) else None

fetch_run_commit

fetch_run_commit(base_url: str, detector: str, platform: str, stack: str, sample: str, run_id: str) -> tuple[str, str | None] | None

Return k4Bench's own (commit_sha, github_run_url) at one exact run, or None.

The run-keyed sibling of :func:fetch_stack_packages, for the one provenance field that varies per run rather than per release: the commit of the harness that produced the run. Unlike the release-keyed read there is no walk and no "any run answers" shortcut — a same-day manual dispatch or partial re-run can leave sibling groups carrying different commits, so only the named run's own run_info.json is read (one small GET).

None covers "no such run" and "that run predates the field" — the harness's position is unknown either way.

Source code in k4bench/remote.py
def fetch_run_commit(
    base_url: str, detector: str, platform: str, stack: str, sample: str,
    run_id: str,
) -> tuple[str, str | None] | None:
    """Return k4Bench's own ``(commit_sha, github_run_url)`` at one exact run,
    or ``None``.

    The run-keyed sibling of :func:`fetch_stack_packages`, for the one
    provenance field that varies per run rather than per release: the commit of
    the harness that produced the run. Unlike the release-keyed read there is
    no walk and no "any run answers" shortcut — a same-day manual dispatch or
    partial re-run can leave sibling groups carrying different commits, so only
    the named run's own ``run_info.json`` is read (one small GET).

    ``None`` covers "no such run" and "that run predates the field" — the
    harness's position is unknown either way.
    """
    info = fetch_run_info(base_url, detector, platform, stack, sample, run_id)
    if info is None:
        return None
    sha = info.get("commit_sha")
    if sha:
        return str(sha), info.get("github_run_url") or None
    return None

list_report_dates

list_report_dates(base_url: str) -> list[str]

Return available nightly regression-report dates (newest first).

Reports live at {base_url}/_reports/{YYYY-MM-DD}/report.json, written by the nightly regression-report CI job. An absent _reports/ tree (a 404: no report generated yet) is not an error — it returns an empty list. Any other failure raises: a listing the server did not answer says nothing about whether reports exist, and must not read as "none yet".

Source code in k4bench/remote.py
def list_report_dates(base_url: str) -> list[str]:
    """Return available nightly regression-report dates (newest first).

    Reports live at ``{base_url}/_reports/{YYYY-MM-DD}/report.json``, written
    by the nightly ``regression-report`` CI job. An absent ``_reports/`` tree
    (a 404: no report generated yet) is not an error — it returns an empty
    list. Any other failure raises: a listing the server did not answer says
    nothing about whether reports exist, and must not read as "none yet".
    """
    try:
        return sorted(_list_subdirs(f"{base_url.rstrip('/')}/_reports"), reverse=True)
    except requests.RequestException as exc:
        if not _is_absent(exc):
            raise
        _log.debug("list_report_dates: no _reports tree — %s", exc)
        return []

fetch_report

fetch_report(base_url: str, date: str, *, strict: bool = False) -> dict | None

Fetch and parse one nightly regression report, or None on failure.

With strict, a transient failure raises :class:IncompleteFetch instead of returning None.

Source code in k4bench/remote.py
def fetch_report(base_url: str, date: str, *, strict: bool = False) -> dict | None:
    """Fetch and parse one nightly regression report, or ``None`` on failure.

    With *strict*, a transient failure raises :class:`IncompleteFetch` instead
    of returning ``None``."""
    url = f"{base_url.rstrip('/')}/_reports/{date}/report.json"
    return _fetch_json(url, strict=strict, missing_is_normal=False)

fetch_blame

fetch_blame(base_url: str, date: str, *, strict: bool = False) -> dict | None

Fetch and parse one night's blame sidecar, or None when absent.

Blame lives beside the report at _reports/{date}/blame.json, written best-effort by the nightly regression-report job only on nights with a confirmed regression it could attribute — so most nights have none, and a 404 here is the common case, not an error (logged at debug, returns None). Uses the same shared, connection-pooled session as every other WebEOS read (see :func:_get_session) rather than a bare requests.get, so it shares the one real concurrency ceiling instead of opening its own connections. With strict, a transient failure raises :class:IncompleteFetch instead of returning None.

Source code in k4bench/remote.py
def fetch_blame(base_url: str, date: str, *, strict: bool = False) -> dict | None:
    """Fetch and parse one night's blame sidecar, or ``None`` when absent.

    Blame lives beside the report at ``_reports/{date}/blame.json``, written
    best-effort by the nightly ``regression-report`` job only on nights with a
    confirmed regression it could attribute — so **most nights have none**, and a
    404 here is the common case, not an error (logged at debug, returns ``None``).
    Uses the same shared, connection-pooled session as every other WebEOS read
    (see :func:`_get_session`) rather than a bare ``requests.get``, so it shares
    the one real concurrency ceiling instead of opening its own connections.
    With *strict*, a transient failure raises :class:`IncompleteFetch` instead
    of returning ``None``.
    """
    url = f"{base_url.rstrip('/')}/_reports/{date}/blame.json"
    return _fetch_json(url, strict=strict, missing_is_normal=True)

ensure_latest_run_cached

ensure_latest_run_cached(base_url: str, detector: str, platform: str, stack: str, sample: str, cache_root: str | None = None) -> Path | None

Cache and return only the newest run for (detector, platform, stack, sample).

Single-run tabs only ever display the latest run, so there is no need to download the full date history for the selected stack.

Source code in k4bench/remote.py
def ensure_latest_run_cached(
    base_url: str,
    detector: str,
    platform: str,
    stack: str,
    sample: str,
    cache_root: str | None = None,
) -> Path | None:
    """Cache and return only the newest run for *(detector, platform, stack, sample)*.

    Single-run tabs only ever display the latest run, so there is no need to
    download the full date history for the selected stack.
    """
    runs = list_runs(base_url, detector, platform, stack, sample)  # newest first
    if not runs:
        return None
    return ensure_run_cached(
        base_url, detector, platform, stack, sample, runs[0], cache_root
    )