| |
| """ |
| Replace symlinks under datasets/humanoid/singleview with real file copies. |
| |
| datasets/humanoid/multiview currently has no symlinks; this script only touches |
| singleview by default. |
| |
| Run from repo root: |
| python scripts/materialize_humanoid_symlinks.py [--dry-run] |
| """ |
| from __future__ import annotations |
|
|
| import argparse |
| import os |
| import shutil |
| import sys |
| from pathlib import Path |
|
|
|
|
| def collect_file_symlinks(base: Path) -> list[Path]: |
| out: list[Path] = [] |
| for root, dirs, files in os.walk(base, followlinks=False): |
| for name in list(dirs): |
| p = Path(root) / name |
| if p.is_symlink(): |
| dirs.remove(name) |
| for name in files: |
| p = Path(root) / name |
| if p.is_symlink(): |
| out.append(p) |
| return out |
|
|
|
|
| def iter_dir_symlinks(base: Path) -> list[Path]: |
| out: list[Path] = [] |
| for root, dirs, _files in os.walk(base, followlinks=False): |
| for name in list(dirs): |
| p = Path(root) / name |
| if p.is_symlink(): |
| out.append(p) |
| dirs.remove(name) |
| return out |
|
|
|
|
| def materialize_files(paths: list[Path], dry_run: bool) -> int: |
| err = 0 |
| for i, p in enumerate(sorted(paths, key=str), start=1): |
| if not p.is_symlink(): |
| continue |
| tgt = p.resolve() |
| if not tgt.exists(): |
| print(f"Missing target for {p}: {tgt}", file=sys.stderr) |
| err += 1 |
| continue |
| if not tgt.is_file(): |
| print(f"Expected file target for {p}, got {tgt}", file=sys.stderr) |
| err += 1 |
| continue |
| if dry_run: |
| if i <= 5 or i == len(paths): |
| print(f"[dry-run] copy file {tgt} -> {p}") |
| continue |
| os.unlink(p) |
| shutil.copy2(tgt, p) |
| if i % 50 == 0 or i == len(paths): |
| print(f"files: {i}/{len(paths)}", flush=True) |
| return err |
|
|
|
|
| def materialize_dir(p: Path, dry_run: bool) -> int: |
| if not p.is_symlink(): |
| return 0 |
| tgt = p.resolve() |
| if not tgt.is_dir(): |
| print(f"Expected directory target for {p}, got {tgt}", file=sys.stderr) |
| return 1 |
| if dry_run: |
| print(f"[dry-run] copytree {tgt} -> {p}") |
| return 0 |
| os.unlink(p) |
| shutil.copytree( |
| tgt, |
| p, |
| symlinks=False, |
| copy_function=shutil.copy2, |
| dirs_exist_ok=False, |
| ) |
| return 0 |
|
|
|
|
| def main() -> int: |
| ap = argparse.ArgumentParser() |
| ap.add_argument( |
| "--root", |
| type=Path, |
| default=Path(__file__).resolve().parents[1] |
| / "datasets" |
| / "humanoid" |
| / "singleview", |
| help="humanoid singleview dataset root", |
| ) |
| ap.add_argument("--dry-run", action="store_true") |
| args = ap.parse_args() |
| base: Path = args.root.resolve() |
| if not base.is_dir(): |
| print(f"Not a directory: {base}", file=sys.stderr) |
| return 1 |
|
|
| file_links = [p for p in collect_file_symlinks(base) if p.is_symlink()] |
| dir_links = [p for p in iter_dir_symlinks(base) if p.is_symlink()] |
|
|
| if args.dry_run: |
| print(f"[dry-run] file symlinks: {len(file_links)}") |
| print(f"[dry-run] directory symlinks: {len(dir_links)}") |
|
|
| err = materialize_files(file_links, args.dry_run) |
| for d in sorted(dir_links, key=str): |
| err += materialize_dir(d, args.dry_run) |
|
|
| if err: |
| return 1 |
| if not args.dry_run: |
| leftover = collect_file_symlinks(base) + [ |
| p for p in iter_dir_symlinks(base) if p.is_symlink() |
| ] |
| if leftover: |
| print(f"Warning: {len(leftover)} symlink(s) remain", file=sys.stderr) |
| return 1 |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|