Files
anonymisation/anonymisation_cli_onefile.spec

188 lines
4.9 KiB
Python

import os
from pathlib import Path
from PyInstaller.utils.hooks import collect_all, copy_metadata
# Spec CLI frozen — EXE de PRODUCTION (anonymisation fichier unique sans GUI).
# Même moteur / mêmes datas que anonymisation_onefile.spec, mais :
# - entrypoint = scripts/anonymize_cli.py (CLI production, pas launcher.py)
# Contrat : Anonymisation-CLI.exe <fichier> <dossier_sortie>
# Modèle CamemBERT-bio ONNX OBLIGATOIRE (fail-closed, code 3 si absent).
# - console=True (CLI), pas de Splash
# - name = Anonymisation-CLI -> ne remplace pas dist/Anonymisation.exe
# (Le harnais perf D-19 reste scripts/anonymize_batch_cli.py, non buildé ici.)
block_cipher = None
project_dir = Path(globals().get("SPECPATH", os.getcwd())).resolve()
def _data_entry(relative_path: str, target_dir: str | None = None):
src = project_dir / relative_path
if not src.exists():
return None
return (str(src), target_dir or relative_path)
binaries = []
datas = []
for relative_path, target_dir in [
("config", "config"),
("data/bdpm", "data/bdpm"),
("data/edsnlp", "data/edsnlp"),
("data/finess", "data/finess"),
("data/insee", "data/insee"),
("models/camembert-bio-deid/onnx", "models/camembert-bio-deid/onnx"),
("detectors", "detectors"),
("scripts", "scripts"),
("assets", "assets"),
]:
entry = _data_entry(relative_path, target_dir)
if entry is not None:
datas.append(entry)
for relative_path in [
"data/stopwords_manuels.txt",
"data/villes_blacklist.txt",
"data/dpi_labels_blacklist.txt",
"data/companion_blacklist.txt",
]:
entry = _data_entry(relative_path, "data")
if entry is not None:
datas.append(entry)
onnxtr_cache_dir = Path(os.environ.get("ONNXTR_CACHE_DIR", Path.home() / ".cache" / "onnxtr"))
required_onnxtr_weights = [
"db_resnet50-69ba0015.onnx",
"crnn_vgg16_bn-743599aa.onnx",
]
missing_onnxtr_weights = []
for filename in required_onnxtr_weights:
src = onnxtr_cache_dir / "models" / filename
if src.exists():
datas.append((str(src), "models/onnxtr/models"))
else:
missing_onnxtr_weights.append(str(src))
if missing_onnxtr_weights:
raise FileNotFoundError(
"Poids OCR OnnxTR manquants pour le build frozen : "
+ ", ".join(missing_onnxtr_weights)
+ ". Précharger OnnxTR (lancer une OCR une fois) ou définir ONNXTR_CACHE_DIR avant PyInstaller."
)
hiddenimports = [
"anonymizer_core_refactored_onnx",
"admin_rules",
"config_defaults",
"profile_defaults",
"gui_batch_paths",
"manual_masking",
"pdf_mask_designer",
"format_converter",
"ner_manager_onnx",
"camembert_ner_manager",
"eds_pseudo_manager",
"gliner_manager",
"vlm_manager",
"build_info",
# OCR OnnxTR (ONNX Runtime, remplace docTR — sans torch ni doctr)
"onnxtr",
"onnxtr.io",
"onnxtr.models",
"onnxtr.models.detection",
"onnxtr.models.recognition",
"onnxtr.utils",
"onnxtr.utils.data",
# Dépendances transitives OnnxTR (hiddenimports défensifs vs omission PyInstaller)
"pyclipper",
"scipy.cluster.hierarchy",
"scipy.special",
"cv2",
"edsnlp",
"edsnlp.pipes",
"edsnlp.pipes.ner",
"edsnlp.pipes.ner.pseudo",
"spacy",
"spacy.lang.fr",
"gliner",
"onnxruntime",
"transformers",
"tokenizers",
"pdfplumber",
"fitz",
"PIL",
"yaml",
"loguru",
"regex",
]
def _collect_optional_package(package_name: str):
try:
package_datas, package_binaries, package_hiddenimports = collect_all(package_name)
datas.extend(package_datas)
binaries.extend(package_binaries)
hiddenimports.extend(package_hiddenimports)
try:
datas.extend(copy_metadata(package_name, recursive=True))
except Exception:
pass
except Exception:
pass
for _package_name in [
"edsnlp",
"spacy",
"thinc",
"blis",
"srsly",
"catalogue",
"confection",
"cymem",
"preshed",
"murmurhash",
"gliner",
"loguru",
]:
_collect_optional_package(_package_name)
# P0-3 (Plan 3) : exclusion dure de la pile torch. Le core fait un
# `import torch` lazy (try/except no-op) dans _configure_torch_threads que
# l'analyse statique suivrait ; en frozen l'ImportError est attendue et gérée.
EXCLUDED_TORCH_STACK = [
"torch",
"torchvision",
"optimum",
"doctr",
]
a = Analysis(
[str(project_dir / "scripts" / "anonymize_cli.py")],
pathex=[str(project_dir)],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
excludes=EXCLUDED_TORCH_STACK,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name="Anonymisation-CLI",
debug=False,
strip=False,
upx=False,
console=True,
)