Cion-lab commited on
Commit
292c9d2
·
verified ·
1 Parent(s): 2efe81e

publish_mix: resume listing must read lfs.oid as a dict (getattr on a dict returned None for every shard, so a 'resume' re-sent all 2.3 GB); hard-exit 11/12 when a batched commit leaves a path absent or a remote sha that disagrees with the local bytes

Browse files
Files changed (1) hide show
  1. build/publish_mix.py +63 -14
build/publish_mix.py CHANGED
@@ -117,6 +117,27 @@ def attribution(man):
117
  return ATTRIBUTION + "\n".join(lines) + "\n"
118
 
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  def main():
121
  ap = argparse.ArgumentParser()
122
  ap.add_argument("--root", default="/kaggle/working/mixroot")
@@ -235,30 +256,29 @@ def main():
235
  # because a bare upload_file has no retry and no place to resume from).
236
  remote_sha = {}
237
  try:
238
- for ent in api.list_repo_files(repo_id=a.repo, repo_type="dataset"):
239
- remote_sha[ent] = None
240
- for ent in api.dataset_info(a.repo, files_metadata=True).siblings or []:
241
- remote_sha[ent.rfilename] = getattr(ent, "lfs", None) and getattr(ent.lfs, "sha256", None) \
242
- or getattr(ent, "sha256", None)
243
  except Exception as e:
244
  print(f" (no resumable listing: {type(e).__name__}; uploading everything)", flush=True)
245
 
246
- failures = []
247
  todo = []
248
  for np, lp in to_upload:
 
249
  with open(lp, "rb") as fh:
250
- payload = fh.read()
251
- local_sha = hashlib.sha256(payload).hexdigest()
 
252
  if remote_sha.get(np) == local_sha:
253
  print(f" = {np} already on the Hub with this sha256, skipped", flush=True)
254
  continue
255
- todo.append((np, lp, local_sha, payload))
256
  print(f" {len(todo)} of {len(to_upload)} files still need uploading", flush=True)
257
 
258
  # ONE commit for the whole batch, not one commit per file. Gate 2f proved why: 139 sequential
259
  # `upload_file` commits succeeded and then the Hub began rejecting every request -- including 30-byte
260
  # files -- with a bare HfHubHTTPError, which is a commit-rate limit, not a network fault. A single
261
  # upload_folder call is one commit and handles LFS itself.
 
262
  if todo:
263
  try:
264
  api.upload_folder(repo_id=a.repo, repo_type="dataset", folder_path=a.root, path_in_repo="",
@@ -270,9 +290,11 @@ def main():
270
  except Exception as e:
271
  print(f" batched commit failed ({type(e).__name__}: {str(e)[:160]}); "
272
  "falling back to per-file uploads with retries", flush=True)
273
- for np, lp, local_sha, payload in todo:
274
  for attempt in (1, 2, 3):
275
  try:
 
 
276
  api.upload_file(path_or_fileobj=payload, path_in_repo=np, repo_id=a.repo,
277
  repo_type="dataset",
278
  commit_message=f"add {np} ({local_sha[:12]})")
@@ -290,11 +312,30 @@ def main():
290
  "resumes from the ones that did", flush=True)
291
  sys.exit(11)
292
 
293
- # Independent verification from the Hub, not from the client that just wrote it.
 
 
294
  remote = set(api.list_repo_files(repo_id=a.repo, repo_type="dataset"))
 
 
 
 
295
  want = {np for np, _ in to_upload} | {".gitattributes"}
 
 
 
 
 
 
 
 
 
 
296
  R = {"repo": a.repo, "files_expected": len(want), "files_present": len(want & remote),
297
- "missing": sorted(want - remote)}
 
 
 
298
  first = man["shards"][0]["file"]
299
  url = f"https://huggingface.co/datasets/{a.repo}/resolve/main/{first}"
300
  try:
@@ -307,11 +348,19 @@ def main():
307
  == man["shards"][0]["sha256"]}
308
  except Exception as e:
309
  R["anonymous_shard_read"] = {"error": f"{type(e).__name__}: {str(e)[:160]}"}
310
- R["published_verified"] = bool(not R["missing"] and R["anonymous_shard_read"].get(
311
- "sha256_matches_manifest"))
312
  print("PUBLISH_JSON_BEGIN")
313
  print(json.dumps(R, indent=1, default=str))
314
  print("PUBLISH_JSON_END")
 
 
 
 
 
 
 
 
315
 
316
 
317
  if __name__ == "__main__":
 
117
  return ATTRIBUTION + "\n".join(lines) + "\n"
118
 
119
 
120
+ def remote_digests(api, repo):
121
+ """path -> content sha256 for what is already on the Hub, so a re-run resumes instead of resending.
122
+
123
+ Only LFS-tracked files expose a content sha256 (`lfs.oid`, sha256 by construction). Small non-LFS
124
+ files expose only a git blob sha, which is not a content sha256, so they are reported unknown and
125
+ re-uploaded -- a handful of json/md/u8 files, which costs nothing. `lfs` arrives as a dict from
126
+ huggingface_hub, so attribute access alone silently returns None for every shard and turns a resume
127
+ into a full 2.3 GB re-upload (Gate 2f).
128
+ """
129
+ out = {}
130
+ for ent in api.dataset_info(repo, repo_type="dataset", files_metadata=True).siblings or []:
131
+ lfs = getattr(ent, "lfs", None)
132
+ if isinstance(lfs, dict):
133
+ out[ent.rfilename] = lfs.get("sha256") or lfs.get("oid")
134
+ elif lfs is not None:
135
+ out[ent.rfilename] = getattr(lfs, "sha256", None) or getattr(lfs, "oid", None)
136
+ else:
137
+ out[ent.rfilename] = None
138
+ return out
139
+
140
+
141
  def main():
