Skip to navigation

nemo_gym.data.tasks.harbor.manifest

View as Markdown

manifest.toml: the derived identity of a dataset folder.

The file sits beside the task folders and holds only facts computed from them: where the folders came from (source), one dataset digest, the digest of dataset.toml alone, and per task the folder digest (the value rows carry as _ng_digest) with its upstream pin. The dataset’s name and version live only in dataset.toml, so nothing appears in both files; what a fetch records besides is the hub reference it was made from (ref), a fact of the fetch like the pins, which is what a committed entry’s declared source is checked against without the hub (nemo_gym.data.tasks.harbor.prepare._check_fetch_record). Nobody edits the manifest by hand.

The output is deterministic: a fixed key order, sorted tasks and no timestamps, so equal manifests mean identical inputs and two runs compare by [dataset].digest. The dataset digest is a hash over the sorted per-task digests, then the dataset.toml digest, then, when the dataset ships them, the digest of the dataset-level Gym files that shape every task (task_data.py, the row model, and verifier.py, the shared grader; dataset_files_digest, recorded only when present, so a dataset without them hashes and reads as before). Two datasets that differ only in a timeout override in dataset.toml, or only in the shared grader, are therefore different datasets, on purpose: either can change a score. Like dataset.toml, those files are Gym’s layer, not fetched content: an edit rewrites the manifest and is reported, while an edited task folder of a fetched dataset is refused.

Fetchers write the manifest (nemo_gym.data.tasks.harbor.hub, nemo_gym.data.tasks.harbor.package_store) and record only the tasks they fetched, so a folder nobody fetched is never blessed; prepare writes it for a local folder, unless git tracks the folder (the repository’s own example datasets), whose identity is computed in memory. Prepare recomputes the per-task digests from the folders and refuses to run a fetched dataset whose folders no longer match (check_manifest). A task folder that cannot be hashed (a file under it escapes it through a link, or cannot be read) is recorded by name without a digest and does not enter the dataset digest: the loader refuses the same folder, so no row runs it, and the rest of a local dataset still prepares; a fetched manifest that recorded its digest names it when it refuses.

Module Contents

Classes

NameDescription
Manifestmanifest.toml in memory. tasks maps a task name to its digest and pin keys.
ManifestErrorThe manifest cannot be read, or the task folders no longer match it.
UnsupportedManifestErrorThe manifest was written by a newer Gym (a schema_version this Gym does not read).

Functions

