Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / trainer / config / parser.py: 88%
49 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""Training configuration entry point: a YAML file plus dotted CLI overrides."""
17from __future__ import annotations
19__all__ = [
20 "parse_training_args",
21]
23import argparse
24from pathlib import Path
25from typing import Sequence
27import yaml
29from hyper_parallel.trainer.config.resolver import (
30 ConfigResolutionError,
31 replace_override_path,
32 resolve_config,
33)
34from hyper_parallel.trainer.config.trainer import TrainerConfig
37def _parse_override_tokens(tokens: Sequence[str]) -> list[tuple[str, str]]:
38 """Split ``--field=value`` CLI tokens into validated paths and raw values."""
39 parsed = []
40 for token in tokens:
41 if not token.startswith("--") or token == "--":
42 raise ConfigResolutionError(
43 "CLI", f"expected a dotted override in '--field=value' form, got {token!r}"
44 )
45 option = token[2:]
46 if "=" not in option:
47 raise ConfigResolutionError(
48 "CLI", f"expected a dotted override in '--field=value' form, got {token!r}"
49 )
50 path, raw_value = option.split("=", 1)
51 if not path or any(not part for part in path.split(".")):
52 raise ConfigResolutionError("CLI", f"invalid override path {path!r}")
53 parsed.append((path, raw_value))
54 return parsed
57def _apply_typed_overrides(
58 config: TrainerConfig,
59 overrides: Sequence[str],
60) -> TrainerConfig:
61 """Parse CLI override values and apply them to the resolved configuration."""
62 config_with_overrides = config
63 for dot_path, raw_value in _parse_override_tokens(overrides):
64 try:
65 parsed_value = yaml.safe_load(raw_value)
66 except yaml.YAMLError as exc:
67 raise ConfigResolutionError(
68 f"CLI.{dot_path}", f"invalid YAML value {raw_value!r}: {exc}"
69 ) from exc
70 config_with_overrides = replace_override_path(
71 config_with_overrides, dot_path.split("."), parsed_value, path=""
72 )
73 return config_with_overrides
76def _load_training_config(
77 config_file: str | Path,
78 cli_overrides: Sequence[str] = (),
79) -> TrainerConfig:
80 """Load a YAML configuration and apply CLI overrides."""
81 config_path = Path(config_file)
82 try:
83 raw = yaml.safe_load(config_path.read_text(encoding="utf-8"))
84 except OSError as exc:
85 raise ConfigResolutionError(config_path, f"could not read config file: {exc}") from exc
86 except yaml.YAMLError as exc:
87 raise ConfigResolutionError(config_path, f"invalid YAML: {exc}") from exc
89 config = resolve_config(raw)
90 return _apply_typed_overrides(config, cli_overrides)
93def parse_training_args(argv: Sequence[str] | None = None) -> TrainerConfig:
94 """Parse a YAML configuration path and CLI overrides into a ``TrainerConfig``.
96 Args:
97 argv: Argument tokens excluding the program name. Uses ``sys.argv[1:]``
98 when ``None``. The configuration path precedes any
99 ``--field=value`` overrides.
101 Returns:
102 The resolved configuration with CLI overrides applied.
104 Raises:
105 ConfigResolutionError: The file cannot be read, or the YAML
106 configuration or CLI overrides are invalid.
107 SystemExit: Help is requested or required command-line arguments
108 are missing.
110 Example:
111 config = parse_training_args(["train.yaml", "--accelerator.tp_size=4"])
112 """
113 parser = argparse.ArgumentParser(description="HyperParallel training config")
114 parser.add_argument("config_file", help="Path to the YAML training config")
115 args, overrides = parser.parse_known_args(argv)
116 return load_training_config(args.config_file, overrides)
119def load_training_config(
120 config_file: str | Path,
121 cli_overrides: Sequence[str] = (),
122) -> TrainerConfig:
123 """Load a training YAML into ``TrainerConfig``.
125 Args:
126 config_file: Path to the YAML training config.
127 cli_overrides: Canonical ``--field=value`` dotted overrides.
129 Returns:
130 Resolved training configuration, carrying the resolved YAML path for
131 callers that need the source of the configuration (codegen artifact
132 layout, signature).
133 """
134 config = _load_training_config(config_file, cli_overrides)
135 config._yaml_path = str(Path(config_file).resolve()) # pylint: disable=protected-access
136 return config