142
  ap = argparse.ArgumentParser()
143
  ap.add_argument("--root", default="/kaggle/working/mixroot")
 
256
  # because a bare upload_file has no retry and no place to resume from).
257
  remote_sha = {}
258
  try:
259
+ remote_sha = remote_digests(api, a.repo)
260
+ print(f" {len(remote_sha)} file(s) already on the Hub", flush=True)
 
 
 
261
  except Exception as e:
262
  print(f" (no resumable listing: {type(e).__name__}; uploading everything)", flush=True)
263
 
 
264
  todo = []
265
  for np, lp in to_upload:
266
+ h = hashlib.sha256()
267
  with open(lp, "rb") as fh:
268
+ for chunk in iter(lambda: fh.read(1 << 20), b""):
269
+ h.update(chunk)
270
+ local_sha = h.hexdigest()
271
  if remote_sha.get(np) == local_sha:
272
  print(f" = {np} already on the Hub with this sha256, skipped", flush=True)
273
  continue
274
+ todo.append((np, lp, local_sha))
275
  print(f" {len(todo)} of {len(to_upload)} files still need uploading", flush=True)
276
 
277
  # ONE commit for the whole batch, not one commit per file. Gate 2f proved why: 139 sequential
278
  # `upload_file` commits succeeded and then the Hub began rejecting every request -- including 30-byte
279
  # files -- with a bare HfHubHTTPError, which is a commit-rate limit, not a network fault. A single
280
  # upload_folder call is one commit and handles LFS itself.
281
+ failures = []
282
  if todo:
283
  try:
284
  api.upload_folder(repo_id=a.repo, repo_type="dataset", folder_path=a.root, path_in_repo="",
 
290
  except Exception as e:
291
  print(f" batched commit failed ({type(e).__name__}: {str(e)[:160]}); "
292
  "falling back to per-file uploads with retries", flush=True)
293
+ for np, lp, local_sha in todo:
294
  for attempt in (1, 2, 3):
295
  try:
296
+ with open(lp, "rb") as fh:
297
+ payload = fh.read()
298
  api.upload_file(path_or_fileobj=payload, path_in_repo=np, repo_id=a.repo,
299
  repo_type="dataset",
300
  commit_message=f"add {np} ({local_sha[:12]})")
 
312
  "resumes from the ones that did", flush=True)
313
  sys.exit(11)
314
 
315
+ # Independent verification from the Hub, not from the client that just wrote it. A batched commit can
316
+ # return without raising and still not carry every file, so presence is checked per path and the sha
317
+ # is re-required wherever the Hub exposes one (LFS).
318
  remote = set(api.list_repo_files(repo_id=a.repo, repo_type="dataset"))
319
+ try:
320
+ after = remote_digests(api, a.repo)
321
+ except Exception:
322
+ after = {}
323
  want = {np for np, _ in to_upload} | {".gitattributes"}
324
+ local_by_path = {np: lp for np, lp in to_upload}
325
+ sha_bad = []
326
+ for np, rsha in after.items():
327
+ if rsha and np in local_by_path:
328
+ h = hashlib.sha256()
329
+ with open(local_by_path[np], "rb") as fh:
330
+ for chunk in iter(lambda: fh.read(1 << 20), b""):
331
+ h.update(chunk)
332
+ if h.hexdigest() != rsha:
333
+ sha_bad.append(np)
334
  R = {"repo": a.repo, "files_expected": len(want), "files_present": len(want & remote),
335
+ "missing": sorted(want - remote), "sha_mismatch_on_hub": sorted(sha_bad)[:20],
336
+ "sha_verified_files": sum(1 for v in after.values() if v)}
337
+ print("after upload: %d/%d present, %d missing, %d sha mismatch" % (
338
+ R["files_present"], R["files_expected"], len(R["missing"]), len(sha_bad)), flush=True)
339
  first = man["shards"][0]["file"]
340
  url = f"https://huggingface.co/datasets/{a.repo}/resolve/main/{first}"
341
  try:
 
348
  == man["shards"][0]["sha256"]}
349
  except Exception as e:
350
  R["anonymous_shard_read"] = {"error": f"{type(e).__name__}: {str(e)[:160]}"}
351
+ R["published_verified"] = bool(not R["missing"] and not R["sha_mismatch_on_hub"]
352
+ and R["anonymous_shard_read"].get("sha256_matches_manifest"))
353
  print("PUBLISH_JSON_BEGIN")
354
  print(json.dumps(R, indent=1, default=str))
355
  print("PUBLISH_JSON_END")
356
+ if not R["missing"] and R["sha_mismatch_on_hub"]:
357
+ print(f"FATAL: {len(R['sha_mismatch_on_hub'])} file(s) on the Hub do not match the local bytes",
358
+ flush=True)
359
+ sys.exit(12)
360
+ if R["missing"]:
361
+ print(f"FATAL: {len(R['missing'])} file(s) still absent after the commit -- re-running "
362
+ "publish_mix resumes from the ones that landed", flush=True)
363
+ sys.exit(11)
364
 
365
 
366
  if __name__ == "__main__":