NameDescription
_file_digest-
_quotevalue as a TOML basic string, ASCII only.
_someThe first five of names and how many there are in all.
_write_or_warnWrite the manifest; a folder that cannot be written (read-only shared storage) keeps its in-memory identity.
check_manifestRecompute the manifest of folder from disk and reconcile it with the one on disk.
check_task_manifestHold one task folder to the manifest of the dataset folder whose check reaches it, when that is a fetch’s.
dataset_digestThe dataset’s identity: sorted per-task digests, then dataset.toml, then the dataset-level files when
dataset_files_digestDigest of the dataset-level Gym files that shape tasks (DATASET_FILES): name and content of each one
dataset_toml_digestDigest of dataset.toml alone, or of nothing when the file is absent.
enclosing_manifestThe nearest fetched manifest above path whose dataset folder’s check reaches path, with its folder.
gym_layer_changeWhich part of the Gym layer differs between two manifests of one task set, for a rewrite notice.
refresh_gym_layerBring a fetched manifest’s Gym-layer digests up to date with folder without touching its task records.
report_rewriteThe one-line notice that folder’s manifest was rewritten, and how the dataset digest moved.
task_differencesOne line naming the tasks whose digest differs between a manifest and the folders, or None.
task_foldersThe task folders under folder, as the loader discovers them (iter_task_folders: recursively,
tracked_by_gitWhether git tracks files under folder: the repository’s own example datasets, or a checkout.

Data

DATASET_FILES

DIGEST_RECORDING_SOURCES

LOCAL_SOURCE

MANIFEST_FILE

MANIFEST_SCHEMA_VERSION

SOURCES

TASK_FILE

TASK_KEYS

_EMPTY_DIGEST

_NO_HARD_LINKS

_SURROGATE_PAIR

logger

API

class nemo_gym.data.tasks.harbor.manifest.Manifest(
source: str,
digest: str,
dataset_toml_digest: str,
tasks: dict[str, dict[str, str]] = dict(),
dataset_files_digest: str = _EMPTY_DIGEST,
ref: str | None = None,
unhashable: frozenset[str] = frozenset()
)
Dataclass

manifest.toml in memory. tasks maps a task name to its digest and pin keys.

dataset_files_digest
str = _EMPTY_DIGEST
dataset_toml_digest
str
digest
str
pins
dict[str, dict[str, str]]

The per-task entries without their digests: the upstream pins alone.

records_content
bool

Whether the manifest records what the task folders held, so a pin without a digest is unfinished business.

One with a dataset digest does, and so does one written before the dataset digest existed by a fetcher that recorded a digest per landed task from the start (DIGEST_RECORDING_SOURCES), whether or not a task had landed when it was written; a registry manifest from before digests has pins only, and check_manifest upgrades it by hashing the folders it finds.

ref
str | None = None
source
str
task_digests
dict[str, str | None]

Per-task folder digests; None for a task pinned before its folder was written or one that cannot be hashed.

tasks
dict[str, dict[str, str]] = field(default_factory=dict)
unhashable
frozenset[str] = field(default_factory=frozenset)
nemo_gym.data.tasks.harbor.manifest.Manifest._write_text(
fd: int
) -> None

Write the file’s bytes to fd and flush them to disk; the descriptor is closed.

folder: pathlib.Path,
source: str,
pins: dict[str, dict[str, str]] | None = None,
digests: dict[str, str] | None = None,
names: collections.abc.Iterable[str] | None = None,
ref: str | None = None
classmethod

Compute the manifest of folder from its task folders and dataset.toml.

pins are the upstream pins to record per task; a pinned task whose folder is absent (an interrupted fetch) keeps its pin and gets no digest. digests are folder digests already computed (by a loader or a fetch); any task folder not in it is hashed here, and the mapping is extended in place so a caller can reuse the work. ref is the hub reference a fetch was made from, recorded as the pins are and kept by every rewrite of the manifest.

names are the tasks to record. A fetcher passes the tasks it fetched, so a folder it did not fetch stays unrecorded (and prepare refuses it as not in the manifest) and no folder is scanned or stat-ed again for a name whose digest is known. Without it, every task folder on disk is recorded: a local dataset, or prepare recomputing a fetched one to compare. A folder found there that cannot be hashed is recorded by name with no digest (unhashable) and left out of the dataset digest, with a warning; the loader refuses it per task, so the rest of the dataset still prepares.

classmethod

The manifest beside the task folders, None when there is none; malformed raises.

nemo_gym.data.tasks.harbor.manifest.Manifest.text() -> str

The file’s bytes: fixed key order, sorted tasks, no timestamps.

nemo_gym.data.tasks.harbor.manifest.Manifest.write(
folder: pathlib.Path,
exclusive: bool = False
) -> pathlib.Path

Write manifest.toml atomically: a failed write leaves the previous, truthful file intact.

The file keeps the permissions it had, so a shared dataset another user could read stays readable after a rewrite; a new manifest gets the usual umask-aware mode (0o666 before the umask, like Path.write_text), not the owner-only mode a mkstemp file would have.

With exclusive, the manifest is created only where none is and FileExistsError is raised otherwise, the target untouched (the temporary is written and removed either way): the complete file is linked into place, so of two writers racing for an empty folder exactly one succeeds and a reader never sees a partial file. A filesystem without hard links falls back to an exclusive create of the target itself, still one winner, though a reader may then glimpse the file before its last byte. A fetcher claims a folder this way before its first task lands.

class nemo_gym.data.tasks.harbor.manifest.ManifestError()

Bases: ValueError

The manifest cannot be read, or the task folders no longer match it.

class nemo_gym.data.tasks.harbor.manifest.UnsupportedManifestError()

Bases: ManifestError

The manifest was written by a newer Gym (a schema_version this Gym does not read).

Distinct from a malformed manifest: the file is intact and says more than this Gym understands, so a fetcher must leave it alone rather than rewrite it with what it knows.

nemo_gym.data.tasks.harbor.manifest._file_digest(
path: pathlib.Path
) -> str
nemo_gym.data.tasks.harbor.manifest._quote(
value: str
) -> str

value as a TOML basic string, ASCII only.

JSON string escapes are valid TOML basic-string escapes (the quote, the backslash, the controls and U+007F as \u007f, other non-ASCII as \uXXXX) except that JSON writes a character above U+FFFF as a UTF-16 surrogate pair, which TOML forbids; that pair becomes TOML’s \UXXXXXXXX.

nemo_gym.data.tasks.harbor.manifest._some(
names: list[str]
) -> str

The first five of names and how many there are in all.

nemo_gym.data.tasks.harbor.manifest._write_or_warn(
folder: pathlib.Path
) -> bool

Write the manifest; a folder that cannot be written (read-only shared storage) keeps its in-memory identity.

nemo_gym.data.tasks.harbor.manifest.check_manifest(
folder: pathlib.Path,
digests: dict[str, str] | None = None

Recompute the manifest of folder from disk and reconcile it with the one on disk.

  • No manifest: the folder is a local dataset; one is written with source local, unless git tracks the folder, whose identity is then computed in memory and nothing written.
  • A manifest naming a source this Gym does not know (written by a newer Gym, or edited): refused, never read as local, because this Gym cannot tell whether the folders still match it.
  • A manifest without digests (written before they existed): upgraded in place, pins kept.
  • A manifest with per-task digests but no dataset digest (the package-store format before the schema was numbered): the folders are held to the recorded digests, and an unchanged folder is upgraded in place, the recorded digests kept, so a cache written by an earlier Gym keeps its fetch record.
  • A fetched manifest pinning a task with no digest: the fetch that wrote it was interrupted before that task landed (a fetcher claims the folder with every pin before its first download), so the folder is refused, named with the tasks still to come, until the fetch is run again.
  • Task folders differ from a local manifest: the folder is its own source of truth, so the manifest is rewritten and the new digest reported.
  • No manifest, or a local one, in a folder that a fetched dataset’s check reaches (a subfolder of a fetched dataset, within its discovery range, see enclosing_manifest): refused, and no local manifest written there. The task folders under it are that dataset’s, recorded in its manifest and held to it by its check and by check_task_manifest; a local manifest beside them would prepare an edited one as local and shadow the fetched manifest for the single-task check.
  • Task folders differ from a fetched manifest: the folder was edited by hand, or its source moved since the fetch. ManifestError says which tasks and what to do.
  • Only dataset.toml or a dataset-level task_data.py / verifier.py moved: the Gym layer changed on purpose, the task folders are the same, so the derived facts are rewritten without a fetch, and the rewrite says which.

digests are folder digests a loader already computed, extended in place with the rest. Returns the manifest now on disk.

nemo_gym.data.tasks.harbor.manifest.check_task_manifest(
task: pathlib.Path,
digests: dict[str, str] | None = None

Hold one task folder to the manifest of the dataset folder whose check reaches it, when that is a fetch’s.

A single task folder target (<dataset>/<task>, or a task deeper under the dataset folder) is prepared with the dataset.toml and image records beside it but is no folder of task folders, so check_manifest never runs for it; without this check an edited task of a fetched dataset prepared alone, as local, while the dataset folder was refused. The manifest that holds the task is the one the dataset folder’s check holds it to, not only its parent’s: that check discovers tasks under subfolders too (iter_task_folders, at most MAX_DISCOVERY_DEPTH levels down, never inside a hidden folder or through a link) and names each by its folder name, so a task moved into a subfolder of a fetched dataset is still the task its manifest records and is held to it here, while a task that check never sees (hidden, deeper, under a hidden or linked folder, or inside another task folder, as a fixture a task ships) is held to nothing, and a manifest above that range is not read (enclosing_manifest; a folder in between that carries a fetched manifest of its own, such as a dataset fetched inside another folder, holds its tasks itself, and a local one in between is passed over, as is a manifest.toml this Gym cannot read as a fetch’s record, another tool’s for one). The task’s own parent is read as it was: it is the dataset root the task is prepared with, so its manifest is the task’s dataset’s whatever it says, and one naming a source this Gym does not know, or written by a newer Gym, refuses the task as it refuses the dataset folder. The task’s folder digest is compared with the one that manifest records for it, with task_differences’ words: a folder that differs was edited by hand or its source moved, one the manifest does not record was never fetched, one that cannot be hashed is refused as the dataset folder’s check refuses it, and one recorded without a digest is a fetch that never finished. Nothing is written: the manifest stays the dataset folder’s record, and it is not upgraded or rewritten here. Returns that manifest, for the task’s identity to carry its source (the task is still prepared with the dataset.toml and image records beside it, as any single task folder target is: the manifest holds its content, not the settings the preparation applies), or None when no fetched manifest in that range holds the task (a standalone task folder, whose parent is whatever directory holds it, or one whose nearest manifest is local: the folder is its own source of truth, and the dataset folder’s next prepare records the edit) or that manifest is from before digests were recorded (pins only; the dataset folder’s prepare upgrades it).

digests are folder digests already computed, keyed by task name as check_manifest keys them: the task’s is used when present (a loader’s, to check the folder as it loaded) and hashed and stored otherwise.

nemo_gym.data.tasks.harbor.manifest.dataset_digest(
task_digests: dict[str, str],
dataset_toml: str,
dataset_files: str = _EMPTY_DIGEST
) -> str

The dataset’s identity: sorted per-task digests, then dataset.toml, then the dataset-level files when there are any (dataset_files other than the digest of nothing).

nemo_gym.data.tasks.harbor.manifest.dataset_files_digest(
folder: pathlib.Path
) -> str

Digest of the dataset-level Gym files that shape tasks (DATASET_FILES): name and content of each one present, in order; the digest of nothing when the dataset ships none (every hub dataset).

nemo_gym.data.tasks.harbor.manifest.dataset_toml_digest(
folder: pathlib.Path
) -> str

Digest of dataset.toml alone, or of nothing when the file is absent.

nemo_gym.data.tasks.harbor.manifest.enclosing_manifest(
path: pathlib.Path,
levels: int
) -> tuple[pathlib.Path, nemo_gym.data.tasks.harbor.manifest.Manifest] | None

The nearest fetched manifest above path whose dataset folder’s check reaches path, with its folder.

A dataset folder’s check (check_manifest) sees the task folders iter_task_folders discovers: at most MAX_DISCOVERY_DEPTH levels down, never inside a hidden folder, never through a symbolic link, never inside a task folder it found (a task’s own files are its content, not more tasks). So the folders whose manifest can hold path are its nearest levels parents, reached from path through visible folders that are not links or task folders; a manifest further up, or above a hidden, linked or task folder, belongs to a dataset whose check never sees path and is not read. Only a manifest this Gym reads as a fetch’s record (a source it knows other than local) claims the folders below it: a local one is passed over (a local folder inside a fetched dataset’s range is no dataset of its own, check_manifest refuses to prepare it as one, so the fetched manifest above holds its task folders), and so is a manifest.toml that is no record this Gym can read, such as another tool’s in a project root, or a newer Gym’s, which claims nothing here and is refused where it is the folder’s own, by that folder’s check. None when no such manifest is in reach.

nemo_gym.data.tasks.harbor.manifest.gym_layer_change(
) -> str

Which part of the Gym layer differs between two manifests of one task set, for a rewrite notice.

A manifest from before the dataset digest existed (per-task digests only, see Manifest.records_content) is rewritten for that alone, whatever dataset.toml held when it was written.

nemo_gym.data.tasks.harbor.manifest.refresh_gym_layer(
folder: pathlib.Path

Bring a fetched manifest’s Gym-layer digests up to date with folder without touching its task records.

A fetch records dataset.toml and the dataset-level Gym files as it found them, and rewrites the manifest only when it fetches something: a registry fetch with every folder in place, or a pull that only replaced the copy of an entry’s dataset.toml beside the tasks (nemo_gym.data.tasks.harbor.entries.prepare_destination), leaves dataset_toml_digest and the dataset digest describing a file that is no longer there. This recomputes those digests from disk and keeps every task’s recorded digest and pin, and the fetch’s reference, as they are (nothing is hashed, so a hand-edited folder is still caught by check_manifest, which rehashes), then writes the manifest when its text changed, with the notice check_manifest prints for the same rewrite (Rewrote ...; dataset digest <old> -> <new>), so a change of the dataset’s identity is announced whichever function applies it. Returns the manifest now on disk: None without one, and the old one untouched when it records pins only (check_manifest upgrades those) or names a source this Gym does not know (check_manifest refuses those).

nemo_gym.data.tasks.harbor.manifest.report_rewrite(
folder: pathlib.Path,
why: str
) -> None

The one-line notice that folder’s manifest was rewritten, and how the dataset digest moved.

nemo_gym.data.tasks.harbor.manifest.task_differences(
recorded: dict[str, str | None],
on_disk: dict[str, str | None],
unhashable: collections.abc.Iterable[str] = ()
) -> str | None

One line naming the tasks whose digest differs between a manifest and the folders, or None.

A task is missing when the manifest recorded its digest and the folder is gone (on_disk lacks it, or maps it to None because its pin outlived the folder), changed when both digests exist and differ, and not in the manifest when only the folder exists. One of unhashable (its folder is there but has no digest now, see Manifest.build) whose digest the manifest recorded is cannot be hashed, not missing.

nemo_gym.data.tasks.harbor.manifest.task_folders(
folder: pathlib.Path
) -> list[pathlib.Path]

The task folders under folder, as the loader discovers them (iter_task_folders: recursively, hidden folders skipped, links not followed), sorted by path. A task is named by its folder’s name there too.

nemo_gym.data.tasks.harbor.manifest.tracked_by_git(
folder: pathlib.Path
) -> bool

Whether git tracks files under folder: the repository’s own example datasets, or a checkout.

Such a folder is under version control already, so prepare computes its identity in memory instead of leaving a derived manifest.toml in the working tree. No git, no repository or any other failure reads as not tracked.

nemo_gym.data.tasks.harbor.manifest.DATASET_FILES = (TASK_DATA_MODULE, VERIFIER_FILE)
nemo_gym.data.tasks.harbor.manifest.DIGEST_RECORDING_SOURCES = ('harbor-package-store',)
nemo_gym.data.tasks.harbor.manifest.LOCAL_SOURCE = 'local'
nemo_gym.data.tasks.harbor.manifest.MANIFEST_FILE = 'manifest.toml'
nemo_gym.data.tasks.harbor.manifest.MANIFEST_SCHEMA_VERSION = 1
nemo_gym.data.tasks.harbor.manifest.SOURCES = ('harbor-registry', 'harbor-package-store', 'git', 'local')
nemo_gym.data.tasks.harbor.manifest.TASK_FILE = 'task.toml'
nemo_gym.data.tasks.harbor.manifest.TASK_KEYS = ('digest', 'git_url', 'git_commit_id', 'path', 'package', 'content_hash')
nemo_gym.data.tasks.harbor.manifest._EMPTY_DIGEST = hashlib.sha256(b'').hexdigest()
nemo_gym.data.tasks.harbor.manifest._NO_HARD_LINKS = {errno.EPERM, errno.ENOTSUP, errno.EOPNOTSUPP, errno.ENOSYS, errno.EACCES}
nemo_gym.data.tasks.harbor.manifest._SURROGATE_PAIR = re.compile('\\\\u(d[89ab][0-9a-f]{2})\\\\u(d[c-f][0-9a-f]{2})')
nemo_gym.data.tasks.harbor.manifest.logger = logging.getLogger(__name__)