Describe the bug
flag with explict negative_option cannot resolve conflict when argument_generation_mode=ArgumentGenerationMode.NESTED mode.
To Reproduce
from dataclasses import dataclass
from simple_parsing import ArgumentParser, ArgumentGenerationMode, flag
@dataclass
class BaseConfig:
foo: int = 1
@dataclass
class QuantizerConfig:
asym: bool = flag(default=False, negative_option="sym")
if __name__ == "__main__":
parser1 = ArgumentParser()
parser1.add_arguments(BaseConfig, "base")
parser1.add_arguments(QuantizerConfig, "w")
parser1.add_arguments(QuantizerConfig, "a")
print("parser1:")
parser1.print_help()
parser2 = ArgumentParser(argument_generation_mode=ArgumentGenerationMode.NESTED)
parser2.add_arguments(BaseConfig, "base")
parser2.add_arguments(QuantizerConfig, "w")
parser2.add_arguments(QuantizerConfig, "a")
print("parser2:")
parser2.print_help()
Expected behavior
Like parser1, parser2 should resolve conflicts in QuantizerConfig .syminto w.sym and a.sym.
$ python issue.py
parser1:
usage: issue.py [-h] [--foo int] [--w.asym bool] [--a.asym bool]
options:
-h, --help show this help message and exit
BaseConfig ['base']:
BaseConfig(foo: int = 1)
--foo int (default: 1)
QuantizerConfig ['w']:
QuantizerConfig(asym: bool = False)
--w.asym bool, --w.sym bool
(default: False)
QuantizerConfig ['a']:
QuantizerConfig(asym: bool = False)
--a.asym bool, --a.sym bool
(default: False)
parser2:
usage: issue.py [-h] [--foo int] [--w.asym bool] [--a.asym bool]
options:
-h, --help show this help message and exit
BaseConfig ['base']:
BaseConfig(foo: int = 1)
--base.foo int (default: 1)
QuantizerConfig ['w']:
QuantizerConfig(asym: bool = False)
--w.asym bool, --w.sym bool
(default: False)
QuantizerConfig ['a']:
QuantizerConfig(asym: bool = False)
--a.asym bool, --a.sym bool
(default: False)
Actual behavior
parser2 cannot resolve conflict on QuantizerConfig.sym into w.sym and a.sym.
$ python issue.py
parser1:
usage: issue.py [-h] [--foo int] [--w.asym bool] [--a.asym bool]
options:
-h, --help show this help message and exit
BaseConfig ['base']:
BaseConfig(foo: int = 1)
--foo int (default: 1)
QuantizerConfig ['w']:
QuantizerConfig(asym: bool = False)
--w.asym bool, --w.sym bool
(default: False)
QuantizerConfig ['a']:
QuantizerConfig(asym: bool = False)
--a.asym bool, --a.sym bool
(default: False)
parser2:
Traceback (most recent call last):
File "/home/user/reproduce/issue.py", line 28, in <module>
parser2.print_help()
File "/home/user/reproduce/.venv/lib/python3.12/site-packages/simple_parsing/parsing.py", line 382, in print_help
self._preprocessing(args=list(args) if args else [])
File "/home/user/reproduce/.venv/lib/python3.12/site-packages/simple_parsing/parsing.py", line 550, in _preprocessing
wrapped_dataclass.add_arguments(parser=self)
File "/home/user/reproduce/.venv/lib/python3.12/site-packages/simple_parsing/wrappers/dataclass_wrapper.py", line 214, in add_arguments
_ = group.add_argument(*wrapped_field.option_strings, **arg_options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/argparse.py", line 1500, in add_argument
return self._add_action(action)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/argparse.py", line 1705, in _add_action
action = super(_ArgumentGroup, self)._add_action(action)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/argparse.py", line 1514, in _add_action
self._check_conflict(action)
File "/home/user/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/argparse.py", line 1654, in _check_conflict
conflict_handler(action, confl_optionals)
File "/home/user/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/argparse.py", line 1663, in _handle_conflict_error
raise ArgumentError(action, message % conflict_string)
argparse.ArgumentError: argument --a.asym/--sym: conflicting option string: --sym
Desktop (please complete the following information):
- Version 0.1.8
- Python version: 3.12.13
Additional context
Through simple debugging, I confirmed that in argument_generation_mode=ArgumentGenerationMode.FLAT mode, the initial conflict occurring at https://github.com/lebrice/SimpleParsing/blob/3b5bc2377817ee8ee4e5f12709b2db173320b3c1/simple_parsing/parsing. py#L534 sets field_wrapper.prefix as the root path, and that the conflict with negative_option is resolved via arg_options["_conflict_prefix"].
However, in argument_generation_mode=ArgumentGenerationMode.NESTED mode, this process is omitted because there is no conflict between field_wrapper.option_strings from the outset, and it appears that no prefix is added to negative_option.
Describe the bug
flagwith explictnegative_optioncannot resolve conflict whenargument_generation_mode=ArgumentGenerationMode.NESTEDmode.To Reproduce
Expected behavior
Like
parser1,parser2should resolve conflicts inQuantizerConfig .symintow.symanda.sym.Actual behavior
parser2cannot resolve conflict onQuantizerConfig.symintow.symanda.sym.Desktop (please complete the following information):
Additional context
Through simple debugging, I confirmed that in
argument_generation_mode=ArgumentGenerationMode.FLATmode, the initial conflict occurring at https://github.com/lebrice/SimpleParsing/blob/3b5bc2377817ee8ee4e5f12709b2db173320b3c1/simple_parsing/parsing. py#L534 setsfield_wrapper.prefixas the root path, and that the conflict withnegative_optionis resolved viaarg_options["_conflict_prefix"].However, in
argument_generation_mode=ArgumentGenerationMode.NESTEDmode, this process is omitted because there is no conflict betweenfield_wrapper.option_stringsfrom the outset, and it appears that no prefix is added tonegative_option.