File size: 2,043 Bytes
551b309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Build hook that ships an integrity manifest inside the wheel.

The manifest hashes every packaged file. At runtime the package recomputes those
hashes and refuses to run if any differ. The manifest is written to a temporary
path and force-included, so the source tree stays clean and editable installs
remain unenforced.
"""

# Credit: @paparichens

from __future__ import annotations

import hashlib
import json
import tempfile
from pathlib import Path
from typing import Any

from hatchling.builders.hooks.plugin.interface import BuildHookInterface

PACKAGE = "affix_huggingface"
MANIFEST_NAME = "_integrity_manifest.json"
ALGORITHM = "sha256"
_SKIP_SUFFIXES = (".pyc", ".pyo")
_SKIP_DIRS = {"__pycache__"}


def _hash_file(path: Path) -> str:
    digest = hashlib.new(ALGORITHM)
    with path.open("rb") as handle:
        for chunk in iter(lambda: handle.read(65536), b""):
            digest.update(chunk)
    return digest.hexdigest()


class CustomBuildHook(BuildHookInterface):
    PLUGIN_NAME = "custom"

    def initialize(self, version: str, build_data: dict[str, Any]) -> None:
        if self.target_name != "wheel":
            return

        root = Path(self.root) / "src" / PACKAGE
        files: dict[str, str] = {}
        for path in sorted(root.rglob("*")):
            if not path.is_file():
                continue
            rel = path.relative_to(root)
            if any(part in _SKIP_DIRS for part in rel.parts):
                continue
            if path.suffix in _SKIP_SUFFIXES:
                continue
            if path.name == MANIFEST_NAME:
                continue
            files[rel.as_posix()] = _hash_file(path)

        manifest = {"version": 1, "algorithm": ALGORITHM, "files": files}
        tmp = Path(tempfile.mkdtemp(prefix="affix-manifest-")) / MANIFEST_NAME
        tmp.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")

        force_include = build_data.setdefault("force_include", {})
        force_include[str(tmp)] = f"{PACKAGE}/{MANIFEST_NAME}"