"""Copy bundled non-code files into the Nuitka wheel tree.""" from __future__ import annotations import shutil from pathlib import Path # Required files that must ship with the wheel for ORY-FINGER runtime/GUI. REQUIRED_ASSET_RELPATHS = ( "finger/assets/30fps-0703/mesh.npz", "finger/assets/30fps-0703/camera_intrinsics.npz", "finger/assets/30fps-0703/world_to_cam.npy", "finger/assets/visualization/world_to_cam.npy", ) def resolve_bundle_source_files(repo_root: Path, item: dict) -> list[Path]: """Resolve explicit files and/or glob patterns for one bundle item.""" src_dir = repo_root / item["source"] patterns = item.get("patterns", []) files = item.get("files", []) if not patterns and not files: raise ValueError(f"Bundle item {item['name']!r} requires patterns and/or files") seen: set[Path] = set() resolved: list[Path] = [] def add(path: Path) -> None: key = path.resolve() if key not in seen: seen.add(key) resolved.append(path) for pattern in patterns: for path in sorted(src_dir.glob(pattern)): if path.is_file(): add(path) for name in files: path = src_dir / name if not path.is_file(): raise FileNotFoundError(f"Bundle file not found for {item['name']}: {path}") add(path) return resolved def verify_required_assets(repo_root: Path, items: list[dict]) -> None: """Fail early when required finger assets are missing from the bundle.""" asset_items = [item for item in items if item.get("name") == "sensors"] if not asset_items: return resolved: set[str] = set() for item in asset_items: src_dir = (repo_root / item["source"]).resolve() for path in resolve_bundle_source_files(repo_root, item): try: rel = path.resolve().relative_to(src_dir).as_posix() except ValueError: continue resolved.add(rel) missing = [rel for rel in REQUIRED_ASSET_RELPATHS if rel not in resolved] if missing: joined = ", ".join(missing) raise FileNotFoundError( "Required finger assets are missing from the wheel bundle. " f"Missing: {joined}. Update [[tool.orisys.bundle.items]] patterns " "or restore assets under sensors/finger/assets/ before building." ) def package_data_paths_for_item(item: dict) -> list[str]: """Return setuptools package_data paths relative to the orisys package.""" target = str(item["target"]).replace("\\", "/") entries = [f"{target}/{pattern}" for pattern in item.get("patterns", [])] entries.extend(f"{target}/{name}" for name in item.get("files", [])) return entries def data_file_entries_for_item(item: dict) -> list[str]: """Return setuptools data_files paths relative to the repo root.""" source = str(item["source"]).replace("\\", "/") entries = [f"{source}/{pattern}" for pattern in item.get("patterns", [])] entries.extend(f"{source}/{name}" for name in item.get("files", [])) return entries def copy_bundle_items(repo_root: Path, pkg_dir: Path, items: list[dict]) -> dict[str, int]: verify_required_assets(repo_root, items) copied_counts: dict[str, int] = {} for item in items: name = item["name"] src_dir = repo_root / item["source"] dst_dir = pkg_dir / item["target"] dst_dir.mkdir(parents=True, exist_ok=True) copied = 0 for src_file in resolve_bundle_source_files(repo_root, item): if item.get("preserve_tree", False): dst_file = dst_dir / src_file.relative_to(src_dir) dst_file.parent.mkdir(parents=True, exist_ok=True) else: dst_file = dst_dir / src_file.name shutil.copy(src_file, dst_file) copied += 1 print(f"Copied {name}: {src_file}") if name == "examples": (dst_dir / "__init__.py").write_text("# Examples for orisys package\n", encoding="utf-8") copied_counts[name] = copied return copied_counts def package_data_from_bundle(ext_suffix: str, items: list[dict]) -> dict[str, list[str]]: package_data: dict[str, list[str]] = { "orisys": [f"*{ext_suffix}"], "orisys.examples": ["*.py", "README.md", "viz/**/*.py", "pipeline/**/*.py", "gui/**/*.py"], "orisys_blend": ["*.py"], } for item in items: if item["name"] == "examples": continue package_data["orisys"].extend(package_data_paths_for_item(item)) return package_data def copy_orisys_blend(repo_root: Path, wheel_root: Path) -> int: """Copy pure-Python orisys_blend (Numba kernels) next to the Nuitka orisys .pyd.""" src = repo_root / "src" / "orisys_blend" dst = wheel_root / "orisys_blend" if dst.exists(): shutil.rmtree(dst) shutil.copytree(src, dst) count = sum(1 for p in dst.rglob("*.py") if p.is_file()) print(f"Copied orisys_blend ({count} .py files) to {dst}") return count