swansonk14 / typed-argument-parser

Typed argument parser for Python
MIT License
494 stars 40 forks source link

[Feature request] Nested argument parser, e.g., "--model.name MODEL_NAME --model.params.lambda 0.1". #102

Open CM-BF opened 1 year ago

CM-BF commented 1 year ago

Thank you for your great repo!

I've used this package in nearly all of my projects. Gradually, I found that the most urgent and useful feature will be a nested argument parser. That is:

python my_app.py --arg1.subarg1.subsubarg1 xxx --arg1.subarg2 xxx --arg2.subarg1 xxx

I don't know whether there is a workaround. Or do you have any plans to add this feature?:smile:


Currently, my workaround is similar to this:

class TrainArgs(Tap):
    tr_ctn: bool = None  #: Flag for training continue.
    ctn_epoch: int = None  #: Start epoch for continue training.

class DatasetArgs(Tap):
    dataset_name: str = None  #: Name of the chosen dataset.
    dataloader_name: str = None  #: Name of the chosen dataloader. The default is BaseDataLoader.

class ModelArgs(Tap):
    r"""
    Correspond to ``model`` configs in config files.
    """
    model_name: str = None  #: Name of the chosen GNN.
    model_layer: int = None  #: Number of the GNN layer.

class OODArgs(Tap):
    r"""
    Correspond to ``ood`` configs in config files.
    """
    ood_alg: str = None  #: Name of the chosen OOD algorithm.
    ood_param: float = None  #: OOD algorithms' hyperparameter(s). Currently, most of algorithms use it as a float value.

class AutoArgs(Tap):
    config_root: str = None  #: The root of input configuration files.
    launcher: str = None  #: The launcher name.

class CommonArgs(Tap):
    r"""
    Correspond to general configs in config files.
    """
    config_path: str = None  #: (Required) The path for the config file.
    task: Literal['train', 'test'] = None  #: Running mode. Allowed: 'train' and 'test'.

    # For code auto-complete
    train: TrainArgs = None  #: For code auto-complete
    model: ModelArgs = None  #: For code auto-complete
    dataset: DatasetArgs = None  #: For code auto-complete
    ood: OODArgs = None  #: For code auto-complete

    def process_args(self) -> None:
        super().process_args()

        self.dataset = DatasetArgs().parse_args(args=self.argv, known_only=True)
        self.train = TrainArgs().parse_args(args=self.argv, known_only=True)
        self.model = ModelArgs().parse_args(args=self.argv, known_only=True)
        self.ood = OODArgs().parse_args(args=self.argv, known_only=True)

def args_parser(argv: list=None):
    common_args = CommonArgs(argv=argv).parse_args(args=argv, known_only=True)
    return common_args

Therefore, I can use something like args.model.model_name in my code with autocomplete. But I cannot parse them in a similar way from the argv, e.g., --model.model_name XXX.

swansonk14 commented 1 year ago

Hi @CM-BF,

Thank you for raising this issue! We agree that this would be a really cool feature and would be extremely useful. However, in our understanding, creating an interface like this with argument names such as args.model.model_name would require fundamentally changing how arguments are parsed from the command line. Since we intended Tap to be a typed wrapper of Python's argparse, we believe that this feature is out of the scope for this project. However, we really appreciate your idea, and we hope that you or someone else might be able to fork the project and implement it there!

Best, Jesse and Kyle