fferrin
fferrin

Reputation: 908

argparse: optional argument as flag and variable

I'm writing a program that takes two argument (paths) and as an option -l, witch enables save the log in a log file.

I'm using argparse and create the following argument:

parser.add_argument('-l', '--logfile', 
                    nargs='?', 
                    const='tranfer.log', 
                    default=False,
                    help='save log of the transfer')

These are the forms of use:

  1. prog.py path_1 path_2. Nothing special.
  2. prog.py -l filename path_1 path_2. Save the log in filename file.
  3. prog.py -l path_1 path_2. Save the log the file by default (say, logfile).

I have a problem in item 3, because prog.py takes path_1 as the filename of the log file. The issue was easily fixed puttin -l option at the end of the line.

prog.py path_1 path_2 -l

But I was wondering if there's a way of tell argparse to use the last two option as the path files, because I'll be not the only one who use the program.

Path argument were add as follows:

parser.add_argument('left_path', 
                    action='store',
                    help='first path')
parser.add_argument('right_path', 
                    action='store',
                    help='second path')

Upvotes: 4

Views: 3591

Answers (3)

hpaulj
hpaulj

Reputation: 231355

Your assessment is right,

prog.py -l path_1 path_2

will assign path_1 to l and path_2 to the first positional, and raise an error about missing 2nd positional.

http://bugs.python.org/issue9338 argparse optionals with nargs='?', '*' or '+' can't be followed by positionals is a bug/issue that deals with this. Patches have been proposed, but not implemented. It's not trivial. When handing -l the parser would have to look ahead to see how many arguments are needed to satisfy the positionals, and refrain from consuming the next string (even though by your definition it has every right to do so).

It's also been discussed in previous SO questions.

https://stackoverflow.com/a/29853186/901925

https://stackoverflow.com/a/26986546/901925

You have to either put the optional last, or use some other means of signaling the end of its list of arguments (-- or other flagged optional). Or change the argument definitions, so that -l is not ? (or the equivalent), or change the positionals to flagged.

Upvotes: 3

you can also store all options in a single argument and check by hand, as in

parser.add_argument('-p', dest='path', nargs='?',
                    default=('path1/','path2/'))
args = parser.parse_args()
if len(args.path) == 3:
   args.logfile = args.path[0]
   args.path = args.path[1:]
elif len(args.path) == 2:
   args.logfile = ''
else:
   print 'Error'

but then you have to have the -p flag if you want to set the paths to be different from the defaults.

Upvotes: 0

anujm
anujm

Reputation: 309

The neat way to do this would be to introduce option flags for the both the paths arguments too. Then there would be no ambiguity and you'd be able to say:

prog.py -l -leftpath path_1 -rightpath path_2

Upvotes: 0

Related Questions