简体   繁体   中英

Arg parse: parse file name as string (python)

I would like to parse the name of a file into my script as a string, rather than directly converting the file into an object.

Here is a sample code, test.py :

import argparse
import os.path
def is_valid_file(parser, arg):
      if not os.path.exists(arg):
           parser.error("The file %s does not exist! Use the --help flag for input options." % arg)
      else:
           return open(arg, 'r')

parser = argparse.ArgumentParser(description='test') 
parser.add_argument("-test", dest="testfile", required=True,
                    help="test", type=lambda x: is_valid_file(parser, x))
args = parser.parse_args()    
print args.testfile

testfile is a .txt file containing: 1,2,3,4

In principal would like print args.testfile to return the invoked name of testfile as a string:

$ python test.py -test test.txt
>> "test.txt"

To achieve this I need to prevent argparser from converting test.txt into an object. How can I do this?

Many thanks!

you can modify your function as follows to return the string after having checked it exists:

def is_valid_file(parser, arg):
      if not os.path.exists(arg):
           parser.error("The file %s does not exist! Use the --help flag for input options." % arg)
      else:
           return arg

There's also a more direct method:

parser.add_argument("-test", dest="testfile", required=True,
                    help="test", type=file)  # file exists in python 2.x only

parser.add_argument("-test", dest="testfile", required=True,
                    help="test", type=lambda f: open(f))  # python 3.x

args = parser.parse_args()    
print(args.testfile.name)  # name of the file from the file handle

actually args.testfile is the file handle, opened by argparser (exception if not found). You can read from it directly.

The FileType type factory does most of what your code does, with a slightly different message mechanism:

In [16]: parser=argparse.ArgumentParser()
In [17]: parser.add_argument('-f',type=argparse.FileType('r'))

In [18]: args=parser.parse_args(['-f','test.txt'])
In [19]: args
Out[19]: Namespace(f=<_io.TextIOWrapper name='test.txt' mode='r' encoding='UTF-8'>)
In [20]: args.f.read()
Out[20]: '  0.000000,  3.333333,  6.666667, 10.000000, 13.333333, 16.666667, 20.000000, 23.333333, 26.666667, 30.000000\n'
In [21]: args.f.close()

For a valid name it opens the file, which you can use and close. But you can't use it in a with context.

If the file doesn't exist it exits with usage and a cant open message.

In [22]: args=parser.parse_args(['-f','test11.txt'])
usage: ipython3 [-h] [-f F]
ipython3: error: argument -f: can't open 'test11.txt': [Errno 2] No such file or directory: 'test11.txt'

FileType __call__ handles the error with an argparse.ArgumentTypeError

   except OSError as e:
        message = _("can't open '%s': %s")
        raise ArgumentTypeError(message % (string, e))

Using this error mechanism, and omitting your open I'd suggest:

def valid_file(astring):
    if not os.path.exists(astring):
        msg = "The file %s does not exist! Use the --help flag for input options." % astring
        raise argparse.ArgumentTypeError(msg)
    else:
        return astring

Which could be used as:

In [32]: parser=argparse.ArgumentParser()
In [33]: parser.add_argument('-f',type=valid_file)

In [34]: args=parser.parse_args(['-f','test11.txt'])
usage: ipython3 [-h] [-f F]
ipython3: error: argument -f: The file test11.txt does not exist! Use the --help flag for input options.
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2

In [35]: args=parser.parse_args(['-f','test.txt'])
In [36]: args
Out[36]: Namespace(f='test.txt')
In [37]: with open(args.f) as f:print(f.read())
  0.000000,  3.333333,  6.666667, 10.000000, 13.333333, 16.666667, 20.000000, 23.333333, 26.666667, 30.000000

http://bugs.python.org/issue13824 worries about FileType opening a file but not closing it. I proposed a FileContext , modeled on FileType , but instead of opening the file, returns an object that can be use as:

with arg.file() as f:
    f.read()

It would do the file existence or creatablity testing, without actually opening or creating the file. It's a more complicated solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM