qq_lib.properties.interpreter
1# Released under MIT License. 2# Copyright (c) 2025-2026 Ladislav Bartos and Robert Vacha Lab 3 4import shutil 5import socket 6from dataclasses import dataclass, field 7from typing import Any 8 9from qq_lib.core.config import CFG 10from qq_lib.core.error import QQError 11 12 13@dataclass(frozen=True) 14class Interpreter: 15 """ 16 Configuration for the interpreter used to execute a job script. 17 18 Attributes: 19 executable: Name or path of the interpreter executable. 20 Defaults to the executable from `CFG.runner.default_interpreter`. 21 arguments: Additional command-line arguments passed to the interpreter. 22 Defaults to arguments parsed from `CFG.runner.default_interpreter`, 23 or an empty list if `executable` is provided explicitly. 24 """ 25 26 executable: str | None = None 27 arguments: list[str] = field(default_factory=list) 28 29 def __post_init__(self): 30 """Parse executable and arguments from `CFG.runner.default_interpreter` if no executable was provided.""" 31 if self.executable is None: 32 binary, *arguments = CFG.runner.default_interpreter.split() 33 object.__setattr__(self, "executable", binary) 34 object.__setattr__(self, "arguments", arguments) 35 36 @classmethod 37 def from_str(cls, s: str) -> "Interpreter": 38 """ 39 Create an Interpreter from a string containing the executable and optional arguments. 40 41 The string is split on whitespace. The first token is used as the executable 42 and any remaining tokens become arguments. 43 44 Args: 45 s (str): Space-separated string of the interpreter executable followed by 46 optional arguments. For example: "python3 -u" or "/usr/bin/bash". 47 48 Returns: 49 Interpreter:An Interpreter instance with the parsed executable and arguments. 50 """ 51 executable, *arguments = s.split() 52 return cls(executable=executable, arguments=arguments) 53 54 @classmethod 55 def from_dict(cls, d: dict[str, Any]) -> "Interpreter": 56 """ 57 Create an Interpreter from a dictionary. 58 59 Args: 60 d (dict[str, Any]): Dictionary with keys matching the dataclass fields 61 (`executable` and `arguments`). 62 63 Returns: 64 Interpreter: An Interpreter instance constructed from the dictionary values. 65 """ 66 return cls(**d) 67 68 def to_dict(self) -> dict[str, Any]: 69 """ 70 Serialize the Interpreter to a dictionary. 71 72 Returns: 73 A dictionary with `executable` and `arguments` keys. 74 """ 75 return {"executable": self.executable, "arguments": self.arguments} 76 77 def to_command_list(self) -> list[str]: 78 """ 79 Resolve the executable to its full path and build the command list. 80 81 Uses `shutil.which` to locate the interpreter on the current node. 82 The returned list is suitable for use with `subprocess` calls. 83 84 Returns: 85 list[str]: A list containing the resolved absolute path of the executable 86 followed by any configured arguments. 87 88 Raises: 89 QQError: If the interpreter executable cannot be found on the current node. 90 """ 91 # enforced in __post_init__ 92 assert self.executable is not None 93 94 if not (full := shutil.which(self.executable)): 95 raise QQError( 96 f"Interpreter '{self.executable}' is not available on node '{socket.getfqdn()}'." 97 ) 98 99 return [full] + self.arguments
14@dataclass(frozen=True) 15class Interpreter: 16 """ 17 Configuration for the interpreter used to execute a job script. 18 19 Attributes: 20 executable: Name or path of the interpreter executable. 21 Defaults to the executable from `CFG.runner.default_interpreter`. 22 arguments: Additional command-line arguments passed to the interpreter. 23 Defaults to arguments parsed from `CFG.runner.default_interpreter`, 24 or an empty list if `executable` is provided explicitly. 25 """ 26 27 executable: str | None = None 28 arguments: list[str] = field(default_factory=list) 29 30 def __post_init__(self): 31 """Parse executable and arguments from `CFG.runner.default_interpreter` if no executable was provided.""" 32 if self.executable is None: 33 binary, *arguments = CFG.runner.default_interpreter.split() 34 object.__setattr__(self, "executable", binary) 35 object.__setattr__(self, "arguments", arguments) 36 37 @classmethod 38 def from_str(cls, s: str) -> "Interpreter": 39 """ 40 Create an Interpreter from a string containing the executable and optional arguments. 41 42 The string is split on whitespace. The first token is used as the executable 43 and any remaining tokens become arguments. 44 45 Args: 46 s (str): Space-separated string of the interpreter executable followed by 47 optional arguments. For example: "python3 -u" or "/usr/bin/bash". 48 49 Returns: 50 Interpreter:An Interpreter instance with the parsed executable and arguments. 51 """ 52 executable, *arguments = s.split() 53 return cls(executable=executable, arguments=arguments) 54 55 @classmethod 56 def from_dict(cls, d: dict[str, Any]) -> "Interpreter": 57 """ 58 Create an Interpreter from a dictionary. 59 60 Args: 61 d (dict[str, Any]): Dictionary with keys matching the dataclass fields 62 (`executable` and `arguments`). 63 64 Returns: 65 Interpreter: An Interpreter instance constructed from the dictionary values. 66 """ 67 return cls(**d) 68 69 def to_dict(self) -> dict[str, Any]: 70 """ 71 Serialize the Interpreter to a dictionary. 72 73 Returns: 74 A dictionary with `executable` and `arguments` keys. 75 """ 76 return {"executable": self.executable, "arguments": self.arguments} 77 78 def to_command_list(self) -> list[str]: 79 """ 80 Resolve the executable to its full path and build the command list. 81 82 Uses `shutil.which` to locate the interpreter on the current node. 83 The returned list is suitable for use with `subprocess` calls. 84 85 Returns: 86 list[str]: A list containing the resolved absolute path of the executable 87 followed by any configured arguments. 88 89 Raises: 90 QQError: If the interpreter executable cannot be found on the current node. 91 """ 92 # enforced in __post_init__ 93 assert self.executable is not None 94 95 if not (full := shutil.which(self.executable)): 96 raise QQError( 97 f"Interpreter '{self.executable}' is not available on node '{socket.getfqdn()}'." 98 ) 99 100 return [full] + self.arguments
Configuration for the interpreter used to execute a job script.
Attributes:
- executable: Name or path of the interpreter executable.
Defaults to the executable from
CFG.runner.default_interpreter. - arguments: Additional command-line arguments passed to the interpreter.
Defaults to arguments parsed from
CFG.runner.default_interpreter, or an empty list ifexecutableis provided explicitly.
37 @classmethod 38 def from_str(cls, s: str) -> "Interpreter": 39 """ 40 Create an Interpreter from a string containing the executable and optional arguments. 41 42 The string is split on whitespace. The first token is used as the executable 43 and any remaining tokens become arguments. 44 45 Args: 46 s (str): Space-separated string of the interpreter executable followed by 47 optional arguments. For example: "python3 -u" or "/usr/bin/bash". 48 49 Returns: 50 Interpreter:An Interpreter instance with the parsed executable and arguments. 51 """ 52 executable, *arguments = s.split() 53 return cls(executable=executable, arguments=arguments)
Create an Interpreter from a string containing the executable and optional arguments.
The string is split on whitespace. The first token is used as the executable and any remaining tokens become arguments.
Arguments:
- s (str): Space-separated string of the interpreter executable followed by optional arguments. For example: "python3 -u" or "/usr/bin/bash".
Returns:
Interpreter:An Interpreter instance with the parsed executable and arguments.
55 @classmethod 56 def from_dict(cls, d: dict[str, Any]) -> "Interpreter": 57 """ 58 Create an Interpreter from a dictionary. 59 60 Args: 61 d (dict[str, Any]): Dictionary with keys matching the dataclass fields 62 (`executable` and `arguments`). 63 64 Returns: 65 Interpreter: An Interpreter instance constructed from the dictionary values. 66 """ 67 return cls(**d)
Create an Interpreter from a dictionary.
Arguments:
- d (dict[str, Any]): Dictionary with keys matching the dataclass fields
(
executableandarguments).
Returns:
Interpreter: An Interpreter instance constructed from the dictionary values.
69 def to_dict(self) -> dict[str, Any]: 70 """ 71 Serialize the Interpreter to a dictionary. 72 73 Returns: 74 A dictionary with `executable` and `arguments` keys. 75 """ 76 return {"executable": self.executable, "arguments": self.arguments}
Serialize the Interpreter to a dictionary.
Returns:
A dictionary with
executableandargumentskeys.
78 def to_command_list(self) -> list[str]: 79 """ 80 Resolve the executable to its full path and build the command list. 81 82 Uses `shutil.which` to locate the interpreter on the current node. 83 The returned list is suitable for use with `subprocess` calls. 84 85 Returns: 86 list[str]: A list containing the resolved absolute path of the executable 87 followed by any configured arguments. 88 89 Raises: 90 QQError: If the interpreter executable cannot be found on the current node. 91 """ 92 # enforced in __post_init__ 93 assert self.executable is not None 94 95 if not (full := shutil.which(self.executable)): 96 raise QQError( 97 f"Interpreter '{self.executable}' is not available on node '{socket.getfqdn()}'." 98 ) 99 100 return [full] + self.arguments
Resolve the executable to its full path and build the command list.
Uses shutil.which to locate the interpreter on the current node.
The returned list is suitable for use with subprocess calls.
Returns:
list[str]: A list containing the resolved absolute path of the executable followed by any configured arguments.
Raises:
- QQError: If the interpreter executable cannot be found on the current node.