From f40e3470b53696096ddee61f4e6b0eca6e11fd50 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 20 Dec 2025 17:12:52 +0800 Subject: [PATCH 1/6] fix overrides for same key in sweeper --- hydra/_internal/core_plugins/basic_sweeper.py | 30 ++++++++++++++----- tests/test_basic_sweeper.py | 20 +++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/hydra/_internal/core_plugins/basic_sweeper.py b/hydra/_internal/core_plugins/basic_sweeper.py index 3e41fcb5529..effcfa0d6bc 100644 --- a/hydra/_internal/core_plugins/basic_sweeper.py +++ b/hydra/_internal/core_plugins/basic_sweeper.py @@ -108,13 +108,22 @@ def split_arguments( overrides: List[Override], max_batch_size: Optional[int] ) -> List[List[List[str]]]: lists = [] - final_overrides = OrderedDict() - for override in overrides: + # NOTE: key -> index of last override with no dict value. (e.g. a=1,2,3) + # any override for key before this would be skipped. + skip_to = {} + # track if a key ends up to be a dict override + contains_dict = {} + list_overrides = [] + for i, override in enumerate(overrides): if override.is_sweep_override(): if override.is_discrete_sweep(): key = override.get_key_element() sweep = [f"{key}={val}" for val in override.sweep_string_iterator()] - final_overrides[key] = sweep + has_dict = any(override.sweep_iterator(lambda x: isinstance(x, dict))) + if not has_dict: + skip_to[key] = i + contains_dict[key] = has_dict + list_overrides.append((key, sweep)) else: assert override.value_type is not None raise HydraException( @@ -123,11 +132,16 @@ def split_arguments( else: key = override.get_key_element() value = override.get_value_element_as_str() - final_overrides[key] = [f"{key}={value}"] - - for _, v in final_overrides.items(): - lists.append(v) - + has_dict = isinstance(override.value(), dict) + if not has_dict: + skip_to[key] = i + contains_dict[key] = has_dict + list_overrides.append((key, [f"{key}={value}"])) + + for i, (k, v) in enumerate(list_overrides): + s = skip_to.get(k, -1) + if i > s or (i == s and not contains_dict[k]): + lists.append(v) all_batches = [list(x) for x in itertools.product(*lists)] assert max_batch_size is None or max_batch_size > 0 if max_batch_size is None: diff --git a/tests/test_basic_sweeper.py b/tests/test_basic_sweeper.py index 0d2b0dc89a0..e97e722e8ed 100644 --- a/tests/test_basic_sweeper.py +++ b/tests/test_basic_sweeper.py @@ -48,6 +48,26 @@ ), param(["a=range(0,3)"], None, [[["a=0"], ["a=1"], ["a=2"]]], id="range"), param(["a=range(3)"], None, [[["a=0"], ["a=1"], ["a=2"]]], id="range_no_start"), + param(["a=1,2,3", "a=20"], None, [[["a=20"]]], id="override_same_key1"), + param(["a=2", "a=10,20"], None, [[["a=10"], ["a=20"]]], id="override_same_key2"), + param(["a=1,2,3", "a=10,20"], None, [[["a=10"], ["a=20"]]], id="override_same_key3"), + param(["a={x:1},{x:2}"], None, [[["a={x:1}"], ["a={x:2}"]]], id="dicts"), + param( + ["a={x:1},{x:2}", "+a={y:10},{y:20}"], + None, + [[["a={x:1}", "+a={y:10}"], ["a={x:1}", "+a={y:20}"], ["a={x:2}", "+a={y:10}"], ["a={x:2}", "+a={y:20}"]]], + id="dicts_multiple_with_plus", + ), + param( + ["a={x:1},{x:2}", "a={y:10},{y:20}"], + None, + [[["a={x:1}", "a={y:10}"], ["a={x:1}", "a={y:20}"], ["a={x:2}", "a={y:10}"], ["a={x:2}", "a={y:20}"]]], + id="dicts_multiple", + ), + param(["a=1,2,3", "a={x:1}"], None, [[["a={x:1}"]]], id="override_with_dict1"), + param(["a=1,2,3", "a={x:1},{x:2}"], None, [[["a={x:1}"], ["a={x:2}"]]], id="override_with_dict2"), + param(["a={x:1}", "a=1,2,3"], None, [[["a=1"], ["a=2"], ["a=3"]]], id="override_with_dict3"), + param(["a={x:1},{x:2}", "a=1"], None, [[["a=1"]]], id="override_with_dict4"), ], ) def test_split( From 70af5a04053729b478ab50393692ae8fdeca11fa Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 20 Dec 2025 17:51:40 +0800 Subject: [PATCH 2/6] add simplify_overrides --- hydra/_internal/core_plugins/basic_sweeper.py | 73 +++++++++++++------ 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/hydra/_internal/core_plugins/basic_sweeper.py b/hydra/_internal/core_plugins/basic_sweeper.py index effcfa0d6bc..98aa11a02f5 100644 --- a/hydra/_internal/core_plugins/basic_sweeper.py +++ b/hydra/_internal/core_plugins/basic_sweeper.py @@ -28,7 +28,7 @@ from hydra.core.config_store import ConfigStore from hydra.core.override_parser.overrides_parser import OverridesParser -from hydra.core.override_parser.types import Override +from hydra.core.override_parser.types import Override, QuotedString from hydra.core.utils import JobReturn from hydra.errors import HydraException from hydra.plugins.launcher import Launcher @@ -93,6 +93,50 @@ def setup( config=config, ) + @staticmethod + def simplify_overrides( + overrides: List[Override], + ) -> List[Override]: + lists = [] + # NOTE: key -> index of last override with no dict value. (e.g. a=1,2,3) + # any override for key before this would be skipped. + last_primitive = {} + last_dict = {} + # track if a key ends up to be a dict override + is_primitive = {} + is_dict = {} + + def check_primitive(x: Any) -> bool: + return isinstance(x, (int, float, bool)) or isinstance(x, QuotedString) + for i, override in enumerate(overrides): + if override.is_sweep_override(): + if override.is_discrete_sweep(): + key = override.get_key_element() + is_primitive[i] = all(override.sweep_iterator(check_primitive)) + is_dict[i] = any(override.sweep_iterator(lambda x: isinstance(x, dict))) + if is_primitive[i]: + last_primitive[key] = i + if is_dict[i]: + last_dict[key] = i + else: + key = override.get_key_element() + is_primitive[i] = check_primitive(override.value()) + is_dict[i] = isinstance(override.value(), dict) + if is_primitive[i]: + last_primitive[key] = i + if is_dict[i]: + last_dict[key] = i + + for i, override in enumerate(overrides): + key = override.get_key_element() + if is_primitive.get(i, False) and (last_primitive.get(key, -1) != i or last_dict.get(key, -1) > i): + continue + if is_dict.get(i, False) and last_primitive.get(key, -1) > i: + continue + lists.append(override) + + return lists + @staticmethod def split_overrides_to_chunks( lst: List[List[str]], n: Optional[int] @@ -108,22 +152,13 @@ def split_arguments( overrides: List[Override], max_batch_size: Optional[int] ) -> List[List[List[str]]]: lists = [] - # NOTE: key -> index of last override with no dict value. (e.g. a=1,2,3) - # any override for key before this would be skipped. - skip_to = {} - # track if a key ends up to be a dict override - contains_dict = {} - list_overrides = [] - for i, override in enumerate(overrides): + overrides = BasicSweeper.simplify_overrides(overrides) + for override in overrides: if override.is_sweep_override(): if override.is_discrete_sweep(): key = override.get_key_element() sweep = [f"{key}={val}" for val in override.sweep_string_iterator()] - has_dict = any(override.sweep_iterator(lambda x: isinstance(x, dict))) - if not has_dict: - skip_to[key] = i - contains_dict[key] = has_dict - list_overrides.append((key, sweep)) + lists.append(sweep) else: assert override.value_type is not None raise HydraException( @@ -132,16 +167,8 @@ def split_arguments( else: key = override.get_key_element() value = override.get_value_element_as_str() - has_dict = isinstance(override.value(), dict) - if not has_dict: - skip_to[key] = i - contains_dict[key] = has_dict - list_overrides.append((key, [f"{key}={value}"])) - - for i, (k, v) in enumerate(list_overrides): - s = skip_to.get(k, -1) - if i > s or (i == s and not contains_dict[k]): - lists.append(v) + lists.append([f"{key}={value}"]) + all_batches = [list(x) for x in itertools.product(*lists)] assert max_batch_size is None or max_batch_size > 0 if max_batch_size is None: From 4325a0c4623645346dd08255fb60516b34f09f39 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 20 Dec 2025 17:59:09 +0800 Subject: [PATCH 3/6] add test for simplify overrides --- tests/test_basic_sweeper.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_basic_sweeper.py b/tests/test_basic_sweeper.py index e97e722e8ed..56e10e98656 100644 --- a/tests/test_basic_sweeper.py +++ b/tests/test_basic_sweeper.py @@ -80,6 +80,28 @@ def test_split( lret = [list(x) for x in ret] assert lret == expected +@mark.parametrize( + "args,expected", + [ + param(["a=1", "b=2", "a=3"], ["b=2", "a=3"], id="simple_override"), + param(["a=1,2", "a=3,4"], ["a=3,4"], id="override_split"), + param(["a=1", "b=2", "+a={x:10}", "+a={y:20}"], ["a=1", "b=2", "+a={x:10}", "+a={y:20}"], id="override_plus"), + param(["a=1", "b=2", "a={x:10}", "a={y:20}"], ["b=2", "a={x:10}", "a={y:20}"], id="override_plus"), + param(["a={x:1}", "a={y:2}"], ["a={x:1}", "a={y:2}"], id="override_dict"), + param(["a=1,2", "+a={x:10},{y:20}", "a=3,4"], ["+a={x:10},{y:20}", "a=3,4"], id="override_mixed"), + param(["a=1,2", "a={x:10},{y:20}", "a=3,4"], ["a=3,4"], id="override_mixed"), + param(["+a=xx,yy", "+a=[zz]"], ["+a=xx,yy", "+a=[zz]"], id="override_plus_list"), + ] +) +def test_simplify( + args: List[str], expected: List[str] +) -> None: + parser = OverridesParser.create() + overrides = parser.parse_overrides(args) + simplified = BasicSweeper.simplify_overrides(overrides) + expected_overrides = parser.parse_overrides(expected) + assert simplified == expected_overrides + def test_partial_failure( tmpdir: Any, From 4367a15d414bcc6b8603cf658f51e092e700430e Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 20 Dec 2025 18:04:28 +0800 Subject: [PATCH 4/6] fmt --- hydra/_internal/core_plugins/basic_sweeper.py | 9 ++- tests/test_basic_sweeper.py | 72 +++++++++++++++---- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/hydra/_internal/core_plugins/basic_sweeper.py b/hydra/_internal/core_plugins/basic_sweeper.py index 98aa11a02f5..f0763f6494d 100644 --- a/hydra/_internal/core_plugins/basic_sweeper.py +++ b/hydra/_internal/core_plugins/basic_sweeper.py @@ -108,12 +108,15 @@ def simplify_overrides( def check_primitive(x: Any) -> bool: return isinstance(x, (int, float, bool)) or isinstance(x, QuotedString) + for i, override in enumerate(overrides): if override.is_sweep_override(): if override.is_discrete_sweep(): key = override.get_key_element() is_primitive[i] = all(override.sweep_iterator(check_primitive)) - is_dict[i] = any(override.sweep_iterator(lambda x: isinstance(x, dict))) + is_dict[i] = any( + override.sweep_iterator(lambda x: isinstance(x, dict)) + ) if is_primitive[i]: last_primitive[key] = i if is_dict[i]: @@ -129,7 +132,9 @@ def check_primitive(x: Any) -> bool: for i, override in enumerate(overrides): key = override.get_key_element() - if is_primitive.get(i, False) and (last_primitive.get(key, -1) != i or last_dict.get(key, -1) > i): + if is_primitive.get(i, False) and ( + last_primitive.get(key, -1) != i or last_dict.get(key, -1) > i + ): continue if is_dict.get(i, False) and last_primitive.get(key, -1) > i: continue diff --git a/tests/test_basic_sweeper.py b/tests/test_basic_sweeper.py index 56e10e98656..8f0fb534679 100644 --- a/tests/test_basic_sweeper.py +++ b/tests/test_basic_sweeper.py @@ -49,24 +49,55 @@ param(["a=range(0,3)"], None, [[["a=0"], ["a=1"], ["a=2"]]], id="range"), param(["a=range(3)"], None, [[["a=0"], ["a=1"], ["a=2"]]], id="range_no_start"), param(["a=1,2,3", "a=20"], None, [[["a=20"]]], id="override_same_key1"), - param(["a=2", "a=10,20"], None, [[["a=10"], ["a=20"]]], id="override_same_key2"), - param(["a=1,2,3", "a=10,20"], None, [[["a=10"], ["a=20"]]], id="override_same_key3"), + param( + ["a=2", "a=10,20"], None, [[["a=10"], ["a=20"]]], id="override_same_key2" + ), + param( + ["a=1,2,3", "a=10,20"], + None, + [[["a=10"], ["a=20"]]], + id="override_same_key3", + ), param(["a={x:1},{x:2}"], None, [[["a={x:1}"], ["a={x:2}"]]], id="dicts"), param( ["a={x:1},{x:2}", "+a={y:10},{y:20}"], None, - [[["a={x:1}", "+a={y:10}"], ["a={x:1}", "+a={y:20}"], ["a={x:2}", "+a={y:10}"], ["a={x:2}", "+a={y:20}"]]], + [ + [ + ["a={x:1}", "+a={y:10}"], + ["a={x:1}", "+a={y:20}"], + ["a={x:2}", "+a={y:10}"], + ["a={x:2}", "+a={y:20}"], + ] + ], id="dicts_multiple_with_plus", ), param( ["a={x:1},{x:2}", "a={y:10},{y:20}"], None, - [[["a={x:1}", "a={y:10}"], ["a={x:1}", "a={y:20}"], ["a={x:2}", "a={y:10}"], ["a={x:2}", "a={y:20}"]]], + [ + [ + ["a={x:1}", "a={y:10}"], + ["a={x:1}", "a={y:20}"], + ["a={x:2}", "a={y:10}"], + ["a={x:2}", "a={y:20}"], + ] + ], id="dicts_multiple", ), param(["a=1,2,3", "a={x:1}"], None, [[["a={x:1}"]]], id="override_with_dict1"), - param(["a=1,2,3", "a={x:1},{x:2}"], None, [[["a={x:1}"], ["a={x:2}"]]], id="override_with_dict2"), - param(["a={x:1}", "a=1,2,3"], None, [[["a=1"], ["a=2"], ["a=3"]]], id="override_with_dict3"), + param( + ["a=1,2,3", "a={x:1},{x:2}"], + None, + [[["a={x:1}"], ["a={x:2}"]]], + id="override_with_dict2", + ), + param( + ["a={x:1}", "a=1,2,3"], + None, + [[["a=1"], ["a=2"], ["a=3"]]], + id="override_with_dict3", + ), param(["a={x:1},{x:2}", "a=1"], None, [[["a=1"]]], id="override_with_dict4"), ], ) @@ -80,22 +111,35 @@ def test_split( lret = [list(x) for x in ret] assert lret == expected + @mark.parametrize( "args,expected", [ param(["a=1", "b=2", "a=3"], ["b=2", "a=3"], id="simple_override"), param(["a=1,2", "a=3,4"], ["a=3,4"], id="override_split"), - param(["a=1", "b=2", "+a={x:10}", "+a={y:20}"], ["a=1", "b=2", "+a={x:10}", "+a={y:20}"], id="override_plus"), - param(["a=1", "b=2", "a={x:10}", "a={y:20}"], ["b=2", "a={x:10}", "a={y:20}"], id="override_plus"), + param( + ["a=1", "b=2", "+a={x:10}", "+a={y:20}"], + ["a=1", "b=2", "+a={x:10}", "+a={y:20}"], + id="override_plus", + ), + param( + ["a=1", "b=2", "a={x:10}", "a={y:20}"], + ["b=2", "a={x:10}", "a={y:20}"], + id="override_plus", + ), param(["a={x:1}", "a={y:2}"], ["a={x:1}", "a={y:2}"], id="override_dict"), - param(["a=1,2", "+a={x:10},{y:20}", "a=3,4"], ["+a={x:10},{y:20}", "a=3,4"], id="override_mixed"), + param( + ["a=1,2", "+a={x:10},{y:20}", "a=3,4"], + ["+a={x:10},{y:20}", "a=3,4"], + id="override_mixed", + ), param(["a=1,2", "a={x:10},{y:20}", "a=3,4"], ["a=3,4"], id="override_mixed"), - param(["+a=xx,yy", "+a=[zz]"], ["+a=xx,yy", "+a=[zz]"], id="override_plus_list"), - ] + param( + ["+a=xx,yy", "+a=[zz]"], ["+a=xx,yy", "+a=[zz]"], id="override_plus_list" + ), + ], ) -def test_simplify( - args: List[str], expected: List[str] -) -> None: +def test_simplify(args: List[str], expected: List[str]) -> None: parser = OverridesParser.create() overrides = parser.parse_overrides(args) simplified = BasicSweeper.simplify_overrides(overrides) From 45aaa8ef6563041182e8fd8919863e88f6d4f92b Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 20 Dec 2025 19:38:54 +0800 Subject: [PATCH 5/6] support is_group and fix test --- hydra/_internal/core_plugins/basic_sweeper.py | 64 +++++++++++++------ hydra/core/override_parser/types.py | 6 ++ tests/test_basic_sweeper.py | 11 +++- tests/test_examples/test_basic_sweep.py | 12 ++-- 4 files changed, 63 insertions(+), 30 deletions(-) diff --git a/hydra/_internal/core_plugins/basic_sweeper.py b/hydra/_internal/core_plugins/basic_sweeper.py index f0763f6494d..093d4e171ff 100644 --- a/hydra/_internal/core_plugins/basic_sweeper.py +++ b/hydra/_internal/core_plugins/basic_sweeper.py @@ -25,6 +25,7 @@ from typing import Any, Dict, Iterable, List, Optional, Sequence from omegaconf import DictConfig, OmegaConf +from omegaconf._utils import is_structured_config from hydra.core.config_store import ConfigStore from hydra.core.override_parser.overrides_parser import OverridesParser @@ -97,38 +98,57 @@ def setup( def simplify_overrides( overrides: List[Override], ) -> List[Override]: + # this would simplify the overrides by removing those that are overridden later + # in the list. + # e.g. a=1 and later a=10 would remove the first override. lists = [] # NOTE: key -> index of last override with no dict value. (e.g. a=1,2,3) # any override for key before this would be skipped. last_primitive = {} last_dict = {} - # track if a key ends up to be a dict override - is_primitive = {} - is_dict = {} + last_defaults: Dict[str, int] = {} - def check_primitive(x: Any) -> bool: - return isinstance(x, (int, float, bool)) or isinstance(x, QuotedString) + is_defaults: Dict[int, bool] = {} + is_primitive: Dict[int, bool] = {} + has_dict: Dict[int, bool] = {} + + # check value should override earlier ones + # TODO: handle extend_list + def check_write_override(x: Any): + return ( + isinstance(x, (str, int, float, bool, list, QuotedString)) or x is None + ) + + def check_has_dict(x: Any): + return isinstance(x, dict) or is_structured_config(x) for i, override in enumerate(overrides): + if override.config_loader is None: + continue + is_group = len(override.config_loader.get_group_options(override.key_or_group)) > 0 + + key = override.get_key_element() + _write = False + _has_dict = False if override.is_sweep_override(): if override.is_discrete_sweep(): - key = override.get_key_element() - is_primitive[i] = all(override.sweep_iterator(check_primitive)) - is_dict[i] = any( - override.sweep_iterator(lambda x: isinstance(x, dict)) - ) - if is_primitive[i]: - last_primitive[key] = i - if is_dict[i]: - last_dict[key] = i + _write = all(override.sweep_iterator(check_write_override)) + _has_dict = any(override.sweep_iterator(check_has_dict)) else: - key = override.get_key_element() - is_primitive[i] = check_primitive(override.value()) - is_dict[i] = isinstance(override.value(), dict) - if is_primitive[i]: + _write = check_write_override(override.value()) + _has_dict = check_has_dict(override.value()) + + if _write: + if is_group: + is_defaults[i] = True + if override.is_change(): + last_defaults[key] = i + else: + is_primitive[i] = True last_primitive[key] = i - if is_dict[i]: - last_dict[key] = i + if _has_dict: + has_dict[i] = True + last_dict[key] = i for i, override in enumerate(overrides): key = override.get_key_element() @@ -136,7 +156,9 @@ def check_primitive(x: Any) -> bool: last_primitive.get(key, -1) != i or last_dict.get(key, -1) > i ): continue - if is_dict.get(i, False) and last_primitive.get(key, -1) > i: + if has_dict.get(i, False) and last_primitive.get(key, -1) > i: + continue + if is_defaults.get(i, False) and last_defaults.get(key, -1) > i: continue lists.append(override) diff --git a/hydra/core/override_parser/types.py b/hydra/core/override_parser/types.py index f020ccb6c6b..d813bb7edfb 100644 --- a/hydra/core/override_parser/types.py +++ b/hydra/core/override_parser/types.py @@ -274,6 +274,12 @@ class Override: # Configs repo config_loader: Optional[ConfigLoader] = None + def is_change(self) -> bool: + """ + :return: True if this override represents a change of a config value or config group option + """ + return self.type == OverrideType.CHANGE + def is_delete(self) -> bool: """ :return: True if this override represents a deletion of a config value or config group option diff --git a/tests/test_basic_sweeper.py b/tests/test_basic_sweeper.py index 8f0fb534679..47e330218a6 100644 --- a/tests/test_basic_sweeper.py +++ b/tests/test_basic_sweeper.py @@ -6,7 +6,9 @@ from pytest import mark, param +from hydra._internal.config_loader_impl import ConfigLoaderImpl from hydra._internal.core_plugins.basic_sweeper import BasicSweeper +from hydra._internal.utils import create_config_search_path from hydra.core.override_parser.overrides_parser import OverridesParser from hydra.test_utils.test_utils import assert_multiline_regex_search, run_process @@ -104,7 +106,9 @@ def test_split( args: List[str], max_batch_size: Optional[int], expected: List[List[List[str]]] ) -> None: - parser = OverridesParser.create() + + config_loader = ConfigLoaderImpl(config_search_path=create_config_search_path(None)) + parser = OverridesParser.create(config_loader) ret = BasicSweeper.split_arguments( parser.parse_overrides(args), max_batch_size=max_batch_size ) @@ -135,12 +139,13 @@ def test_split( ), param(["a=1,2", "a={x:10},{y:20}", "a=3,4"], ["a=3,4"], id="override_mixed"), param( - ["+a=xx,yy", "+a=[zz]"], ["+a=xx,yy", "+a=[zz]"], id="override_plus_list" + ["+a=xx,yy", "+a=[zz]"], ["+a=[zz]"], id="override_plus_list" ), ], ) def test_simplify(args: List[str], expected: List[str]) -> None: - parser = OverridesParser.create() + config_loader = ConfigLoaderImpl(config_search_path=create_config_search_path(None)) + parser = OverridesParser.create(config_loader) overrides = parser.parse_overrides(args) simplified = BasicSweeper.simplify_overrides(overrides) expected_overrides = parser.parse_overrides(expected) diff --git a/tests/test_examples/test_basic_sweep.py b/tests/test_examples/test_basic_sweep.py index 8e1ac120ea5..1bbf8b2f25e 100644 --- a/tests/test_examples/test_basic_sweep.py +++ b/tests/test_examples/test_basic_sweep.py @@ -37,9 +37,9 @@ dedent( """\ [HYDRA] Launching 2 jobs locally - [HYDRA] \t#0 : db=mysql db.timeout=5 + [HYDRA] \t#0 : db.timeout=5 db=mysql driver=mysql, timeout=5 - [HYDRA] \t#1 : db=mysql db.timeout=10 + [HYDRA] \t#1 : db.timeout=10 db=mysql driver=mysql, timeout=10""" ), ), @@ -48,13 +48,13 @@ dedent( """\ [HYDRA] Launching 4 jobs locally - [HYDRA] \t#0 : db=mysql db.timeout=5 db.user=one + [HYDRA] \t#0 : db.timeout=5 db=mysql db.user=one driver=mysql, timeout=5 - [HYDRA] \t#1 : db=mysql db.timeout=5 db.user=two + [HYDRA] \t#1 : db.timeout=5 db=mysql db.user=two driver=mysql, timeout=5 - [HYDRA] \t#2 : db=mysql db.timeout=10 db.user=one + [HYDRA] \t#2 : db.timeout=10 db=mysql db.user=one driver=mysql, timeout=10 - [HYDRA] \t#3 : db=mysql db.timeout=10 db.user=two + [HYDRA] \t#3 : db.timeout=10 db=mysql db.user=two driver=mysql, timeout=10""" ), ), From 3b8e65d2c01052c65ece6e1c7d95142ed123c1dd Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 20 Dec 2025 19:39:39 +0800 Subject: [PATCH 6/6] fmt --- hydra/_internal/core_plugins/basic_sweeper.py | 4 +++- tests/test_basic_sweeper.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hydra/_internal/core_plugins/basic_sweeper.py b/hydra/_internal/core_plugins/basic_sweeper.py index 093d4e171ff..c10ca0edc45 100644 --- a/hydra/_internal/core_plugins/basic_sweeper.py +++ b/hydra/_internal/core_plugins/basic_sweeper.py @@ -125,7 +125,9 @@ def check_has_dict(x: Any): for i, override in enumerate(overrides): if override.config_loader is None: continue - is_group = len(override.config_loader.get_group_options(override.key_or_group)) > 0 + is_group = ( + len(override.config_loader.get_group_options(override.key_or_group)) > 0 + ) key = override.get_key_element() _write = False diff --git a/tests/test_basic_sweeper.py b/tests/test_basic_sweeper.py index 47e330218a6..1eb9b2d5fba 100644 --- a/tests/test_basic_sweeper.py +++ b/tests/test_basic_sweeper.py @@ -138,9 +138,7 @@ def test_split( id="override_mixed", ), param(["a=1,2", "a={x:10},{y:20}", "a=3,4"], ["a=3,4"], id="override_mixed"), - param( - ["+a=xx,yy", "+a=[zz]"], ["+a=[zz]"], id="override_plus_list" - ), + param(["+a=xx,yy", "+a=[zz]"], ["+a=[zz]"], id="override_plus_list"), ], ) def test_simplify(args: List[str], expected: List[str]) -> None: