91

I want to open file for reading using argparse. In cmd it must look like: my_program.py /filepath

That's my try:

parser = argparse.ArgumentParser()
parser.add_argument('file', type = file)
args = parser.parse_args()

This gives me [edit: added later from comment by OP]:

parser.add_argument('file', type = file) NameError: name 'file' is not defined
Matt Hall
  • 7,614
  • 1
  • 23
  • 36
nuT707
  • 1,543
  • 4
  • 17
  • 27
  • 5
    And it did what, exactly? – Ry- Sep 18 '13 at 02:17
  • What is your problem currently? – justhalf Sep 18 '13 at 02:31
  • Running this i have error: parser.add_argument('file', type = file) NameError: name 'file' is not defined I don't exaclty understand how argparse work... – nuT707 Sep 18 '13 at 02:38
  • 1
    `type = fnc` works if `fnc` is a function that takes a string, and returns a desired object, or raises an error. There isn't a `file` function in Python. `type=open` does work since there is a Python function `open(filename)'. argparse provides a better function, `argparse.FileType()` (actually a function constructor). But a simple string argument as in `wim`s answer is a better starting place. – hpaulj Sep 18 '13 at 03:53
  • 4
    `type=file` does work in Python2.7 (and earlier). But in Python3, `file` has been removed. http://stackoverflow.com/questions/112970/python-when-to-use-file-vs-open – hpaulj Sep 19 '13 at 05:16

6 Answers6

228

Take a look at the documentation: https://docs.python.org/3/library/argparse.html#type

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('file', type=argparse.FileType('r'))
args = parser.parse_args()

