Source code for kusp.kim.kim_utils

import hashlib
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, List, Optional, Tuple

from loguru import logger

from ..utils import (
    generate_ast_env_yaml,
    generate_conda_env_yaml,
    generate_pip_requirements,
)

# TODO change the driver names etc
KUSP_MODEL_ARTIFACT = "KUSP__MO_000000000000_000"
KUSP_DRIVER_ARTIFACT = "KUSP__MD_000000000000_000"
KUSP_MODEL_PREFIX = "KUSP__MO"
KUSP_DRIVER_PREFIX = "KUSP__MD"
KIM_COLLECTIONS_TOOL = "kim-api-collections-management"
KIM_ITEMS_TOOL = "kimitems"


[docs] @dataclass(frozen=True) class DeploymentPackage: """One stop information about the generated deployment package.""" model_name: str target_dir: Path env_file: Path cmake_file: Path files_written: Tuple[str, ...]
def _list_kim_items(tool: str = KIM_COLLECTIONS_TOOL) -> str: """Return the textual output of the KIM list command.""" try: proc = subprocess.run( [tool, "list"], capture_output=True, text=True, check=True, ) except (OSError, subprocess.CalledProcessError) as exc: logger.error(f"Failed to query installed KIM items via {tool}: {exc}") raise return proc.stdout def _artifact_present(prefix: str, *, tool: str = KIM_COLLECTIONS_TOOL) -> bool: """Check whether a given artifact prefix is present in the installed list.""" listing = _list_kim_items(tool=tool) return any(prefix in line for line in listing.splitlines())
[docs] def check_if_model_installed() -> bool: """Return True if the bundled Python model is already installed.""" return _artifact_present(KUSP_MODEL_PREFIX)
[docs] def check_if_driver_installed() -> bool: """Return True if the bundled driver is already installed.""" return _artifact_present(KUSP_DRIVER_PREFIX)
[docs] def package_model_for_deployment( model_file: Path, resources: Iterable[Path] = (), *, name: Optional[str] = None, env_mode: str, ) -> DeploymentPackage: """Bundle a decorated model and auxiliary files into a deployable package. Args: model_file: Path to a Python module containing a ``@kusp_model`` entry. resources: Extra files copied verbatim into the package directory. name: Optional explicit KIM-compliant model name. env_mode: ``ast``, ``pip``, or ``conda`` env description strategy. Returns: DeploymentPackage with paths to all generated artifacts. """ resources = tuple(resources or ()) resolved_name = name or f"KUSP_{model_file.stem}__MO_111111111111_000" target_dir = Path(resolved_name) target_dir.mkdir(exist_ok=False) model_hash = hashlib.blake2b( model_file.read_bytes(), digest_size=8 ).hexdigest() model_target = target_dir / f"@kusp_model_{model_hash}_{resolved_name}.py" shutil.copy2(model_file, model_target) env_mode = env_mode.lower() files_to_write: List[str] = [model_target.name] if env_mode == "ast": env_text = generate_ast_env_yaml(model_target, env_name=resolved_name) env_file = target_dir / "kusp_env.ast.env" elif env_mode == "pip": env_text = generate_pip_requirements() env_file = target_dir / "kusp_env.pip.txt" elif env_mode == "conda": env_text = generate_conda_env_yaml(env_name=resolved_name) env_file = target_dir / "kusp_env.conda.yml" else: raise ValueError(f"Unsupported env resolver: {env_mode}") env_file.write_text(env_text) files_to_write.append(env_file.name) for resource in resources: destination = target_dir / Path(resource).name shutil.copy2(resource, destination) files_to_write.append(destination.name) files_to_write_str = " ".join(f'"{name}"' for name in files_to_write) cmake_template = f""" cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_PREFIX_PATH $ENV{{KIM_API_CMAKE_PREFIX_DIR}}) find_package(KIM-API-ITEMS 2.2 REQUIRED CONFIG) kim_api_items_setup_before_project(ITEM_TYPE "portableModel") project({resolved_name} LANGUAGES CXX) kim_api_items_setup_after_project(ITEM_TYPE "portableModel") add_kim_api_model_library( NAME "${{PROJECT_NAME}}" DRIVER_NAME "{KUSP_DRIVER_ARTIFACT}" PARAMETER_FILES {files_to_write_str} ) """ cmake_file = target_dir / "CMakeLists.txt" cmake_file.write_text(cmake_template.strip() + "\n") return DeploymentPackage( model_name=resolved_name, target_dir=target_dir, env_file=env_file, cmake_file=cmake_file, files_written=tuple(files_to_write), )