I would like to be able to put command-line arguments in a file, and then pass them to a python program, with argparse, using an option rather than a prefix character, for instance:
$ python myprogram.py 1 2 --foo 1 -A somefile.txt --bar 2
This is almost the same as this question, except that I need to have some positional arguments at the start; when that solution calls parse_args, it fails if the file does not have the positional arguments in it.
- 1
- 1
- 4,829
- 1
- 16
- 17
1 Answers
If somefile.txt contains
one
two
three
then
$ python myprogram.py 1 2 --foo 1 @somefile.txt --bar 2
using the prefix char is effectively the same as
$ python myprogram.py 1 2 --foo 1 one two three --bar 2
In other words, right at the start of parsing, the @ file is read, and its contents are spliced into the argv list. From there parsing occurs normally.
One thing I suggested in the other SO question was to implement that splicing yourself, prior to parsing.
The answer that you are referring to uses a custom Action; at the point where the usual Action just places the value in the namespace, this action reads and parses the file:
parser.parse_args(f.read().split(), namespace)
A variant parses into a new namespace and selectively copies values to the main namespace.
Apparently your problem is that your parser has some required arguments, and this parse_args raises an error if the file does not contain those. That's not surprising.
One solution is to use a different parser for this file, one that is designed to work with just the content of the file. I would just define it globally.
alt_parser.parse_args(f.read().split(), namespace)
In other words, you don't have to use the parser that was passed in as a parameter.
A variation on this is to put the filename in the namespace, and handle it after the first parsing:
args = parser.parse_args()
if args.A:
argv = open(args.A).read().split()
args1 = alt_parser.parse_args(argv)
But you might ask, can't we tell what arguments have already been parsed, and take that into account in handling -A? The parser is not a state-machine; parsing does not alter any of its attributes. The only variable that reflects parsing so far is the namespace. All other parsing variables are local to the calling code.
- 221,503
- 14
- 230
- 353
-
Should have said: `@` does just the right thing, but I am copying behavior of another program. This makes sense; now the trick is making a second parser with the same arguments except without the required, positional ones. – petrelharp Oct 16 '16 at 17:07
-
1You could make that parser first, and use it as a `parents` to the main one. Then you just have to add the `positionals` to the `child`. – hpaulj Oct 16 '16 at 17:28