21

I want to use argparse to parse command lines of form "arg=val" For example, the usage would be:

script.py conf_dir=/tmp/good_conf

To achieve it, I am doing this:

desc   = "details"
parser = argparse.ArgumentParser(description=desc, add_help=False)
args = parser.add_argument("conf_dir")
args = parser.parse_args("conf_dir=FOO".split())
args = parser.parse_args()
print args.conf_dir

But, the problem is that, on invocation of the script with:

python script.py conf_dir=/tmp/good_conf

I get:

conf_dir=/tmp/good_conf

Where as I expect

/tmp/good_conf

So, the question is: Can I use argparse to parse cmd line, which contains name value pairs? Any hints?

Edit: The reason I want to do this and not some thing like --conf_dir=/tmp/good_dir is because there are other tools (written in other language), which uses conf_dir=/tmp/good_dir style of arguments. To maintain consistency, I was to parse args in this way.

tshepang
  • 12,111
  • 21
  • 91
  • 136
  • 2
    I don't have `argparse` available to check this, but shouldn't `"conf_dir=FOO".split()` actually be `"conf_dir=FOO".split('=')`? – lafras Mar 01 '11 at 13:11

4 Answers4

29

You need a custom action

class StoreNameValuePair(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        n, v = values.split('=', 1)
        setattr(namespace, n, v)

args = parser.add_argument("conf_dir", action=StoreNameValuePair)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • `str.partition()` is less prone to ValueError than `str.split()` as it always returns three items. – Gringo Suave Dec 23 '20 at 00:23
  • I'd rather handle that with `values.split('=', 1)`, which doesn't unnecessarily return the `=`. Arguably, you'd *want* a `ValueError` if the name value pair wasn't actually a pair. (Though whether you want to catch the error--or let it propagate upwards--or check if `values.partition("=")[1]` is empty is probably a matter of personal preference.) – chepner Dec 23 '20 at 01:02
  • Ok, although `.partition` is built primarily for this use case. Delimiter can be assigned to `_`, also error can be checked by checking if v is empty. All without try except. ;-) – Gringo Suave Dec 23 '20 at 19:55
6

As per the documentation, argparse doesn't natively let you have unprefixed options like that. If you omit the leading -, it assumes you are describing a positional argument and expects it to be provided as:

python script.py /tmp/good_conf

If you want it to be optional, it needs to be correctly marked as a flag by calling it --conf_dir, and invoking the script like:

python script.py --conf_dir=/tmp/good_conf

However, to accept name-value pairs, you can implement a custom action. In combination with nargs, such an action could accept an arbitrary number of name-value pairs and store them on the argument parsing result object.

ncoghlan
  • 40,168
  • 10
  • 71
  • 80
2

@chepner This is great. I improved this to support multiple args as well and store the result as dict:

class StoreDict(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        kv={}
        if not isinstance(values, (list,)):
            values=(values,)
        for value in values:
            n, v = value.split('=')
            kv[n]=v
        setattr(namespace, self.dest, kv)
Michael Wyraz
  • 3,638
  • 1
  • 27
  • 25
1

The usual way to put name value pairs on the command line is with options. I.e. you would use

python script.py --confdir=/tmp/good_conf

argparse can certainly handle that case. See the docs at:

http://docs.python.org/library/argparse.html#option-value-syntax

Thomas K
  • 39,200
  • 7
  • 84
  • 86