argh icon indicating copy to clipboard operation
argh copied to clipboard

Get list of characters instead of strings when using nargs='+' and typing hints

Open BrendanSimon opened this issue 5 years ago • 1 comments

How to specify a list of "arguments" with typing hints ?

import argh
from typing import List

@argh.arg( '-i', '--input_files', nargs='+', help='list of input files' )
def main ( input_files : List[ str ] = list() ) -> None :
    print( f"input_files = {input_files}" )

Output:

$ python test_argh.py -i xyz abc
input_files = [['x', 'y', 'z'], ['a', 'b', 'c']]

but I expect:

$ python test_argh.py -i xyz abc
input_files = ['xyz', 'abc']

I can get the expected results if I change the code to:

@argh.arg( '-i', '--input_files', nargs='+', help='list of input files' )
def test ( input_files : str = '???' ) -> None :
    print( f"input_files = {input_files}" )

But then that says the variable input_files is of type str, when really it is a list of str.

Is this a bug or am I using argh incorrectly ?

NOTE: using Python 3.7.7 and argh 0.26.2

BrendanSimon avatar May 17 '20 03:05 BrendanSimon

I have this problem too. Have to set type=str in @arg.

jplehmann avatar Jan 08 '22 17:01 jplehmann

Thanks for the question! Not that I think it's still relevant to @BrendanSimon, but who knows? :) Also someone may stumble upon this issue while having a similar problem.

The problem is not in the typing hints (Argh still doesn't use them to infer types) but in the default value. As it's an instance of list, we get the call parser.add_argument(..., type=list).

In general, if the function has a chance to be used in a different context (not just a one-time CLI command), you really don't want to have a mutable object as a default value for an argument. Even if you know it's safe here, your linter will probably explode.

This is what you probably want — the argument must be present but may not have any value and then you get an empty list:

@argh.arg("-i", "--input-files", nargs=argparse.ZERO_OR_MORE)
def test(*, input_files: list[str]) -> str:
    return f"input files: {input_files}"

Please note that this requires Argh v.0.30+ with its new name mapping policy.

This example demonstrates a useful edge case. I'll make sure that continues to work after Argh actually switches to typing hints (which is going to happen very soon).

Thank you!

neithere avatar Oct 21 '23 22:10 neithere