rpx_benchmark.dataset_hub.croissant

Stage the Croissant metadata JSON into the upload tree.

Croissant (https://mlcommons.org/working-groups/croissant/) is the ML metadata standard HuggingFace renders alongside dataset cards and that search platforms (Kaggle, Papers-with-Code) parse. We already authored rpx_benchmark/dataset_hub/croissant_template.json against an earlier dataset URL; this helper:

  1. Copies the source JSON into <staging>/rpx_croissant.json.
  2. Patches the url, version, and citeAs fields to point at the HF repo (the source pre-dates the HF repo decision).
  3. Annotates the description so consumers know that per-frame records now live inside per-modality tar shards rather than as loose files (the field-level path descriptions in the JSON still describe the logical type, but the on-disk shape changed under our packer).

A full path-by-path schema reconciliation against the new tar layout is left to the dataset owner — it's a deeper edit that depends on what loaders downstream consumers actually use, and is not blocking for the v1 upload.

  1"""Stage the Croissant metadata JSON into the upload tree.
  2
  3Croissant (https://mlcommons.org/working-groups/croissant/) is the ML
  4metadata standard HuggingFace renders alongside dataset cards and that
  5search platforms (Kaggle, Papers-with-Code) parse. We already authored
  6``rpx_benchmark/dataset_hub/croissant_template.json`` against
  7an earlier dataset URL; this helper:
  8
  91. **Copies** the source JSON into ``<staging>/rpx_croissant.json``.
 102. **Patches** the `url`, `version`, and `citeAs` fields to point at the
 11   HF repo (the source pre-dates the HF repo decision).
 123. **Annotates** the `description` so consumers know that per-frame
 13   records now live inside per-modality tar shards rather than as loose
 14   files (the field-level path descriptions in the JSON still describe
 15   the logical type, but the on-disk shape changed under our packer).
 16
 17A full path-by-path schema reconciliation against the new tar layout is
 18left to the dataset owner — it's a deeper edit that depends on what
 19loaders downstream consumers actually use, and is not blocking for the
 20v1 upload.
 21"""
 22
 23from __future__ import annotations
 24
 25import json
 26from dataclasses import dataclass
 27from pathlib import Path
 28from typing import Optional
 29
 30from ..exceptions import ConfigError, DatasetError
 31from ..logging_utils import get_logger
 32from .recipes import DEFAULT_REPO_ID
 33
 34log = get_logger(__name__)
 35
 36
 37# Default source of the Croissant template. The JSON ships inside the
 38# package so pip-installed users get it without needing any local
 39# checkout outside the package.
 40def _default_croissant_src() -> Path:
 41    """``<package>/dataset_hub/croissant_template.json``."""
 42    return Path(__file__).resolve().parent / "croissant_template.json"
 43
 44
 45@dataclass(frozen=True)
 46class CroissantPatch:
 47    """Knobs for the staging step."""
 48
 49    repo_id: str = DEFAULT_REPO_ID
 50    version: str = "1.0.0"
 51    cite_as: Optional[str] = None
 52    annotate_layout: bool = True
 53
 54
 55def _hf_url(repo_id: str) -> str:
 56    return f"https://huggingface.co/datasets/{repo_id}"
 57
 58
 59_LAYOUT_NOTE = (
 60    "\n\nNote on on-disk shape: per-frame records are physically packed "
 61    "into per-(scene, phase, modality) tar shards on the HF repo "
 62    "(scenes/<scene>/<phase>/<modality>.tar for raw modalities; "
 63    "labels/<modality>/v<N>.tar for versioned labels). The "
 64    "rpx_benchmark.dataset_hub downloader resolves selective task+split "
 65    "downloads from these shards transparently."
 66)
 67
 68
 69def stage_croissant(
 70    staging_root: Path,
 71    src: Optional[Path] = None,
 72    patch: Optional[CroissantPatch] = None,
 73    overwrite: bool = False,
 74) -> Path:
 75    """Copy + patch the Croissant JSON into ``<staging>/rpx_croissant.json``.
 76
 77    Returns the destination path.
 78    """
 79    src = Path(src) if src else _default_croissant_src()
 80    patch = patch or CroissantPatch()
 81    if not src.is_file():
 82        raise ConfigError(
 83            f"croissant source JSON does not exist: {src}",
 84            hint=(
 85                "Pass src= explicitly, or check that "
 86                "croissant_template.json is present in the package "
 87                "in the repo."
 88            ),
 89        )
 90
 91    out_path = Path(staging_root) / "rpx_croissant.json"
 92    if out_path.exists() and not overwrite:
 93        raise DatasetError(
 94            f"refusing to overwrite existing croissant: {out_path}",
 95            hint="Pass overwrite=True or remove the file first.",
 96        )
 97
 98    payload = json.loads(src.read_text(encoding="utf-8"))
 99
100    payload["url"] = _hf_url(patch.repo_id)
101    payload["version"] = patch.version
102    if patch.cite_as is not None:
103        payload["citeAs"] = patch.cite_as
104
105    if patch.annotate_layout:
106        desc = payload.get("description", "")
107        if _LAYOUT_NOTE.strip() not in desc:
108            payload["description"] = desc + _LAYOUT_NOTE
109
110    out_path.parent.mkdir(parents=True, exist_ok=True)
111    out_path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
112    log.info("staged croissant → %s (%d bytes)", out_path, out_path.stat().st_size)
113    return out_path
log = <Logger rpx_benchmark.dataset_hub.croissant (WARNING)>
@dataclass(frozen=True)
class CroissantPatch:
46@dataclass(frozen=True)
47class CroissantPatch:
48    """Knobs for the staging step."""
49
50    repo_id: str = DEFAULT_REPO_ID
51    version: str = "1.0.0"
52    cite_as: Optional[str] = None
53    annotate_layout: bool = True

Knobs for the staging step.

CroissantPatch( repo_id: str = 'IRVLUTD/RPX', version: str = '1.0.0', cite_as: Optional[str] = None, annotate_layout: bool = True)
repo_id: str = $GITHUB_REPOSITORY
version: str = '1.0.0'
cite_as: Optional[str] = None
annotate_layout: bool = True
def stage_croissant( staging_root: pathlib.Path, src: Optional[pathlib.Path] = None, patch: Optional[CroissantPatch] = None, overwrite: bool = False) -> pathlib.Path:
 70def stage_croissant(
 71    staging_root: Path,
 72    src: Optional[Path] = None,
 73    patch: Optional[CroissantPatch] = None,
 74    overwrite: bool = False,
 75) -> Path:
 76    """Copy + patch the Croissant JSON into ``<staging>/rpx_croissant.json``.
 77
 78    Returns the destination path.
 79    """
 80    src = Path(src) if src else _default_croissant_src()
 81    patch = patch or CroissantPatch()
 82    if not src.is_file():
 83        raise ConfigError(
 84            f"croissant source JSON does not exist: {src}",
 85            hint=(
 86                "Pass src= explicitly, or check that "
 87                "croissant_template.json is present in the package "
 88                "in the repo."
 89            ),
 90        )
 91
 92    out_path = Path(staging_root) / "rpx_croissant.json"
 93    if out_path.exists() and not overwrite:
 94        raise DatasetError(
 95            f"refusing to overwrite existing croissant: {out_path}",
 96            hint="Pass overwrite=True or remove the file first.",
 97        )
 98
 99    payload = json.loads(src.read_text(encoding="utf-8"))
100
101    payload["url"] = _hf_url(patch.repo_id)
102    payload["version"] = patch.version
103    if patch.cite_as is not None:
104        payload["citeAs"] = patch.cite_as
105
106    if patch.annotate_layout:
107        desc = payload.get("description", "")
108        if _LAYOUT_NOTE.strip() not in desc:
109            payload["description"] = desc + _LAYOUT_NOTE
110
111    out_path.parent.mkdir(parents=True, exist_ok=True)
112    out_path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
113    log.info("staged croissant → %s (%d bytes)", out_path, out_path.stat().st_size)
114    return out_path

Copy + patch the Croissant JSON into <staging>/rpx_croissant.json.

Returns the destination path.