Skip to content

Improving CLI args/option seperation logic to match expected keyword arguments behavior #757

@MilkClouds

Description

@MilkClouds

🚀 Feature request

Currently jsonargparse mark function parameters as arguments or options simply based on default value exists or not.
This behavior does not match community's expectation nowadays. So I suggest to adopt design which determines CLI arg is arg or option by inspect's kind and also default value.

Motivation

Following is design of typer, which is nearly same as jsonargparse. They separate argument and option based on whether default value exists or not.

import typer

def main(
    poskeyword: int,
    *,
    kwarg: str,
    kwarg_default: str = "default",
):
    print(poskeyword, kwarg, kwarg_default)


if __name__ == "__main__":
    typer.run(main)
                                                                                   
 Usage: typer_main.py [OPTIONS] POSKEYWORD KWARG                                   
                                                                                   
╭─ Arguments ─────────────────────────────────────────────────────────────────────╮
│ *    poskeyword      INTEGER  [default: None] [required]                        │
│ *    kwarg           TEXT     [default: None] [required]                        │
╰─────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ───────────────────────────────────────────────────────────────────────╮
│ --kwarg-default        TEXT  [default: default]                                 │
│ --help                       Show this message and exit.                        │
╰─────────────────────────────────────────────────────────────────────────────────╯

here we can check that kwarg, which is keyword argument and which user expected to be an option, is treated as arguments, not options.
Authors of https://github.com/BrianPugh/cyclopts argue that it is relic design, does not fit into modern python standards. You can check more details in https://cyclopts.readthedocs.io/en/latest/vs_typer/argument_vs_option/README.html

for same example as above cyclopts prints out following. They treat inspect's kind carefully and (1) let --poskeyword possible, (2) make --kwargs only possible.

Usage: main [ARGS] [OPTIONS]

╭─ Commands ──────────────────────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                                       │
│ --version  Display application version.                                         │
╰─────────────────────────────────────────────────────────────────────────────────╯
╭─ Parameters ────────────────────────────────────────────────────────────────────╮
│ *  POSKEYWORD --poskeyword  [required]                                          │
│ *  --kwarg                  [required]                                          │
│    --kwarg-default          [default: default]                                  │
╰─────────────────────────────────────────────────────────────────────────────────╯

And it's bit more minor, less-influential one. typer can't deal with positional only argument. And it's same for jsonargparse.

import typer
def main(poskeyword: int, /):
    print(poskeyword)

if __name__ == "__main__":
    typer.run(main)
TypeError: main() got some positional-only arguments passed as keyword arguments: 
'poskeyword'

Of course, cyclopts can deal with this case well.

Usage: main [ARGS]

╭─ Commands ──────────────────────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                                       │
│ --version  Display application version.                                         │
╰─────────────────────────────────────────────────────────────────────────────────╯
╭─ Arguments ─────────────────────────────────────────────────────────────────────╮
│ *  POSONLY  [required]                                                          │
╰─────────────────────────────────────────────────────────────────────────────────╯

Pitch

To introduce what I want in detail with example, I created PR #756. Have a look and give me any review in free time.

Alternatives

none.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions