"""Pure validation and command contracts for remote execution.""" from __future__ import annotations import re import shlex _REMOTE_USER = re.compile(r"[A-Za-z0-9_.-]+") def _require_text(value: object, label: str) -> str: if not isinstance(value, str) or not value: raise ValueError(f"{label} must be a non-empty string") if any(ord(character) < 32 or ord(character) == 127 for character in value): raise ValueError(f"{label} may not contain control characters") return value def normalize_gpu_selection(value: str | None) -> str | None: """Return a canonical CUDA device selection or reject malformed input.""" if value is None: return None value = _require_text(value, "remote gpus").strip() if value.lower() == "all": return "all" parts = value.split(",") if any(not part.strip().isdigit() for part in parts): raise ValueError("remote gpus must be 'all' or comma-separated non-negative integers") return ",".join(str(int(part.strip())) for part in parts) def _validate_remote_identity(host: str, user: str) -> None: """Validate the host and user portions shared by CLI and YAML inputs.""" host = _require_text(host, "remote host") user = _require_text(user, "remote user") if host.startswith("-") or "@" in host or any(character.isspace() for character in host): raise ValueError("remote host must be a host name or address without user or options") if user.startswith("-") or _REMOTE_USER.fullmatch(user) is None: raise ValueError("remote user contains unsupported characters") def validate_remote_settings( *, host: str, user: str, port: int, remote_dir: str, python: str, gpus: str | None, install_source: str, ) -> str | None: """Validate public remote settings and return canonical GPU selection.""" _validate_remote_identity(host, user) remote_dir = _require_text(remote_dir, "remote directory") _require_text(python, "remote Python") _require_text(install_source, "remote install source") if isinstance(port, bool) or not isinstance(port, int) or not 1 <= port <= 65535: raise ValueError("remote port must be an integer from 1 through 65535") if not remote_dir.startswith("/"): raise ValueError("remote directory must be an absolute POSIX path") return normalize_gpu_selection(gpus) def parse_remote_target(remote: str) -> tuple[str, str]: """Parse and validate ``[USER@]HOST`` without accepting SSH options.""" remote = _require_text(remote, "remote target").strip() if remote.count("@") > 1: raise ValueError("remote target may contain at most one user separator") user, separator, host = remote.partition("@") if not separator: user, host = "root", user _validate_remote_identity(host, user) return user, host def remote_python_command( python: str, arguments: list[str], *, gpus: str | None = None, ) -> str: """Build one shell-safe command for the remote SSH shell.""" tokens: list[str] = [] normalized_gpus = normalize_gpu_selection(gpus) if normalized_gpus not in (None, "all"): tokens.extend(["env", f"CUDA_VISIBLE_DEVICES={normalized_gpus}"]) tokens.extend([_require_text(python, "remote Python"), *arguments]) return shlex.join(tokens) def remote_scp_spec(target: str, path: str, *, directory: bool = False) -> str: """Quote a remote SCP path while preserving the host/path separator.""" target = _require_text(target, "SSH target") path = _require_text(path, "remote SCP path") if directory: path = f"{path.rstrip('/')}/" return f"{target}:{shlex.quote(path)}"