Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop argparse.FileType creating the file specified as default

I love the argparse module. argparse.FileType is helpful too, unless you want the default to be something other than sys.std* since the default output file is created even if you supply a value.

For example:

parser.add_argument('--outfile', type=FileType('w'), default="out.txt")

will create out.txt even if you specify a file with --outfile.

The best I can come up with is:

class MagicFileType(object):

    def __init__(self, *args, **kwargs):

        # save args/kwargs and set filetype to None
        self.filetype = None
        self.args = args
        self.kwargs = kwargs

    def __getattr__(self, attr):
        """ Delegate everything to the filetype """

        # If we haven't created it, now is the time to do so
        if self.filetype is None:
            self.filetype = FileType(*self.args, **self.kwargs)
            self.filetype = self.filetype(self.filename)

        return getattr(self.filetype, attr)

    def __call__(self, filename):
        """ Just cache the filename """

        # This is called when the default is created
        # Just cache the filename for now.
        self.filename = filename
        return self

But if feels like this should be easier, am I missing something?

like image 427
John Gill Avatar asked Sep 27 '13 16:09

John Gill


1 Answers

There was a relatively recent change in argparse, http://bugs.python.org/issue12776 (Aug 2012), that delays the evaluation of the default value. Originally a string default would be passed through type (via _get_value) at the start of parsing, resulting in the opening (and creation) of a FileType file (whether it would be needed or not). In this patch, the string is written to the Namespace, but not evaluated until the end of parsing, when it can determine whether another value was provided or not. Basically, this line was moved from early in parse_known_args to the end of _parse_known_args

default = self._get_value(action, action.default)

In http://bugs.python.org/issue13824 I proposed a patch that provides a FileContext type. Its main difference from FileType is that it wraps the open(file...) in a partial. That way the file isn't opened (or created) until actually used in a with args.output() as f: context.

That patch deals with some other things like testing whether the file can be created or not (using os.access) and wrapping stdin/out in a dummy context so it does not try to close it.

Without testing, you could modify FileType like this:

class FileOpener(argparse.FileType):
    # delayed FileType;
    # sample use:
    # with args.input.open() as f: f.read()
    def __call__(self, string):
        # optionally test string
        self.filename = string
        return self
    def open(self):
        return super(FileOpener,self).__call__(self.filename)
    file =  property(open, None, None, 'open file property')
like image 96
hpaulj Avatar answered Sep 26 '22 02:09

hpaulj