"""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