5

How I can do conditional loop with argparse with variable nargs? So, basically, it should run with or without argument. I am trying:

parser = argparse.ArgumentParser(description="output parser")
group = parser.add_mutually_exclusive_group()
group.add_argument("--dos", help="get DOSCAR for plot",
                   nargs="?", metavar=("int"))
args = parser.parse_args()

if args.dos:    
    if len(args.dos) > 1:
        chosen = int(args.dos[0])
        chdos = "at_dos"+args.dos[0]+".dat"
    else:
        chosen = None

    inpt = "DOSY"
    print(chosen)
    print(inpt)

Now, if I have variable, then its printing some value, wrong but some value:

$python3 vasp.py --dos 111
111
None   # IT SHOULDN'T BE NONE
DOSY

but nothing without argument.

I have also tried with the normal sys.argv, as:

def get_dos():
    if len(sys.argv) > 2:
        chosen = int(sys.argv[2])
        chdos = "at_dos"+sys.argv[2]+".dat"
    else:
        chosen = None
    inpt = "DOSCAR"
    print(sys.argv)

    print(args.dos)
    print(chosen)
    print(inpt)

in this case, when option is present, its giving correct result:

python3 vasp.py --dos 12
['vasp.py', '--dos', '12']
12
12
DOSCAR

but again, nothing without option:

$python3 vasp.py --dos

I have tried with hpaulj's suggestion. It gives:

$python3 tt.py --dos 12
Namespace(dos='12')
1
DOSY

and without argument, its still not printing anything.

7
  • I don't get that None when I run it and fix the invalid syntax. What version of Python are you using? I've tried both 3.5.1 and 2.7.11 and it works in both versions. You need a minimal reproducible example. Commented Jun 29, 2016 at 20:40
  • 1
    Also, I don't think len(args.dos) > 1 does what you think it does. It checks if you've entered a number with more than 1 digit. Commented Jun 29, 2016 at 20:43
  • 2
    Did you try print(args) to see directly what was parsed? Commented Jun 29, 2016 at 20:55
  • You now need a test that can handle the string '12', not a list or number. A string. Commented Jun 29, 2016 at 21:16
  • What is is supposed to do with just --dos and without it at all? Commented Jun 29, 2016 at 21:22

3 Answers 3

4

Simplifying your parser in a Ipython session:

In [1004]: parser=argparse.ArgumentParser()    
In [1005]: parser.add_argument('--dos', nargs='?')

In [1007]: parser.parse_args('--dos 111'.split())
Out[1007]: Namespace(dos='111')

In this case args.dos will be the string '111', with len 3, and int(args.dos[0]) the number 1. The same happens in you leave nargs blank (default None).

With nargs='?' I can also use the flag without an argument, in which case the value is the default None.

In [1013]: parser.parse_args('--dos'.split())
Out[1013]: Namespace(dos=None)

nargs=?is most useful with aconst, which gives a convenient 3 way action. I can addtype=int` to convert the string, if any to integer.

In [1015]: parser.add_argument('--dos', nargs='?', type=int,
   default=None, const=123)

In [1016]: parser.parse_args([]) # not used
Out[1016]: Namespace(dos=None)

In [1017]: parser.parse_args('--dos'.split())  # without argument
Out[1017]: Namespace(dos=123)

In [1018]: parser.parse_args('--dos 456'.split())  # with argument
Out[1018]: Namespace(dos=456)

Other nargs, like 1, '*' and '+' give you a list, which you can check for length etc.

====================

In your argv testing

if len(sys.argv) > 2:
    chosen = int(sys.argv[2])
    chdos = "at_dos"+sys.argv[2]+".dat"

sys.argv is a list, so the len counts if there are enough elements to apply the sys.argv[2] step.

This does not work because args.dos is a single string, not a list.

if len(args.dos) > 1:
    chosen = int(args.dos[0])
    chdos = "at_dos"+args.dos[0]+".dat"

len(args.dos) is then the number of characters in the string, and args.dos[0] is the first character.

============

If I define:

def get_dos(argv=None):
    parser=argparse.ArgumentParser()
    parser.add_argument('--dos', type=int, nargs='?')
    args = parser.parse_args(argv)
    chosen = args.dos
    if chosen is not None:
        chdos = 'at_dos%s.dat'%chosen
    else:
        chdos = ''
    return chosen, chdos

these tests produce values which I think match your needs:

In [1042]: get_dos([])
Out[1042]: (None, '')

In [1043]: get_dos(['--dos'])
Out[1043]: (None, '')

In [1044]: get_dos(['--dos','123'])
Out[1044]: (123, 'at_dos123.dat')
Sign up to request clarification or add additional context in comments.

Comments

0

You need to replace nargs = "?" with nargs = "*". Setting nargs to "?" means there will be 1 optional argument. What you want is a list of optional arguments, which is "*". Like so.

import argparse
parser = argparse.ArgumentParser(description="output parser")
group = parser.add_mutually_exclusive_group()
group.add_argument("--dos", help="get DOSCAR for plot",
               nargs="*", metavar=("int"))
args = parser.parse_args()

if args.dos:    
    if len(args.dos) > 1:
        chosen = int(args.dos[0])
        chdos = "at_dos"+args.dos[0]+".dat"
    else:
        chosen = None

    inpt = "DOSY"
    print(chosen)
    print(inpt)

1 Comment

I have already tried nargs = "*". Have you checked the code? its giving same problem.
0

thanks to all the people who replied, and specially @hpaulj. But none of them really solve my problem. So, I took other way. I am posting this here for completeness.

#!/usr/bin/python3
import argparse

parser = argparse.ArgumentParser(description="output parser")
group = parser.add_mutually_exclusive_group()
group.add_argument("--dos", help="get DOSCAR for plot",action='store_true')
parser.add_argument("-n", help="Showing last n line",
                    metavar='integer', type=int)

args = parser.parse_args()
if args.dos:
    if args.n:
        chosen = int(args.n)
        chdos = "at_dos"+str(args.n)+".dat"
    else:
        chosen = None
    inpt = "DOSY"
    print(inpt)
    print(chosen)

which yields expected results properly:

$python3 tt.py --dos
DOSY
None

$python3 tt.py --dos -n 222
DOSY
222

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.