22

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.

1
  • 2
    I don't have argparse available to check this, but shouldn't "conf_dir=FOO".split() actually be "conf_dir=FOO".split('=')? Commented Mar 1, 2011 at 13:11

4 Answers 4

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)
Sign up to request clarification or add additional context in comments.

3 Comments

str.partition() is less prone to ValueError than str.split() as it always returns three items.
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.)
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. ;-)
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.

Comments

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)

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.