So three sample calls are:
python reader.py some.txt python reader.py new another.txt python reader.py edit some.txt
The easiest way to handle these is with one 'optional' positional, and one required one.
parser = ArgumentParser... parser.add_argument('-v','--verbose', ...) parser.add_argument('cmd', nargs='?', default='open', choices=['open','edit','new']) parser.add_argument('filename')
For your 3 samples, it should produce something like:
namespace(cmd='open', filename='some.txt') namespace(cmd='new', filename='another.txt') namespace(cmd='edit', filename='some.txt')
cmd is an optional positional argument. If it is missing, the one string will be allocated to filename, and cmd gets its default. It's easier to do this than trying make a subparsers optional.
As for your current parser:
parent_parser = argparse.ArgumentParser(description="Read text files.") parent_parser.add_argument('filename', help='TXT file', type=file, nargs='+')
I would not recommend using type=file. Better to use FileType or the default string (which lets you open the file in a with context later).
As to the nargs='+', do you really want to allocate 1 or more strings to filename? Or were you thinking of '?', which would be 0 or 1, i.e. making it optional?
parent_parser.add_argument('--verbose', '-v', action='store_true', help="Verbosity on") child_parser = parent_parser.add_subparsers(title="subcommand", help="Subcommand help") new_file_command = child_parser.add_parser('new', help="New text file") edit_file_command = child_parser.add_parser('edit', help="Edit existing text file")
Mixing this filename positional which accepts a variable number of values, with a subparsers argument (a positional that expects either new or edit) could be a problem.
I expect 'python reader.py some.txt' to object that the subparser command is missing. 'python reader.py new another.txt' will try to allocate new to filename, and another.txt to subparser, and raise an error.
It would be better to expect a subparsers command in all 3 cases:
parent_parser = argparse.ArgumentParser(description="Read text files.") parent_parser.add_argument('--verbose', '-v', action='store_true', help="Verbosity on") child_parser = parent_parser.add_subparsers(title="subcommand", help="Subcommand help", dest='cmd') open_file_command = child_parser.add_parser('open', help="Open text file") open_file_command.add_argument('filename', help='TXT file') new_file_command = child_parser.add_parser('new', help="New text file") new_file_command.add_argument('filename', help='TXT file') edit_file_command = child_parser.add_parser('edit', help="Edit existing text file") edit_file_command.add_argument('filename', help='TXT file')
Each of commands, 'open','new','edit', expects a 'filename'.
Trying to avoid the use of an open command is going to create more difficulties than it's worth.
(There is a bug/feature in the latest argparse that makes subparsers optional, but you shouldn't take advantage of that without really knowing the issues.)
With:
parser = argparse.ArgumentParser() parser.add_argument('-v', '--verbose') parser.add_argument('cmd', nargs='?', default='open', choices=['open', 'edit', 'new']) parser.add_argument('filename', nargs='+')
I expect reader.py new customstring to give
namespace(cmd='new', filename=[customstring])
which could be used as:
if args.cmd=='new': with open(args.filename[0] + '.txt', 'w') as f: # do something with the newly created file
open and edit would use different open modes.
Beware that in Py3, subparsers are not required. That is, if one of the subcommands is not provided, it won't raise an error. That's an inadvertent change from early versions.
Argparse with required subparser