print(args.file.readlines())
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
ilent2
  • 5,171
  • 3
  • 21
  • 30
  • 1
    This is the superior answer. This makes argparse accept only a file for the argument and opens it in the process, whereas the answer by wim will accept any string, and you have to handle the file open() operation (and therefore file verification and error handling) in the body of the program. – L0j1k Jan 11 '14 at 05:28
  • 65
    @L0j1k Whilst I feel it is good to have both answers here, one should also mention the caveats of `argparse.FileType`. For example, it leaves an open file handle dangling which is in my opinion sloppy implementation. Usually I would rather be handling the file `open()` myself so that I can do it properly (with a context manager), and also customise the behaviour and output for 'file not found' errors. – wim Apr 02 '14 at 12:22
  • 43
    p.s. [there is an issue on the python bug tracker about this](http://bugs.python.org/issue13824), and python devs have commented that "FileType is not what you want for anything but quick scripts that can afford to either leave a file open or to close stdin and/or stdout" – wim Apr 02 '14 at 12:22
  • 2
    Indeed, that changes everything. – L0j1k Apr 03 '14 at 01:52
  • `args.file` and other handles can be closed when done with them. If there was a parse error, they will get closed on exit, even if argparse is not smart enough to not open them in the first place. – Gringo Suave Jun 11 '14 at 23:51
  • 20
    You can do `with args.file as f: ...` which will close the file earlier in the good scenario case. – Wernight Sep 01 '14 at 10:42
  • 2
    @Wernight: but that will close `stdout` and `stdin`, correct? – MestreLion Jan 21 '15 at 09:36
  • @MestreLion It'll open and close a file pointer to the file of argument named `file`. So for `$ myapp foo.txt` it may be the file pointer of `foo.txt`, not stdin/stdout. – Wernight Jan 23 '15 at 10:33
  • 12
    @Wernight: But `argparse` automatically sets file to `` (or ``, depending on `mode`) if the argument is missing or is `-`. Which is a very cool feature, but if `with args.file as f: ...` is used, it will (or try to) close them, when it shouldn't. – MestreLion Jan 23 '15 at 10:49
  • 2
    @MestreLion Good point, didn't know about that feature. Could do `try: ... finally: if isReadFile(args.file): args.file.close()`. See http://stackoverflow.com/q/28236302/167897 – Wernight Jan 30 '15 at 12:48
  • 2
    @Wernight `isReadFile()`? – MestreLion Jan 30 '15 at 13:16
  • Can I open a gzip file directly with argparse by changing the `type=argparse.FileType()` to some gzip type? It's not in the docs, so I'm not sure if argparse even supports compressed file types... – Thomas Matthew Nov 10 '15 at 01:17
  • @ThomasMatthew I have no special insight, but I'd strongly suspect no. A gzipped file might be full of many files, not just one. So what would the returned value be? – Jonathan Hartley Jul 08 '20 at 19:59
  • Ever hear of try-except-finally? `try: ... ... finally: if args.file: args.file.close()` – ingyhere Sep 17 '21 at 07:16
97

The type of the argument should be string (which is default anyway). So make it like this:

parser = argparse.ArgumentParser()
parser.add_argument('filename')
args = parser.parse_args()
with open(args.filename) as file:
  # do stuff here
wim
  • 338,267
  • 99
  • 616
  • 750
  • 14
    Please see wim's comments under ilent2's answer for the reasons why this answer is probably the best answer in most cases. – L0j1k Apr 14 '14 at 04:19
  • 3
    You shouldn't use the variable name `file` since that is a built-in. – jnrbsn Jul 12 '14 at 04:44
  • 9
    Generally that is correct, but `file` is never used in python these days - it was removed in python3, and in python2 the docstring even advises to use `open` instead. So it's OK to stomp this name, it is even done several times in core libraries. – wim Jul 12 '14 at 12:19
40

In order to have the file closed gracefully, you can combine argparse.FileType with the with statement

parser.add_argument('file', type=argparse.FileType('r'))
args = parser.parse_args()

with args.file as file:
    print(file.read())
user16217248
  • 3,119
  • 19
  • 19
  • 37
Ming
  • 872
  • 9
  • 15
  • In the line `with args.file` the "file" refers to the name of the argument right? If I had named the argument as `-f` then it should be `with args.-f` right?? Correct me if I'm wrong. Argparse beginner here – vishal Feb 27 '20 at 12:14
  • 2
    @vishal.k Yes and no. If you do `add_argument('-f', ...`, then you need `with args.f as file` without the dash – Ming Mar 05 '20 at 17:40
  • 4
    By using `type=argparse.FileType('r')`, the file is opened from the point you call `parser.parse_args()`. Does this mean the file would still be left open if your code crashes before your context handler? Not a big deal, but something to keep in mind. – LondonAppDev Nov 12 '20 at 07:49
29

I'll just add the option to use pathlib:

import argparse, pathlib

parser = argparse.ArgumentParser()
parser.add_argument('file', type=pathlib.Path)
args = parser.parse_args()

with args.file.open('r') as file:
    print(file.read())
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
8

TL; DR

parser.add_argument(
    '-f', '--file',
    help='JSON input file',
    type=argparse.FileType('r'),
)

Description

Simple command line script to reformat JSON files

reformat-json \
    -f package.json \
    --indent=2 \
    --sort-keys \
    --output=sorted_package.json

can be code in Python as follows

#!/usr/bin/env python3

import argparse, json, sys

EXIT_SUCCESS = 0
EXIT_FAILURE = 1

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument(
        '-f', '--file',
        help='JSON input file',
        type=argparse.FileType('r'),
    )

    parser.add_argument(
        '-i', '--indent',
        help='Non-negative integer indent level',
        type=int
    )

    parser.add_argument(
        '-o', '--output',
        help='Write JSON into output file',
        type=argparse.FileType('w'),
    )

    parser.add_argument(
        '-s', '--sort-keys',
        action='store_true',
        help='Sort output JSON by keys',
    )

    args = parser.parse_args()

    if not args.file:
        parser.print_usage()
        return sys.exit(EXIT_FAILURE)

    gson = json.dumps(
        json.load(args.file),
        indent=args.indent,
        sort_keys=args.sort_keys
    )

    args.file.close()

    if args.output:
        args.output.write(gson)
        args.output.write('\n')
        args.output.close()
    else:
        print(gson)

    return sys.exit(EXIT_SUCCESS)

if __name__ == '__main__':
    main()
JP Ventura
  • 5,564
  • 6
  • 52
  • 69
-2

This implementation allows the "file name" parameter to be optional, as well as giving a short description if and when the user enters the -h or --help argument.

parser = argparse.ArgumentParser(description='Foo is a program that does things')
parser.add_argument('filename', nargs='?')
args = parser.parse_args()

if args.filename is not None:
    print('The file name is {}'.format(args.filename))
else:
    print('Oh well ; No args, no problems')
yPhil
  • 8,049
  • 4
  • 57
  • 83
  • 4
    It doesn't answer the question at all. OP was asking how they could open a file for reading with argparse, not check that the user specified a filename. – Rakurai Jan 02 '19 at 13:53