argparse: nargs with multiple flags in one dash? - python

If I try running python test.py -bf with the below code, I get bar=f and foo=foo1 instead of the desired result (bar=bar1 and foo=foo1). How would I achieve the desired result?
import argparse
ap = argparse.ArgumentParser(description='test')
ap.add_argument('--bar', '-b', nargs='?', const='bar1')
ap.add_argument('--foo', '-f', nargs='?', const='foo1')
args = ap.parse_args()

Related

Argparse with OR logic on arguments

I am coding an argument parser for my script:
import argparse
parser = argparse.ArgumentParser(description='My parser.')
parser.add_argument('path',
type=str)
parser.add_argument('-a',
'--all',
action='store_true')
parser.add_argument('-t',
'--type',
type=str)
parser.add_argument('-d',
'--date',
type=str)
This is the logic I want to implement:
path: must be provided always.
--all: if it is provided, the --type and --date should not appear.
--type and --date: must be provided only if the --all flag is not introduced.
The command would look something like this:
python myscript.py mypath [-a] OR [-t mytype -d mydate]
How can I implement this logic?
You can do something like this:
from argparse import ArgumentParser
parser = ArgumentParser(description='My parser.')
parser.add_argument('path',
type=str)
parser.add_argument('-a',
'--all',
action='store_true')
parser.add_argument('-t',
'--type',
type=str)
parser.add_argument('-d',
'--date',
type=str)
args = parser.parse_args()
if args.all:
print('all argument flow')
else:
if not args.type or not args.date:
print('you need to put either all or specify both type and date')
else:
print(args.type, args.date)
print('and',args.path)

Nargs='+' capturing too many arguments python

I am using argparse for command line arguments, where one argument is one or more .csv files:
parser = argparse.ArgumentParser(description='...')
parser.add_argument('csv', type=str, nargs='+', help='.csv file(s)'))
parser.add_argument('days', type=int, help='Number of days'))
args = parser.parse_args()
print(args.csv)
When running $ python Filename.py csv Filename.csv days 30, args.csv is ['csv', 'Filename.csv', 'days'], but I don't want to be capturing the csv and days arguments which surround the input csv files.
You can use
import argparse
parser = argparse.ArgumentParser(description='...')
parser.add_argument("csv", type=str, nargs='+', help='.csv file(s)')
parser.add_argument("days", type=int, help='Number of days')
args = parser.parse_args()
print(args.csv)
Invoked with e.g. python your_script.py test.csv 100 this yields
['test.csv']
Note that you do not need to encapsulate parser.add_argument(...) twice (as you had in your code).

Using stdin in a mutually exclusive argument group

I'm trying to use argparse in a script such that I could pass a string either as an argument or via standard input. I figured I could use add_mutually_exclusive_group for that, and set required=True to enforce that just one argument is required. So, I created the following script (my_script.py):
#!/usr/bin/env python
import argparse
from sys import stdin
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('str_arg', nargs='?', type=str)
group.add_argument('str_in', nargs='?', type=argparse.FileType('r'), default=stdin)
args = parser.parse_args()
print args.str_arg or args.str_in.readline()
Passing the string as a parameter works fine. However, when I try to pipe the string from standard input like so:
$ echo Hello | python my_script.py
Python complains that one of the arguments str_arg str_in is required. What am I doing wrong? Is there a better way of achieving this?
It doesn't seem like you should try doing this with fancy argparse features, just some simple logic:
import argparse
from sys import stdin
parser = argparse.ArgumentParser()
parser.add_argument('str_arg', nargs='?', type=str)
args = parser.parse_args()
print(args.str_arg or stdin.readline())
2 positionals in a mutually exclusive group does not work.
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('str_arg', nargs='?')
group.add_argument('str_in', nargs='?')
args = parser.parse_args()
print(args)
sample runs:
0217:~/mypy$ python3 stack49705916.py
usage: stack49705916.py [-h] (str_arg | str_in)
stack49705916.py: error: one of the arguments str_arg str_in is required
0904:~/mypy$ python3 stack49705916.py foo
Namespace(str_arg='foo', str_in=None)
0904:~/mypy$ python3 stack49705916.py foo bar
usage: stack49705916.py [-h] (str_arg | str_in)
stack49705916.py: error: argument str_in: not allowed with argument str_arg
piping is not a substitute for a commandline argument. stdin has to be read separately.
import argparse, sys
print(sys.argv)
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('str_arg', nargs='?')
group.add_argument('str_in', nargs='?')
args = parser.parse_args()
print(args)
print(sys.stdin.read())
0909:~/mypy$ echo 'hello' | python3 stack49705916.py foo
['stack49705916.py', 'foo']
Namespace(str_arg='foo', str_in=None)
hello
argparse.FileType('r') recognizes - as stdin.
print(sys.argv)
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--str_in', type=argparse.FileType('r'))
args = parser.parse_args()
print(args)
print(args.str_in.read())
runs
0945:~/mypy$ python3 stack49705916.py -i test.txt
['stack49705916.py', '-i', 'test.txt']
Namespace(str_in=<_io.TextIOWrapper name='test.txt' mode='r' encoding='UTF-8'>)
2000+0
2001+2
2002+1
0946:~/mypy$ python3 stack49705916.py -i -
['stack49705916.py', '-i', '-']
Namespace(str_in=<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>)
typing hello on input line
typing hello on input line
0947:~/mypy$ echo Hello | python3 stack49705916.py -i -
['stack49705916.py', '-i', '-']
Namespace(str_in=<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>)
Hello
0947:~/mypy$
In a shell you can pass the output of echo Whatever as an argument using back quotes and not a pipe:
$ python my_script.py `echo whatever`

