How to add positional options for existing arguments in argparse

拥有回忆 提交于 2019-12-06 06:01:29

Sticking with the FileType will be awkward. That type opens or creates the file. So potentially you'll have 4 open files when you only want 2. But if one of those files is stdin or out you don't want to close it. And you can't handle a positional which could either be read or write depending what other arguments are given.

You could try defining 4 default string arguments, 2 flagged, and 2 nargs='?' positional. Give them different dest. Then you can apply your intelligence to the 4 possible values. The default default None should be a clear enough indication that a value wasn't provided. Once you've decided on the two filenames, then you can open and use them. Newer Python recommends using with contexts, though that can be awkward when a file is already open (e.g. sys.stdin).

I don't think you should try to implement that logic within argparse. Do it after parsing.

For those interested in a more explicit answer, here is how I eventually implemented @hpaulj's suggestion:

First I defined an argument group for the input and output file arguments:

files = parser.add_argument_group('file arguments:',description='These arguments can also be provided as postional arguments, in which case the input file comes first.')
files.add_argument('-i', '--input', nargs='?',
                    help='Source of the words to be syllabified.  If None or -, then input will be read from stdin.',
                    dest='inputfile')
files.add_argument('-o', '--output-file', nargs='?',
                    help='Destination of the syllabified words.  If None or -, then ouput will be written to stdout.',
                    dest='outputfile')
files.add_argument('fileone',nargs='?',
                     help=argparse.SUPPRESS)
files.add_argument('filetwo',nargs='?',
                     help=argparse.SUPPRESS)

This allowed me to keep the arguments together, separate from the other arguments for the program, and provided a bit more control over how the help text would appear so that it would make the most sense.

Then, after parsing the arguments (args = parser.parse_args()), I added the following logic to figure out what the correct input and output were and to open files or stdin and stdout as appropriate:

if (args.inputfile == None or args.inputfile == '-'):
    if (args.outputfile == None or args.outputfile == '-'):
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = sys.stdin
            output = sys.stdout
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            try:
                input = open(args.fileone,'r')
                output = sys.stdout
            except:
                input = sys.stdin
                output = open(args.fileone,'w')
        else:
            input = open(args.fileone,'r')
            output = open(args.filetwo,'w')
    else:
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = sys.stdin
            output = open(args.outputfile,'w')
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.fileone,'r')
            output = open(args.outputfile,'w')
        else:
            print("Error: too many files")
            print("Both -o and positional output file given")
            sys.exit(1)
else:
    if (args.outputfile == None or args.outputfile == '-'):
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = sys.stdout
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = open(args.fileone,'w')
        else:
            print("Error: too many files")
            print("Both -i and positional input file give")
            sys.exit(1)
    else:
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = open(args.outputfile,'w')
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            print("Error: too many files")
            print("Both -i and -o given with a positional file")
            sys.exit(1)
        else:
            print("Error: too many files")
            print("Both -i and -o given with positional files")
            sys.exit(1)

As you can see I decided to accept both the default None and - as possibilities for referring to stdin/stdout. This duplicated the behavior of the FileType argument which also accepted - in this fashion.

The one ambiguity that remains is that "None" (i.e. a string of the word "None") is not the same as None (i.e. the NoneType class) and the help messages might be interpreted to imply that -i None should refer to stdin. However, I figured that the difference here should be obvious enough to most python users that I wasn't going to complicate the logic any further to account for this possibility.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!