1

I have a python program that runs depending on some parameters. Let's say, one of the parameters is C with a default value of 3. So when I run it without any arguments, it does

$ python parsing.py
C=3

When I load a file for my initial data, it can get some parameter values from that file. For example if my file MakeC5 says that the program should run with C=5, I will get

$ python parsing.py --file=MakeC5
C=5

On the other hand, if I specify a different value for C as optional argument, that value is taken, and has priority over the value in the file.

$ python parsing.py --C=4
C=4

$ python parsing.py --file=MakeC5 --C=4
C=4

Up to here, I can just check if the value specified on the command line is different from the default value and otherwise take the one from the file, as with

if parameters.C == parser.get_default('C'):
    parameters.C = load_file(parameters.file)["C"]

But this approach does not work if I give the default value for C on the command line as in

$ python parsing.py --file=MakeC5 --C=3
C=3

How do I get that case right? Is there a way that does not need to parse the command line twice as in

parameters = parser.parse_args()
parameters_from_file = load_file(parameters.file)
parser.set_defaults(**parameters_from_file)
parameters = parser.parse_args()

which does not look like “the obvious pythonic way to do this” to me? This is quasi reading a configuration file that can be specified as argument, so I expect there to be a good way.

Anaphory
  • 6,045
  • 4
  • 37
  • 68
  • How is `python parsing.py --file=MakeC5 --C=3` different from the example above with `python parsing.py --file=MakeC5 --C=4`? – Some programmer dude Feb 13 '13 at 13:02
  • `C=3` is the default value, so it's the reason why `if parameters.C = parser.get_default('C'): parameters.C = load_file(parameters.file)["C"]` does not work, which was my first idea to implement this. – Anaphory Feb 13 '13 at 13:05
  • I think this is pretty much a dupe of this currently featured question: http://stackoverflow.com/q/6133517/748858 – mgilson Feb 13 '13 at 13:09
  • On second thought, that question has a far wider scope. I would be fine just knowing how to see if an argument was explicitly set by a command line switch or implicitly set to its default value, because the way I need to handle my files significantly differs from how one handles config files, so even a good answer to that other question would not necessarily help me a lot. – Anaphory Feb 13 '13 at 13:28

2 Answers2

5

Assuming you use the argparse module, simply don't set a default argument when you add the argument. Then the attribute will be None if the argument was not present, and you can check for that instead of parsing arguments twice:

parser.add_argument('--C'   , action='store', dest='C')
parser.add_argument('--file', action='store', dest='file')
# ...

args = parser.parse_args()
# ...

if args.C is None:
    # Argument `--C` not given on command line
    if args.file is not None:
        # Load the file, set `args.C` if the file contains the `C` option
        ...

if args.C is None:
    # `C` was neither specified on the command line argument given,
    # nor in the configuration file, set default value
    args.C = 3

print 'C =', args.C
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    This does not work if one of the values I can specify on the command line is allowed to be `None`. Changing that particular value to `False` for the moment helps, but essentially this still has the same problem as my preliminary solution. – Anaphory Feb 15 '13 at 10:49
1

Within argsparse there is fromfile-prefix-chars and it would seem to do some of what you wish if you can live with it's limitations.

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default="ABC")
args = parser.parse_args()
print '#1', args.foo

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default="ABC")
args = parser.parse_args(['--foo', 'BAR' ])
print '#2', args.foo

import argparse
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('--foo', default="ABC")
args = parser.parse_args(['--foo', 'BAR', '@myargs' ])
print '#3', args.foo

import argparse
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('--foo', default="ABC")
args = parser.parse_args(['@myargs', '--foo', 'BAR' ])
print '#4', args.foo

myargs

--foo
FISH

Output

#1 ABC
#2 BAR
#3 FISH
#4 BAR

Notice how the position of @myargs on the command line affects the value of foo, when it's at the front it's valued if overriden by --foo and vice versa.

This would seem to do what you want where:

default-value < config-file < cli-args

Although it relies on you remembering to put the config file in the right order, unless of course you take the args and reorder them so that they first in the args.

Hopefully food for thought.

sotapme
  • 4,695
  • 2
  • 19
  • 20