Python argparse.ArgumentParser cannot differentiate between `--modes` and `--mode` - python

In this example script
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--modes', help="test", nargs='+', type=str)
args = parser.parse_args()
write_mode = args.modes
print(write_mode)
There is only one argument modes.
However, python test.py --modes sdfsf and python test.py --mode sdfsf give me the same output (['sdfsf']) which means the parser treats mode as modes.
Is this a bug of argparse?

This is an example of prefix matching, which is allowed by argparse by default.
Turn it off by using argparse.ArgumentParser(..., allow_abbrev=False).

Related

argparse in Python

I came across this Python script:
parser = ap.ArgumentParser()
parser.add_argument("-t", "--trainingSet", help="Path to Training Set", required="True")
args = vars(parser.parse_args())
train_path = args["trainingSet"]
The points I didn't get are:
How do we use those arguments in the command line: "-t", "--trainingSet", help="Path to Training Set", required="True"?
What does args mean? How was the training path retrieved?
Thanks.
Create a parser object:
parser = ap.ArgumentParser()
add an argument definition to the parser (it creates an Action object, though you don't need to worry about that here).
parser.add_argument("-t", "--trainingSet", help="Path to Training Set", required="True")
Tell the parser to parse the commandline arguments that are available in sys.argv. This a list of strings created by the commandline shell (bash or dos).
args = parser.parse_args()
args is a argparse.Namespace object. It is a simple object class. vars converts it to a dictionary
argdict = vars(args)
This is ordinary dictionary access
train_path = argdict["trainingSet"]
you can get the same thing from the namespace
train_path = args.trainingSet
I'd recommend looking at args
print args
With this parser definition, a commandline like
$ python myprog.py -t astring # or
$ python myprog.py --trainingSet anotherstring
will end up setting train_path to the respective string value. It is up to the rest of your code to use that value.
The help parameter will show up in the help message, such as when you do
$ python myprog.py -h
The required parameter means that the parser will raise an error if you don't provide this argument, e.g.
$ python myprog.py

How to use top-level arguments with subparsers in argparse

In Python's argparse, how do you implement top-level arguments while still using commands implemented as subparsers?
I'm trying to implement a --version argument to show the program's version number, but argparse is giving me error: too few arguments because I'm not specifying a sub-command for one of the subparsers.
My code:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
'-v', '--version',
help='Show version.',
action='store_true',
default=False
)
subparsers = parser.add_subparsers(
dest="command",
)
list_parser = subparsers.add_parser('list')
parser.parse_args(['--version'])
the output:
usage: myscript.py [-h] [-v] {list} ...
myscript.py: error: too few arguments
If you only need version to work, you can do this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
'-v', '--version',
action='version',
version='%(prog)s 1.0',
)
Subparsers won't bother any more; the special version action is processed and exits the script before the parser looks for subcommands.
The subparsers is a kind of positional argument. So normally that's required (just as though you'd specified add_argument('foo')).
skyline's suggestion works because action='version' is an action class that exits after displaying its information, just like the default -h.
There is bug/feature in the latest argparse that makes subparsers optional. Depending on how that is resolved, it may be possible in the future to give the add_subparsers command a required=False parameter. But the intended design is that subparsers will be required, unless a flagged argument (like '-h') short circuits the parsing.

Creating mutually inclusive positional arguments with argparse

I'm trying to build a command line interface with Python's argparse module. I want two positional arguments where one depends on the other (mutually inclusive). Here is what I want:
prog [arg1 [arg2]]
Here's what I have so far:
prog [arg1] [arg2]
Which is produced by:
parser = argparse.ArgumentParser()
parser.add_argument('arg1', nargs='?')
parser.add_argument('arg2', nargs='?')
How do I get from there to having a mutually inclusive arg2?
Module argparse doesn't have options for creating mutually inclusive arguments.
However it's simple to write it by yourself.
Start with adding both arguments as optional:
parser.add_argument('arg1', nargs='?')
parser.add_argument('arg2', nargs='?')
After parsing arguments check if arg1 is set and arg2 is not:
args = parser.parse_args()
if args.arg1 and not args.arg2:
(this may be more tricky if you change default value from None for not used arguments to something different)
Then use parser.error() function to display normal argparse error message:
parser.error('the following arguments are required: arg2')
Finally change usage: message to show that arg2 depends on arg1:
parser = argparse.ArgumentParser(usage='%(prog)s [arg1 [arg2]]')
A complete script:
import argparse
parser = argparse.ArgumentParser(usage='%(prog)s [arg1 [arg2]]')
parser.add_argument('arg1', nargs='?')
parser.add_argument('arg2', nargs='?')
args = parser.parse_args()
if args.arg1 and not args.arg2:
parser.error('the following arguments are required: arg2')
You can do something similar to this using sub_parsers.
Here are the docs and examples:
http://docs.python.org/2/library/argparse.html#sub-commands

