30

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?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
jvm
  • 349
  • 1
  • 3
  • 7
  • 3
    That syntax (no dashes) is confusing as hell! There is a reason the convention exists. – Martijn Pieters Jul 03 '12 at 12:15
  • 1
    @MartijnPieters: it may be confusing but for some scientific applications this is a standard... [example](http://www.ccp4.ac.uk/html/refmac5/description.html) – jvm Jul 03 '12 at 12:19
  • 6
    @MartijnPieters: counterexample: the `git` or `svn` command line interface. – Rody Oldenhuis Jul 14 '14 at 14:21
  • 3
    @RodyOldenhuis: those are subcommands, not switches. Each subcommand takes options, which use dashes. – Martijn Pieters Jul 14 '14 at 14:34
  • 2
    @MartijnPieters: that is just semantics, no difference from the syntax point of view - arguments with no dashes. Not (directly) supported by argparse for whatever semantics. BTW, they are not switches in my example either (but that is irrelevant as I have just mentioned). – jvm Dec 30 '14 at 17:56
  • 2
    @jvm: subcommands do not usually share switches. There is a shared level (`git --version`) and per-subcommand switches (`git checkout --patch`). Don't just dismiss the semantics here. – Martijn Pieters Dec 30 '14 at 19:31

3 Answers3

13

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)
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 1
    Positional arguments are not sufficient for me. For example, file1 may be skipped, file2 inputted, file3 skipped, file4 inputted and the assignment is important (ie I need to know that it was file2 and file4 that were specified by the user) – jvm Jul 03 '12 at 12:33
  • 1
    @jvm -- How is argparse (or anything) supposed to know that the file you gave is `file2` instead of `file1`? If you can't sort it out from the order it came on the commandline, certainly `argparse` can't do it either. – mgilson Jul 03 '12 at 12:43
  • Are `file1` and `file2` constant strings, or are they names of files -- maybe that's what I'm not understanding here... – mgilson Jul 03 '12 at 12:45
  • @jvm -- I think I now understand what you're trying to do. I've posted a "workaround". It's a little ugly, but it might be good enough for your purposes. – mgilson Jul 03 '12 at 13:25
  • 1
    Thanks, this is a working solution indeed. I was hoping I was missing some argparse function that would allow me this directly but if that is not possible then your workaround is probably the best. Just some nitpicking - of course you also need to import argparse in your example. – jvm Jul 03 '12 at 16:16
  • @jvm -- I left it off because you did in your original example. Anyway, it's in there now. – mgilson Jul 03 '12 at 16:22
  • @mgilson, it's a good workaround but `--help` still contains those dashes. – Kiran Kumar Kotari Jul 18 '22 at 08:34
3

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

Jagesh Maharjan
  • 866
  • 1
  • 11
  • 24
1

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()
mc.dev
  • 2,675
  • 3
  • 21
  • 27