Anuvani-Diarization / src /tools /licence_headers.py
StephennFernandes's picture
Anuvani-Diarization 1.0.0
63dd946
Raw History Blame Contribute Delete
5.81 kB
# Copyright (c) 2026 The Anuvani-Diarization authors
# SPDX-License-Identifier: MIT OR Apache-2.0
"""Add the copyright and SPDX licence lines to every source file of the release (idempotent).
python3 tools/licence_headers.py [--check]
Covers the git-tracked .py, .rs and .sh files, Dockerfiles, Makefiles and the `diar` command under
release/anuvani_diar_rs, release/anuvani_diar_v1 and release/hub. Our files are MIT OR
Apache-2.0. Files copied from other projects keep those projects' licences: verbatim copies are left as they are
(their own headers say where they come from); copies with small changes get their upstream licence; files that
port another project's code say so in a third line (THIRD_PARTY_NOTICES.md has the notices and the licence texts).
A shebang or a coding line stays first. With --check, lists the files without a header and exits 1 if any.
"""
import re
import subprocess
import sys
from pathlib import Path
RELEASE = Path(__file__).resolve().parents[2]
TREES = ("anuvani_diar_rs", "anuvani_diar_v1", "hub")
OURS = "Copyright (c) 2026 The Anuvani-Diarization authors"
SPDX = "SPDX-License-Identifier: MIT OR Apache-2.0"
VERBATIM = re.compile(r"(nn/seg|hf_port_poc/anuvani_diar_net)/(wav2vec2/.*|conformer\.py|wavlm_config\.py)$")
# file suffix -> (copyright line, SPDX line)
UPSTREAM = {
"python/anuvani_diar/nn/kaldi.py": ("Copyright (c) 2017 Facebook Inc. (Soumith Chintala); copied from torchaudio 2.1.1",
"SPDX-License-Identifier: BSD-2-Clause"),
"python/anuvani_diar/nn/resample.py": ("Copyright (c) 2017 Facebook Inc. (Soumith Chintala); copied from torchaudio 2.1.1",
"SPDX-License-Identifier: BSD-2-Clause"),
"python/anuvani_diar/nn/voiceprint.py": ("Copyright (c) 2020 CNRS (pyannote.audio), (c) 2021 Shuai Wang (WeSpeaker); "
"changes (c) 2026 The Anuvani-Diarization authors",
"SPDX-License-Identifier: MIT AND Apache-2.0"),
"nn/seg/segnet.py": ("Copyright (c) 2024 BUT Speech@FIT (DiariZen); changes (c) 2026 The Anuvani-Diarization authors",
"SPDX-License-Identifier: MIT"),
"hf_port_poc/anuvani_diar_net/segnet.py": ("Copyright (c) 2024 BUT Speech@FIT (DiariZen); changes (c) 2026 The "
"Anuvani-Diarization authors", "SPDX-License-Identifier: MIT"),
"crates/diar-core/src/vbx.rs": (OURS, "SPDX-License-Identifier: (MIT OR Apache-2.0) AND Apache-2.0"),
}
# file suffix -> the third line: what it ports
PORTS = {
"crates/diar-core/src/vbx.rs": "Ports DiariZen's VBx (diarizen/clustering/VBx.py, Apache-2.0, after BUT Speech@FIT's VBx).",
"crates/diar-exact/src/hierarchy.rs": "Ports SciPy 1.15.3's hierarchical clustering (BSD-3-Clause).",
"crates/diar-exact/src/assign.rs": "Ports SciPy 1.15.3's cdist cosine and linear_sum_assignment (BSD-3-Clause).",
"crates/diar-exact/src/special.rs": "Ports SciPy 1.15.3's softmax and logsumexp (BSD-3-Clause).",
"crates/diar-exact/src/sort.rs": "Ports NumPy 1.26.4's argsort (BSD-3-Clause).",
"crates/diar-exact/src/unique.rs": "Ports NumPy 1.26.4's unique (BSD-3-Clause).",
"crates/diar-exact/src/linalg.rs": "Ports NumPy 1.26.4's linalg.norm (BSD-3-Clause).",
"crates/diar-exact/src/npblas.rs": "Ports NumPy 1.26.4's BLAS call sites (BSD-3-Clause).",
"crates/diar-exact/src/pairwise.rs": "Ports NumPy 1.26.4's pairwise summation and reductions (BSD-3-Clause).",
"crates/diar-core/src/recon.rs": "Ports pyannote.audio 3.1.1 and pyannote.core 5.0.0 (MIT, CNRS).",
"crates/diar-core/src/front.rs": "Ports pyannote.audio 3.1.1's sliding-window inference and masks (MIT, CNRS).",
"crates/diar-audio/src/sndfile.rs": "Reproduces python-soundfile 0.14.0's libsndfile calls (BSD-3-Clause).",
}
NOTE = "Notices and licence texts: THIRD_PARTY_NOTICES.md."
def files():
out = []
for tree in TREES:
root = RELEASE / tree
listed = subprocess.run(["git", "ls-files", "-z", "."], cwd=root, capture_output=True, check=True).stdout
for rel in listed.decode().split("\0"):
p = root / rel
if rel and p.is_file() and (p.suffix in (".py", ".rs", ".sh") or p.name in ("Dockerfile", "Makefile", "diar")):
out.append(p)
return sorted(out)
def header_for(p):
rel = str(p.relative_to(RELEASE))
if VERBATIM.search(rel):
return None
mark = "//" if p.suffix == ".rs" else "#"
copyright_line, spdx = OURS, SPDX
for suffix, (c, s) in UPSTREAM.items():
if rel.endswith(suffix):
copyright_line, spdx = c, s
lines = [f"{mark} {copyright_line}", f"{mark} {spdx}"]
for suffix, port in PORTS.items():
if rel.endswith(suffix):
lines.append(f"{mark} {port} {NOTE}")
return lines
def main():
check = "--check" in sys.argv
missing, added = [], 0
for p in files():
lines = header_for(p)
if lines is None:
continue
text = p.read_text()
if "SPDX-License-Identifier:" in "\n".join(text.split("\n")[:6]):
continue
if check:
missing.append(str(p.relative_to(RELEASE)))
continue
body = text.split("\n")
keep = 0
while keep < len(body) and keep < 2 and (body[keep].startswith("#!") or re.match(r"#.*coding[:=]", body[keep])):
keep += 1
p.write_text("\n".join(body[:keep] + lines + body[keep:]))
added += 1
if check:
print("\n".join(missing) or "every source file has a licence header")
return 1 if missing else 0
print(f"headers added to {added} files")
return 0
if __name__ == "__main__":
sys.exit(main())