lebrice / SimpleParsing

Simple, Elegant, Typed Argument Parsing with argparse
MIT License
428 stars 52 forks source link

List[int] parsed as a single value #63

Closed alexanderlobov closed 3 years ago

alexanderlobov commented 3 years ago

Describe the bug List[int] parsed as a single value.

To Reproduce

from dataclasses import dataclass, field
from simple_parsing import ArgumentParser
from typing import List

@dataclass
class Opts:
    int_list: List[int] = field(default_factory=[1,2].copy)

parser = ArgumentParser()
parser.add_arguments(Opts, dest="opts")

def test(arg_string):
    args = parser.parse_args(arg_string.split())
    opts: Opts = args.opts
    print(opts)
    print(type(opts.int_list))

# This gives an expected result:
#
#     Opts(int_list=[1, 2])
#     <class 'list'>

test("")

# This fails with: 
#
#     usage: test_list_single.py [-h] [--int_list int]
#     test_list_single.py: error: argument --int_list: invalid int value: '[1,2]'

test("--int_list [1,2]")

Expected behavior A clear and concise description of what you expected to happen.

$ python test_list_single.py
Opts(int_list=[1, 2])
<class 'list'>
Opts(int_list=[1, 2])
<class 'list'>

Actual behavior

$ python test_list_single.py
Opts(int_list=[1, 2])
<class 'list'>
usage: test_list_single.py [-h] [--int_list int]
test_list_single.py: error: argument --int_list: invalid int value: '[1,2]'

Desktop (please complete the following information):

Additional context The parsing works correct if I add second instance of the Opts to the arguments and use ConflictResolution.ALWAYS_MERGE:

from dataclasses import dataclass, field
from simple_parsing import ArgumentParser, ConflictResolution
from simple_parsing.helpers import list_field
from typing import List

@dataclass
class Opts:
    int_list: List[int] = field(default_factory=[1,2].copy)

parser = ArgumentParser(conflict_resolution=ConflictResolution.ALWAYS_MERGE)
parser.add_arguments(Opts, dest="opts1", default=Opts())
parser.add_arguments(Opts, dest="opts2", default=Opts())

# print(parser.equivalent_argparse_code())

def test(arg_string):
    args = parser.parse_args(arg_string.split())
    opts: Opts = args.opts1
    print(opts)
    print(type(opts.int_list))

# This gives an expected result:
#
#     Opts(int_list=[1, 2])
#     <class 'list'>

test("")

# This gives an expected result:
#
#     Opts(int_list=[1, 2])
#     <class 'list'>

test("--int_list [1,2]")
alexanderlobov commented 3 years ago

Seems to be related to #62

lebrice commented 3 years ago

Thanks fo rposting @alexanderlobov , I'll take a good look at this this week and get back to you.

lebrice commented 3 years ago

Ok @alexanderlobov Sorry I forgot to get back to you. This is expected behaviour from argparse: Lists should be passed using spaces as the separator. Passing in brackets doesn't work. For example: python main.py --int_list 1 2 will give you what you want. Closing this, feel free to reopen if needed.