swansonk14 / typed-argument-parser

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

add_argument should not overwrite properties defined in class member. #131

Open oren-hecht opened 7 months ago

oren-hecht commented 7 months ago

There is a very common practice in scripts, to allow both "long syntax" and shorter versions of some argument name. [The documentation] (https://github.com/swansonk14/typed-argument-parser?tab=readme-ov-file#adding-special-argument-behavior) suggests using add_argument to achieve this:

from tap import Tap

class MyTap(Tap):
    argument_with_really_long_name: int

    def configure(self):
        self.add_argument('-arg', '--argument_with_really_long_name')

However, In the case where I had defined properties on this argument, the operation will overwrite these

from tap import Tap

class MyTap(Tap):
    argument_with_really_long_name: int = 5  # A description for this long argument

    def configure(self):
        self.add_argument('-arg', '--argument_with_really_long_name')

In this case, the default value and the help description will be lost. In my project, we have many such arguments, sometimes defined with more complex properties such as list[Literal] types - it is a shame that the second we want to allow a shorter argument name, we lose all the powerful Tap features that allow defining argument properties. An example from our code:

from typing import Literal, get_args
from tap import Tap

DebugOptionsType = Literal["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"]

class CloudDebuggerOptions(Tap):
    aws_profile: str = "default"  # The AWS profile to use
    debug_levels: list[DebugOptionsType] = []  # A list of debug messages to receive

I want to allow shorter names here - and so have to add all the following lines

    def configure(self) -> None:
        self.add_argument("-a", "--aws-profile", dest="aws_profile", default="default", help="The AWS profile to use")
        self.add_argument(
            "-d",
            "--debug-levels",
            dest="debug_levels",
            nargs="+",
            choices=get_args(DebugOptionsType),
            default=[],
            help="A list of debug messages to receive",
        )

by the way, the same problem is present if we want to allow the CLI options to be in kebab-case (the linux standard) as opposed to snake_case, as can be seen above (since you cannot define class member names using kebab-case in python).

PROPOSAL

Only overwrite the specific add_argument options being used, and take the rest from the class member. Or, possible, make some simpler syntax that allows assigning an alternative argument name

martinjm97 commented 6 months ago

Hi @oren-hecht,

Thank you for raising this issue! Both of your suggestion make sense.

The first change would be to keep all of the information specified by the class variable and then have configure extend/overwrite it. We agree this would help eliminate redundancy.

Additionally, perhaps we could leverage Annotated to specify shortened arg names. For example, argument_with_really_long_name: Annotated[int, short_name=arg].

Thank you again for these ideas! We welcome any contributions.

Best, JK