Files
AnyImage/prepare_vendor.py
nan 2e8af26cdf feat: Add GitHub Actions workflow for building and testing the Depth Anything 3 extension
- Create a build workflow in `.github/workflows/build.yml` to automate the build process.
- Configure Python environment and install dependencies.
- Add steps for running tests and packaging the extension.

chore: Initialize project metadata and dependencies for Depth Anything 3

- Add `pyproject.toml` for project configuration and dependencies.
- Include optional dependencies for app and gs functionalities.
- Define project scripts and metadata.

chore: Add requirements file for Depth Anything 3 dependencies

- Create `requirements.txt` listing all necessary packages for the project.

feat: Implement export utility functions for Depth Anything 3

- Create `src/depth_anything_3/utils/export/__init__.py` to handle various export formats.
- Implement functions for exporting predictions in different formats.

feat: Add ray utility functions for homography estimation

- Create `src/depth_anything_3/utils/ray_utils.py` with functions for computing optimal rotation and homography using RANSAC.

feat: Implement vendor preparation script

- Add `prepare_vendor.py` to manage third-party dependencies and artifacts.
- Include functions for downloading and verifying artifacts based on a lock file.

test: Add unit tests for vendor preparation script

- Create `tests/test_prepare_vendor.py` to test artifact downloading and verification.
- Implement tests for handling hash mismatches and unsupported platforms.

chore: Add vendor lock file for dependency management

- Create `vendor.lock.toml` to define artifacts and platforms for the project.
2026-08-09 11:44:24 +08:00

112 lines
3.5 KiB
Python

import argparse
import hashlib
import sys
import tomllib
import urllib.request
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent
LOCK_PATH = PROJECT_ROOT / "vendor.lock.toml"
VENDOR_DIR = PROJECT_ROOT / "vendor"
CHUNK_SIZE = 1024 * 1024
def load_lock(path=LOCK_PATH):
"""Load and validate the vendor lock file."""
with Path(path).open("rb") as stream:
lock = tomllib.load(stream)
if lock.get("schema_version") != 1:
raise RuntimeError("Unsupported vendor.lock.toml schema")
return lock
def file_sha256(path):
"""Return the SHA-256 digest for a file."""
digest = hashlib.sha256()
with Path(path).open("rb") as stream:
while chunk := stream.read(CHUNK_SIZE):
digest.update(chunk)
return digest.hexdigest()
def download_artifact(artifact, destination_dir=VENDOR_DIR):
"""Download one locked artifact and verify its content."""
destination_dir = Path(destination_dir)
destination = destination_dir / artifact["filename"]
expected = artifact["sha256"]
if destination.is_file() and file_sha256(destination) == expected:
print(f"Ready: {destination.name}")
return destination
destination_dir.mkdir(parents=True, exist_ok=True)
temporary = destination.with_name(f"{destination.name}.part")
temporary.unlink(missing_ok=True)
print(f"Downloading: {destination.name}")
request = urllib.request.Request(
artifact["url"],
headers={"User-Agent": "SnapForm vendor preparation"},
)
try:
with urllib.request.urlopen(request) as response, temporary.open("wb") as output:
while chunk := response.read(CHUNK_SIZE):
output.write(chunk)
actual = file_sha256(temporary)
if actual != expected:
raise RuntimeError(
f"SHA-256 mismatch for {destination.name}: "
f"expected {expected}, got {actual}"
)
temporary.replace(destination)
except Exception:
temporary.unlink(missing_ok=True)
raise
return destination
def prepare(platforms=None, lock_path=LOCK_PATH, destination_dir=VENDOR_DIR):
"""Prepare common third-party archives for selected platforms."""
lock = load_lock(lock_path)
available = lock["platforms"]
selected = list(available) if platforms is None else platforms
unknown = sorted(set(selected) - set(available))
if unknown:
raise ValueError(f"Unsupported platform: {', '.join(unknown)}")
artifacts = list(lock["artifacts"].values())
artifacts.extend(available[platform] for platform in selected)
return [
download_artifact(artifact, destination_dir)
for artifact in artifacts
]
def parse_args(argv=None):
"""Parse command-line options."""
parser = argparse.ArgumentParser(
description="Download and verify third-party files for SnapForm.",
)
parser.add_argument(
"--platform",
action="append",
dest="platforms",
help="Prepare one platform; repeat as needed. Default: all platforms.",
)
return parser.parse_args(argv)
def main(argv=None):
"""Run vendor preparation from the command line."""
args = parse_args(argv)
try:
paths = prepare(args.platforms)
except (OSError, RuntimeError, ValueError) as error:
print(f"Preparation failed: {error}", file=sys.stderr)
return 1
print(f"Prepared {len(paths)} files")
return 0
if __name__ == "__main__":
raise SystemExit(main())