lebrice / SimpleParsing

Simple, Elegant, Typed Argument Parsing with argparse
MIT License
386 stars 47 forks source link

Default / Blank destination for add_arguments #174

Closed timbmg closed 1 year ago

timbmg commented 1 year ago

Is it possible to add the parsed arguments the the args object directly? I.e. I would like to access the arguments in the below example with args.var. dest currently is a required parameter, so leaving it empty does not work.

from dataclasses import dataclass
from simple_parsing import ArgumentParser

@dataclass
class Options:
    var: str = "value"

parser = ArgumentParser()
args = parser.add_arguments(Options, dest="options")
print(args.var)
lebrice commented 1 year ago

Hello there @timbmg , thanks for posting!

A few questions first, just so I understand your use-case better:

  1. Are you using only one dataclass ?

--> If yes: you could use the the simple_parsing.parse API, which essentially uses the nested_mode=NestedMode.WITHOUT_ROOT argument to the ArgumentParser constructor under the hood.

import simple_parsing
from dataclasses import dataclass

@dataclass
class Options:
    var: str = "value"

options: Options = simple_parsing.parse(Options)

--> If you're using more than one dataclass, then I'm not 100% sure this fits your use-case, but here's something that might be useful (assuming you're okay with using a single over-arching dataclass called Args):

import simple_parsing
from simple_parsing.helpers.flatten import FlattenedAccess
from simple_parsing import parse
from dataclasses import dataclass, field

@dataclass
class Options:
    var: str = "value"

@dataclass
class OtherStuff:
    foo: int = 123

@dataclass
class Args(FlattenedAccess):
    options: Options = field(default_factory=Options)
    other: OtherStuff = field(default_factory=OtherStuff)

args: Args = simple_parsing.parse(Args) 
assert args.var is args.options.var
assert args.foo is args.other.foo

As you can see, this FlattenedAccess gives you a "flat" access to nested fields. It was created specifically for the use-case where you have some code that depends on everything being on the args object. With this kind of setup, you can gradually transition from using a single args to using a different dataclass for each argument group. The only thing is, if you access the nested fields on the args object directly, you won't get the nice type-checking benefits, but the code will work.

  1. Are you mixing dataclasses and regular argparse-style arguments? (i.e. parser.add_argument and parser.add_arguments?)

--> If yes, then I'd suggest converting the argparse args to a dataclass and trying the solution above. I can't think of a better solution atm, unfortunately. --> If not, then no problem, solution above might work.

Let me know what you think!

timbmg commented 1 year ago

Thank you @lebrice for such a detailed answer!

I am currently transitioning from vanilla argpase, so I didn't want to change a bunch of code. I will try the parse API. Thanks again! 🤗

alexmlong commented 1 year ago

Hey @lebrice ! Sorry to resurrect this thread but I'm a bit confused by the parse API you described in use-case 1 above. Can you continue the example to explain what you would do with the options variable next?

I was expecting it would go something like

options: Options = simple_parsing.parse(Options)
args = parser.add_argument(Options)

But I don't see a way to do that. Can you share the details on what to do with this options object in order to add it without using dest? Thanks!

alexmlong commented 1 year ago

@timbmg Hey there, can you share how you figured this out? I still don't see a way to add the arguments to the args object directly. It seems like we still are required to specify dest.

timbmg commented 1 year ago

Hey @alexmlong, I am actually just using parse_args (or actually parse_known_args). So something like this:

from dataclasses import dataclass
import simple_parsing

@dataclass
class Args:
    foo: str

args, _ = simple_parsing.parse_known_args(Args)

Hope this helps!

alexmlong commented 1 year ago

Got it, thanks!