Can't figure out how to pass arguments to argparse

I'm going through the docs on the argparse module and I can't figure out how to get the same results as explained in the docs. You can either specify the arguments on the command-line, or it allows you to specify them within parse_args() which is helpful for testing. Here is an example:
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.parse_args('--foo 1'.split())
That is directly from the docs here:
https://docs.python.org/3.6/library/argparse.html#action
It is supposed to output this:
Namespace(foo='1')
But this is what I get:
Namespace(foo=None)
I also tried the following:
parser = argparse.ArgumentParser()
parser.add_argument('--foo', action='store_true')
parser.add_argument('--bar', action='store_false')
parser.add_argument('--baz', action='store_false')
parser.parse_args(['--foo', '--bar'])
And that one outputs this:
Namespace(bar=True, baz=True, foo=False)
which is what it's supposed to do. Can anyone tell me what's going on here? Here is my full code that I used to generate the output for both snippets of code shown above:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.parse_args('--foo 1'.split())
args = parser.parse_args()
print(args)
#supposed to be Namespace(foo='1')
parser = argparse.ArgumentParser()
parser.add_argument('--foo', action='store_true')
parser.add_argument('--bar', action='store_false')
parser.add_argument('--baz', action='store_false')
parser.parse_args(['--foo', '--bar'])
args = parser.parse_args()
print(args)
#supposed to be Namespace(foo=True, bar=False, baz=True)
I don't know if it makes a difference, but I'm doing this in Spyder 3.1.4 and I'm running Python 3.6.0
UPDATE
Due to some ambiguity in the docs I didn't know how they got from setting the command-line arguments to displaying the output. With the help of #hpaulj I realized all I was doing was displaying the output relative to arg.sysv instead of the specified custom command-line - oops! Here's the corrected code:
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
#added this assignment to args
args = parser.parse_args('--foo 1'.split())
#following line was wrong - removing
#args = parser.parse_args()
print(args)
#supposed to be Namespace(foo='1')
parser = argparse.ArgumentParser()
parser.add_argument('--foo', action='store_true')
parser.add_argument('--bar', action='store_false')
parser.add_argument('--baz', action='store_false')
#added this assignment to args
args = parser.parse_args(['--foo', '--bar'])
#following line was wrong - removing
#args = parser.parse_args()
print(args)
#supposed to be Namespace(foo=True, bar=False, baz=True)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
args = parser.parse_args('--foo 1'.split())
print(args)
This will give you your answer. You are trying to parse again thats why it was giving None

Using argparse with a specified acceptable option

I am trying to use argparse to accept required command line options. I have defined a function like so
def get_args():
parser = argparse.ArgumentParser(description='Help Desk Calendar Tool')
parser.add_argument('-s', '--start', type=str, required=True, metavar='YYYY-MM-DD')
parser.add_argument('-e','--end', type=str, required=True, metavar='YYYY-MM-DD')
parser.add_argument('-m','--mode', type=str, required=True , metavar='add|del')
args = parser.parse_args()
start = args.start
end = args.end
mode = args.mode
return start,end,mode
What I am trying to do is for the option --mode I would like it to ONLY accept an parameter of either add or del. I could do this from an if statement but was wondering if argparse has a built in way of accomplishing this task. I looked at nargs but wasn't too clear if that's the path I need to go down
I think you are asking about choices:
parser.add_argument('-m','--mode', type=str, required=True, choices=['add', 'del'])
Demo:
$ python test.py -s 10 -e 20 -m invalid
usage: test.py [-h] -m {add,del}
test.py: error: argument -m/--mode: invalid choice: 'invalid' (choose from 'add', 'del')

Categories