Source code for qdk_chemistry.ui.io

"""File I/O utilities for QDK Chemistry data objects."""

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import os

from .validation import current_project_dir, resolve_project_file


def _validated_project_path(filename: str | os.PathLike[str]) -> str:
    """Confine MCP I/O to its project, preserving standalone CLI paths."""
    if current_project_dir() is None:
        return os.fspath(filename)
    return os.fspath(resolve_project_file(os.fspath(filename), allow_absolute=True))


def _load_data_object_from_path(filename: str | os.PathLike[str], data_class):
    """Load a data object from an exact filesystem path.

    Args:
        filename: Exact path to a JSON or HDF5 data file.
        data_class: Data class used to deserialize the file.

    Returns:
        The deserialized data object.

    Raises:
        ValueError: If the file extension is unsupported.

    """
    filename = _validated_project_path(filename)
    if filename.endswith(".json"):
        return data_class.from_json_file(filename)
    if filename.endswith((".hdf5", ".h5")):
        return data_class.from_hdf5_file(filename)
    raise ValueError(f"Unsupported file extension for {filename}. Must be .json, .hdf5, or .h5")


[docs] def check_output_path_exists(filename: str | os.PathLike[str], data_class: type | None = None) -> str | None: """Check whether an exact output path already exists with valid content. Args: filename: Exact output path to inspect. data_class: Optional data class used to validate existing content. Returns: A message when valid output exists, otherwise ``None``. """ filename = _validated_project_path(filename) if not os.path.exists(filename): return None if data_class is not None: try: existing_obj = _load_data_object_from_path(filename, data_class) if existing_obj is not None: return ( f"EXISTS: Output file '{filename}' already exists with valid data. " "Do you want to run again and overwrite it? If this was an expensive " "calculation and you trust the contents of the older file, you should use the older file." ) except (RuntimeError, ValueError, FileNotFoundError, OSError): return None else: return ( f"EXISTS: Output file '{filename}' already exists. " "Do you want to run again and overwrite it? If this was an expensive " "calculation and you trust the contents of the older file, you should use the older file." ) return None
[docs] def check_output_exists(filename: str, data_class: type | None = None) -> str | None: """Check if an output file already exists with valid content. Args: filename (str): The output filename to check data_class: Optional data class to validate file content. If provided, attempts to load the file to verify it contains valid data. Returns: Optional[str]: A message if the file exists with valid content, None otherwise """ return check_output_path_exists(filename, data_class)
[docs] def load_data_object(filename: str | os.PathLike[str], data_class): """Load a data object from either json or hdf5 file based on extension. Args: filename: Path to a file with extension (.json or .hdf5/.h5). data_class: The qdk_chemistry.data class to instantiate Returns: The loaded data object Raises: ValueError: If file extension is not supported """ return _load_data_object_from_path(filename, data_class)
[docs] def save_data_object(data_obj, filename: str | os.PathLike[str]): """Save a data object to either json or hdf5 file based on extension. Args: data_obj: The qdk_chemistry data object to save filename: Path to a file with extension (.json or .hdf5/.h5). Returns: str: The filename where data was saved Raises: ValueError: If file extension is not supported """ filename = _validated_project_path(filename) if filename.endswith(".json"): data_obj.to_json_file(filename) elif filename.endswith((".hdf5", ".h5")): data_obj.to_hdf5_file(filename) else: raise ValueError(f"Unsupported file extension for {filename}. Must be .json, .hdf5, or .h5") return filename