Source code for apem.unit_based_model.evaluation.output_dir
"""Helpers for creating evaluation output directories with Windows-safe name lengths."""
from __future__ import annotations
from datetime import datetime, timezone
import hashlib
from pathlib import Path
import re
DEFAULT_OUTPUT_DIR_NAME_MAX_LENGTH = 64
_NON_ALNUM_PATTERN = re.compile(r"[^A-Za-z0-9_.-]+")
[docs]
def create_timestamped_output_dir(
evaluation_root: Path,
*name_parts: str,
max_dir_name_length: int = DEFAULT_OUTPUT_DIR_NAME_MAX_LENGTH,
) -> Path:
"""
Create a timestamped output directory with bounded folder-name length.
On Windows, long absolute paths can fail around the default 260-character
limit. This helper keeps the timestamped folder segment compact and appends
a stable hash when truncation is required.
"""
timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
sanitized_parts = [_sanitize_part(part) for part in name_parts if str(part).strip()]
descriptor = "_".join(part for part in sanitized_parts if part)
folder_name = timestamp
if descriptor:
folder_name = _truncate_with_hash(f"{timestamp}_{descriptor}", max_dir_name_length)
output_dir = evaluation_root / folder_name
output_dir.mkdir(parents=True, exist_ok=True)
return output_dir
def _sanitize_part(value: str) -> str:
cleaned = _NON_ALNUM_PATTERN.sub("_", str(value).strip())
return cleaned.strip("_.-")
def _truncate_with_hash(value: str, max_length: int) -> str:
if len(value) <= max_length:
return value
digest = hashlib.sha1(value.encode("utf-8")).hexdigest()[:10]
separator = "_"
head_budget = max_length - len(separator) - len(digest)
if head_budget <= 0:
return digest[:max_length]
head = value[:head_budget].rstrip("_.-")
if not head:
return digest[:max_length]
return f"{head}{separator}{digest}"