Python: argparse optional arguments without dashes

I would like to have the following syntax:
python utility.py file1 FILE1 file2 FILE2
where file1 and file2 are optional arguments.
It is simple to make it working with this syntax:
python utility.py --file1 FILE1 --file2 FILE2
using
parser.add_argument('--file1',type=file)
parser.add_argument('--file2',type=file)
however, if I remove the dashes, argparse starts to interprete it as a positional rather than optional argument...
In other words, is it possible to specifically tell argparse whether an argument is optional or positional so that I can have optional parameters without the dashes?
There is no way to get argparse to do this for you. However, you can make argparse accept any number of positional arguments:
parser.add_argument('FILES',nargs='*')
options=parser.parse_args()
file1,optional_files=options.FILES[0],options.FILES[1:]
Of course, you may want to add some checks to make sure that at least 1 file was given, etc.
EDIT
I'm still not 100% sure what you want here, but if file1 and file2 are literal strings, you can work around that a little bit by preprocessing sys.argv. Of course, this will still format your help message strangely, but you can always add an epilog explaining that either form is OK:
import argparse
import sys
mangle_args=('file1','file2')
arguments=['--'+arg if arg in mangle_args else arg for arg in sys.argv[1:]]
parser=argparse.ArgumentParser()
parser.add_argument('--file1')
parser.add_argument('--file2')
options=parser.parse_args(arguments)
Another Example would be:
train.py
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Arguments for wake_word")
parser.add_argument('data', type=str, help="path to data")
parser.add_argument('output', type=str, help="model save path")
parser.add_argument('batch_size', type=int, help="batch size")
parser.add_argument('epochs', type=int, help="no.s of epochs")
args = parser.parse_args()
print(args.data + args.output + args.batch_size + args.epochs)
then you can just run this code with arguments without dash
train.py /path/to/data/ /path/to/output_files/ 128 100
And, in ascending order
Had same problem. My workaround is:
lastarg = sys.argv[-1]
if len(sys.argv) > 1 and lastarg[0] != '-':
sys.argv[-1] = '-file'
sys.argv.append(lastarg)
argparser = argparse.ArgumentParser()
argparser.add_argument('-d', action='store_true')
argparser.add_argument('-nrv', action='store_true')
argparser.add_argument('-file', type=str, default='')
args = argparser.parse_args()

Optional stdin in Python with argparse

I found the very useful syntax
parser.add_argument('-i', '--input-file', type=argparse.FileType('r'), default='-')
for specifying an input file or using stdinā€”both of which I want in my program. However, the input file is not always required. If I'm not using -i or redirecting input with one of
$ someprog | my_python_prog
$ my_python_prog < inputfile
I don't want my Python program to wait for input. I want it to just move along and use default values.
The standard library documentation for argparse suggests this solution to allow optional input/output files:
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
... default=sys.stdin)
>>> parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'),
... default=sys.stdout)
>>> parser.parse_args(['input.txt', 'output.txt'])
Namespace(infile=<_io.TextIOWrapper name='input.txt' encoding='UTF-8'>,
outfile=<_io.TextIOWrapper name='output.txt' encoding='UTF-8'>)
>>> parser.parse_args([])
Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>,
outfile=<_io.TextIOWrapper name='<stdout>' encoding='UTF-8'>)
Use isatty to detect whether your program is in an interactive session or reading from a file:
if not sys.stdin.isatty(): # Not an interactive device.
# ... read from stdin
However, for the sake of consistency and reproducability, consider following the norm and reading from stdin if the filename is -. You may want to consider to let the fileinput module handle that.
Building on top of the answer regarding TTY detection, to answer the question explicitly:
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input-file', type=argparse.FileType('r'), default=(None if sys.stdin.isatty() else sys.stdin))

Categories

Resources