76

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.

Justin Force
  • 5,936
  • 5
  • 26
  • 39

3 Answers3

133

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'>)
mikewaters
  • 3,510
  • 3
  • 27
  • 21
  • 6
    I know I said this about the previous answer, but _this_ is _exactly_ what I was looking for. Thank you. – Justin Force Jun 15 '12 at 18:44
  • 4
    I'm absolutely bewildered. OP said the input file was not always required. So why is `infile` specified first? Why are these positional anyway? Lastly, why the heck wouldn't OP's code work? The `argparse` tutorial seems to imply that it should work just fine... – 2rs2ts Jul 08 '13 at 23:19
  • 1
    @2rs2ts _why is `infile` specified first?_ — Particular ordering of the arguments is just a convention, you usually expect first argument to specify an input and second specify output. _Why are these positional anyway?_ — Because of author's preference. Nothing wrong with this usage of positional arguments. Might be a bit awkward to use in scenario when you want to read from stdin and write to a file, i.e.`app.py - outfile` but other than that positional argument would work just fine. _Why the heck wouldn't OP's code work?_ — it actually works just fine :) – Mr. Deathless Nov 25 '15 at 09:25
  • @Mr.Deathless `app.py - outfile` explains it perfectly, thank you. – 2rs2ts Nov 30 '15 at 18:03
22

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.

Community
  • 1
  • 1
phihag
  • 263,143
  • 67
  • 432
  • 458
13

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))
palswim
  • 11,416
  • 6
  • 50
  • 74