Spaces:
Sleeping
Sleeping
File size: 6,495 Bytes
c380737 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | #!/usr/bin/env python
"""One-time data-prep: build + host the precomputed Example datasets.
For each example defined in ``src/examples.py`` this:
1. runs the real index scan (``src.index_query`` in cdn mode) with the example's
SQL filter + record cap, writing a capped ``ranges.csv`` locally;
2. runs the real ``cdxt repackage`` (same local real-fetch path as
``scripts/verify_real.sh``) to produce the output WARC(s) locally;
3. uploads the WARC(s) + ranges.csv to the PUBLIC examples bucket;
4. prints the confirmed ``n_records`` / ``total_bytes`` / ``scan_files`` / filenames
to paste back into ``EXAMPLES`` in ``src/examples.py``.
Needs ``HF_TOKEN`` (bucket read of the CC index + write to the examples bucket) and a
positive-balance HF account is NOT required (no HF Jobs — everything runs locally).
Usage:
HF_TOKEN=... scripts/build_examples.py [--bucket ns/bucket] [--only key,key] [--keep-private]
"""
from __future__ import annotations
import argparse
import glob
import io
import os
import re
import subprocess
import sys
from contextlib import redirect_stdout
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src import config, examples # noqa: E402
CDXT = ".venv/bin/cdxt"
CDXT_SPEC = (
"cdx_toolkit[hf] @ git+https://github.com/commoncrawl/cdx_toolkit.git@feat/warc-range-sources"
)
# Build-time only: cap the parquet files scanned for the broad no-domain examples so the
# build takes minutes, not hours (PDFs / homepages are common enough in any few files).
BUILD_MAX_FILES = {"pdfs": 8, "homepages": 4}
_ESTIMATE_RE = re.compile(r"ESTIMATE\s+n_records=(\d+)\s+total_bytes=(\d+)")
_SCAN_RE = re.compile(r"\[index\]\s+(\d+)\s+parquet file\(s\) to scan")
def _run_index(ex, ranges_csv: str) -> tuple[int, int, int, str]:
"""Run the real index scan for one example; return (n_records, total_bytes, scan_files, log)."""
from src import index_query
env = {
"CC_INDEX_MODE": "cdn",
"CC_CRAWLS": ",".join(ex.crawls),
"CC_HOSTNAMES": ",".join(ex.hostnames),
"CC_DOMAINS": ",".join(ex.domains),
"CC_LANGUAGES": ",".join(ex.languages),
"CC_SQL_WHERE": ex.sql_where,
"CC_MAX_RECORDS": str(ex.max_records or 0),
"CC_MAX_FILES": str(BUILD_MAX_FILES.get(ex.key, 0)),
"CC_RANGES_OUT": ranges_csv,
}
old = {k: os.environ.get(k) for k in env}
os.environ.update(env)
buf = io.StringIO()
try:
with redirect_stdout(buf):
rc = index_query.main()
finally:
for k, v in old.items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
log = buf.getvalue()
print(log)
if rc != 0:
raise SystemExit(f"index scan failed for {ex.key} (rc={rc})")
m = _ESTIMATE_RE.search(log)
n_records, total_bytes = (int(m.group(1)), int(m.group(2))) if m else (0, 0)
ms = _SCAN_RE.search(log)
scan_files = int(ms.group(1)) if ms else 0
return n_records, total_bytes, scan_files, log
def _bootstrap_cdxt() -> None:
if not os.access(CDXT, os.X_OK):
print("== installing cdx_toolkit for the fetch step ==")
subprocess.run(
["uv", "pip", "install", "--python", ".venv", CDXT_SPEC, "uvloop"], check=True
)
def _run_fetch(ex, ranges_csv: str, out_dir: str) -> list[str]:
"""Run the real cdxt repackage locally; return the produced WARC file paths."""
_bootstrap_cdxt()
out_prefix = os.path.join(out_dir, ex.name)
cmd = [
CDXT, "-v", "repackage",
"--target-source", "csv", "--csv-path", ranges_csv,
"--warc-download-prefix", f"hf://buckets/{config.CC_BUCKET}", "--hf-reader", "cdn",
"--prefix", out_prefix,
"--processes", "1", "--parallel_readers", str(config.DEFAULT_PARALLEL_READERS),
"--size", str(config.WARC_TARGET_SIZE),
]
env = dict(os.environ, CDXT_UVLOOP="1")
subprocess.run(cmd, check=True, env=env)
return sorted(glob.glob(f"{out_prefix}*.warc.gz"))
def _upload(api, bucket: str, ex, warcs: list[str], ranges_csv: str) -> list[str]:
add = [(w, f"{ex.path}/{os.path.basename(w)}") for w in warcs]
add.append((ranges_csv, f"{ex.path}/{config.RANGES_CSV_NAME}"))
api.batch_bucket_files(bucket, add=add, token=os.environ["HF_TOKEN"])
return [os.path.basename(w) for w in warcs]
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--bucket", default=examples.EXAMPLES_BUCKET)
ap.add_argument("--only", default="", help="comma-separated example keys to build")
ap.add_argument("--keep-private", action="store_true", help="do NOT make the bucket public")
ap.add_argument("--out-dir", default="/tmp/cc-examples-build")
args = ap.parse_args()
token = os.environ.get("HF_TOKEN")
if not token:
raise SystemExit("set HF_TOKEN")
from huggingface_hub import HfApi
api = HfApi()
api.create_bucket(args.bucket, private=args.keep_private, exist_ok=True, token=token)
print(f"== examples bucket: {args.bucket} (public={not args.keep_private}) ==")
only = {k for k in args.only.split(",") if k}
todo = [ex for ex in examples.EXAMPLES if not only or ex.key in only]
os.makedirs(args.out_dir, exist_ok=True)
results = {}
for ex in todo:
print(f"\n===== building example: {ex.key} =====")
ranges_csv = os.path.join(args.out_dir, f"{ex.key}-ranges.csv")
ex_out = os.path.join(args.out_dir, ex.key)
os.makedirs(ex_out, exist_ok=True)
n_records, total_bytes, scan_files, _ = _run_index(ex, ranges_csv)
if n_records == 0:
print(f"WARNING: {ex.key} matched 0 records — skipping fetch/upload")
continue
warcs = _run_fetch(ex, ranges_csv, ex_out)
files = _upload(api, args.bucket, ex, warcs, ranges_csv)
results[ex.key] = dict(
n_records=n_records, total_bytes=total_bytes, scan_files=scan_files, files=files
)
print(f" -> {ex.key}: n_records={n_records} total_bytes={total_bytes} "
f"scan_files={scan_files} files={files}")
print("\n===== paste into src/examples.py (per example) =====")
for key, r in results.items():
print(f"# {key}: n_records={r['n_records']}, total_bytes={r['total_bytes']}, "
f"scan_files={r['scan_files']}, files={r['files']}")
return 0
if __name__ == "__main__":
sys.exit(main())
|