from__future__importannotationsimportargparseimportjsonfrompathlibimportPathfromfermilink.configimportresolve_fermilink_homefromfermilink.workspace.commonimport_cli,resolve_cli_pathHPC_PROFILE_FILENAME="HPC_PROFILE.json"LEGACY_HPC_PROFILE_FILENAME="hpc_profile.json"HPC_PROFILE_REQUIRED_KEYS=("slurm_default_partition","slurm_defaults","slurm_resource_policy",)DEFAULT_HPC_PROFILE_PAYLOAD={"slurm_default_partition":"shared","slurm_defaults":("--nodes=1 --ntasks=1 --ntasks-per-node=1 ""--cpus-per-task=1 --time=24:00:00"),"slurm_resource_policy":("Use serial/single-node defaults unless the method explicitly ""requires MPI or multi-node scaling"),}
[docs]defnormalize_hpc_profile_payload(raw_profile:dict[str,object],*,profile_label:str)->dict[str,str]:normalized:dict[str,str]={}forkeyinHPC_PROFILE_REQUIRED_KEYS:raw_value=raw_profile.get(key)ifraw_valueisNone:raiseValueError(f"HPC profile missing required `{key}` in {profile_label}.")ifnotisinstance(raw_value,str):raiseValueError(f"HPC profile `{key}` must be a non-empty string in {profile_label}.")text_value=" ".join(raw_value.strip().split())ifnottext_value:raiseValueError(f"HPC profile `{key}` must be a non-empty string in {profile_label}.")normalized[key]=text_valuereturnnormalized
[docs]defload_hpc_profile_payload(path:Path)->dict[str,str]:ifnotpath.exists():raiseFileNotFoundError(f"HPC profile does not exist: {path}")ifnotpath.is_file():raiseValueError(f"HPC profile must be a file: {path}")try:payload=json.loads(path.read_text(encoding="utf-8"))exceptOSErrorasexc:raiseValueError(f"Failed to read HPC profile: {path}: {exc}")fromexcexceptjson.JSONDecodeErrorasexc:raiseValueError(f"HPC profile must contain valid JSON: {path}: {exc}")fromexcifnotisinstance(payload,dict):raiseValueError(f"HPC profile root JSON must be an object: {path}")returnnormalize_hpc_profile_payload(payload,profile_label=str(path))
[docs]defcmd_hpc(args:argparse.Namespace)->int:cli=_cli()hpc_command=str(getattr(args,"hpc_command","")or"").strip().lower()try:ifnothpc_command:_,created,migrated=ensure_default_hpc_profile()home=resolve_fermilink_home()canonical_path=home/HPC_PROFILE_FILENAMEifcreated:ifmigrated:print(f"[fermilink-hpc] Migrated legacy {LEGACY_HPC_PROFILE_FILENAME} "f"to {canonical_path}")else:print("[fermilink-hpc] Created default HPC profile at "f"{canonical_path}")else:print(f"[fermilink-hpc] {canonical_path} already exists. ""No copy was made.")print("[fermilink-hpc] You can adjust the profile as needed for your ""HPC environment.")return0ifhpc_command=="set":raw_source=str(getattr(args,"file","")or"").strip()ifnotraw_source:raiseValueError("Please provide a JSON profile path.")source=resolve_cli_path(raw_source)destination=set_hpc_profile(source)print(f"[fermilink-hpc] Installed profile at {destination}")return0exceptExceptionasexc:raisecli.PackageError(str(exc))fromexcraisecli.PackageError(f"Unknown hpc command: {hpc_command}")