p0lAris
p0lAris

Reputation: 4820

Python argparse: both optional and positional value for an argument

Consider the following usage:

usage: do.py [-h] [-s | -m] filename

This is not the complete usage. But I effectively want is filename to be an actual value of file and not:

--filename FILENAME

But also, filename should be optional so I can read from the standard input. Think about the cat program on UNIX.

You simply say:

cat filename

OR

cat

EDIT: Right now, if I execute the program do.py without any command line options, I will get an error: too few arguments. Instead I would still want it to execute even if I don't give it a valid filename. How do I do that?

Upvotes: 5

Views: 3137

Answers (1)

Dietrich Epp
Dietrich Epp

Reputation: 213298

Update 2: From the ArgParse documentation,

One of the more common uses of nargs='?' is to allow optional input and 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=<open file 'input.txt', mode 'r' at 0x...>,
          outfile=<open file 'output.txt', mode 'w' at 0x...>)
>>> parser.parse_args([])
Namespace(infile=<open file '<stdin>', mode 'r' at 0x...>,
          outfile=<open file '<stdout>', mode 'w' at 0x...>)

Original answer:

This is straightforward: just add a positional argument with a default value and a nargs='*'. The default value will be used if there are zero arguments, otherwise the arguments on the command line will be used:

>>> p = argparse.ArgumentParser()
>>> p.add_argument('filename', nargs='*', default=['-'])
>>> p.parse_args([])
Namespace(filename=['-'])
>>> p.parse_args(['abc'])
Namespace(filename=['abc'])

Typically, - is used to refer to standard input / standard output.

Then you do something like this:

def get_inputs(ns):
    """Iterate over input files."""
    for path in ns.filename:
        if path == '-':
            yield sys.stdin
        else:
            yield open(path, 'r')

Update: I assumed you wanted multiple filenames, since cat takes multiple filenames. You can use nargs='?' if you want either zero or one filename.

Upvotes: 8

Related Questions