Passing a tuple as command line argument
Asked Answered
L

3

44

My requirement is to pass a tuple as command line argument like

--data (1,2,3,4)

I tried to use the argparse module, but if I pass like this it is receiving as the string '(1,2,3,4)'. I tried by giving type=tuple for argparse.add_argument, but is of no use here.

Do I have to add a new type class and pass that to type argument of add_argument?

Update

I tried the ast.literal_eval based on answers. Thanks for that. But it is giving spaces in the result as shown below.

(1,2,3,4)
<type 'str'>
(1, 2, 3, 4)
<type 'tuple'>
Logroll answered 6/11, 2015 at 10:8 Comment(5)
Per the duplicate, ast.literal_eval would be an appropriate type parameterStrunk
Acording to argparse docs, type must be a function (callable) that takes a simple string and converts it to the desired object. tuple('(1,2)') takes a string, but splits into characters, e.g. ('(', '1', ',', '2', ')'). Also beware of your users giving you --data (1, 2, 3,4). The shell splits on whitespace.Alben
The duplicate link has to do with parsing a string like '(1,2)', but does not address the argparse side of the question.Alben
https://mcmap.net/q/369776/-type-dict-in-argparse-add_argument suggests using json.loads to parse strings that look like dicts and lists (but not tuples).Alben
Why not just store the argument as a string and pass it to eval() to convert it to a tuple.Scrogan
W
68

Set nargs of the data argument to nargs="+" (meaning one or more) and type to int, you can then set the arguments like this on the command line:

--data 1 2 3 4

args.data will now be a list of [1, 2, 3, 4].

If you must have a tuple, you can do:

my_tuple = tuple(args.data)

Putting it all together:

parser = argparse.ArgumentParser()
parser.add_argument('--data', nargs='+', type=int)
args = parser.parse_args()
my_tuple = tuple(args.data)
Willettewilley answered 6/11, 2015 at 10:19 Comment(5)
Sorry this is not my requirement. Please see my questions once. I know this approach.Logroll
Change your requirement then ;) Users don't expect to have to format their arguments in a specific way. Using multiple arguments is normal, however.Willettewilley
Alastair, You are correct. Finally I changed my requirement :-).Logroll
and the greatest of all, in my case, is that you can specify lists of lists with nargs="+" or nargs=<int> combined with action="append", thus allowing multiple occurences of --data in the list of arguments. sweet!Alcaeus
@AlastairMcCormack and how do you set defaults like that?Jollenta
W
7

If you want to scale this, not deal with syntax trees, and provide the tuple conversion within the argument, you'll need a custom argparse type. (I needed this since my parser is in a package, and I didn't want to require my users to all write my_tuple = tuple(args.data). With an argument type, you can also receive a string with parenthesis like the original question. Also, I wouldn't include nargs, because that will wrap your custom type output into a list, which basically defeats the purpose of your custom type. I mapped a float because my input was something like "0.9, 0.99", but your int works the same:

def tuple_type(strings):
    strings = strings.replace("(", "").replace(")", "")
    mapped_int = map(int, strings.split(","))
    return tuple(mapped_int)

parser = argparse.ArgumentParser()
parser.add_argument('--data', type=tuple_type)
args = parser.parse_args()

This way you can send in --data "(1,2,3,4)" or --data "1,2,3,4"

Waldheim answered 10/5, 2021 at 16:17 Comment(0)
S
2

I had same requirement. Could not find in standard packages. So, rolled this. Python 3x. Put inside simple argv parser, called one arg at a time:

  • var is passed in default, of correct type.
  • argval is found command line string. Comma separated,no spaces. i.e. 1,2,3 or one,two,three.

Works with bash shell, may need quotes, other adjustments for other shells, OS's. Same technique works non list types. This version creates a homogeneous list or tuple, based on var[0].

example:

./foo.py   --mytuple 1,2,3,4,5

code:

def chk_arg( var, argstr, dbg=False): 
    ### loop on sys.argv here .....
    # if list/tuple type, use list comprehension
    # converts csv list to list or tuple 'type(var)' of 'type(var[0])' items. 
    if type(var) is list or type(var) is tuple:  
        val = type(var)([type(var[0])(i) for i in argval.split(',')]) 
Simian answered 27/1, 2021 at 15:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.