70

I'm trying to accept an argument of type=dict with argparse but no matter the input it gives an error of invalid dict value.

#!/usr/bin/env python

import argparse

MYDICT = {'key': 'value'}

parser = argparse.ArgumentParser()
parser.add_argument("-m", "--mydict", action="store",
                    required=False, type=dict,
                    default=MYDICT)

args = parser.parse_args()

print args.mydict

This is what happens when I try and pass a dictionary to the script

./argp.py -m "{'key1': 'value1'}"
usage: argp.py [-h] [-m MYDICT]
argp.py: error: argument -m/--mydict: invalid dict value: "{'key1': 'value1'}"

Looking at the documents I would think that this would be possible.

http://docs.python.org/dev/library/argparse.html

“Any object that supports the in operator can be passed as the choices value, so dict objects, set objects, custom containers, etc. are all supported.”

user2745896
  • 753
  • 1
  • 6
  • 7
  • The line you quote from the documentation pertains to the "choices" keyword argument, which defines what values of the arguments are allowed. Passing a dict to choice is effectively equivalent to passing a list of the keys. Thus, passing "{ 'a':2, 'b':3 }" would restrict allowed arguments to your "--mydict" flag to "a" or "b". – Pascal Bugnion Sep 04 '13 at 08:34

1 Answers1

96

I do not think it is possible to pass a dictionary as an argument in the command line because there doesn't exist a conversion function from string to dict (EDIT: A hack is possible which gives similar behaviour, see below). What you are essentially telling python to do is:

dict("{'key1': 'value1'}")

Which if you try it out in the python console, does not work.

What the phrase:

"Any object that supports the in operator can be passed as the choices value, so dict objects, set objects, custom containers, etc. are all supported."

refers to is the choices argument that can be passed with the add_argument function - not to the type argument.

Your best bet is to probably accept your argument as a string and then convert it using the json capabilities of python:

parser.add_argument('-m', '--my-dict', type=str)
args = parser.parse_args()

import json
my_dictionary = json.loads(args.my_dict)

You can then pass a dictionary in the form of a string. You can try the json encoder/decoder out for yourself in the python console to see how it works:

>>>json.loads('{"value1":"key1"}')
{u'value1': u'key1'}

EDIT: hpaulj has pointed out to me that you can "hack" the type parameter by passing it json.loads which allows you to pass JSON that is similar looking to a dictionary.

import json
parser.add_argument('-d', '--my-dict', type=json.loads)
args = parse.parse_args()

mydict = args.my_dict  # Will return a dictionary

NOTE: The input format you pass is not the same as python dictionary but is probably similar enough for your use case.

The reason this works is actually quite interesting because internally argparse will just use the parameter value as a function to convert the argument. i.e. if type=int then it will use int(arg) or if type=json.loads then json.loads(arg)

This also means that you can pass any function which takes a single parameter in as the argument to type and perform custom conversions if you need to :)

Michael Aquilina
  • 5,352
  • 4
  • 33
  • 38
  • 66
    `type=json.loads` works – hpaulj Sep 04 '13 at 16:29
  • 1
    That actually makes a lot of sense since as I mentioned above, the parameter passed to type is handled by placing the argument in the parenthesis. Thanks for pointing that out. – Michael Aquilina Sep 04 '13 at 20:04
  • 5
    I wouldn't necessarily call that a "hack". It's actually how the type argument is supposed to work – mdw7326 Feb 08 '18 at 14:06
  • would that take care of default values per key value? – Prasath Jul 10 '18 at 23:13
  • 10
    Apparently jason can decode with single quotes outside and double quotes inside the dict '{"value1":"key1"}' but not "{'value1':'key1'}" – Timo Kvamme Apr 29 '19 at 11:58
  • 5
    In windows, pass ``-m {\"key1\":\"value1\"}`` in the terminal.(adding backslash ``\`` in front of ``"``, without spaces in the whole ``{}`` string). – Johnson Lai Oct 24 '19 at 14:13
  • If you want the string representation of the dictionary to be correctly formatted remember to have a look at `json.dumps(dictionary)` – Alessio Arena Aug 04 '21 at 04:23
  • It is worth taking a look at this blog post (not mine): https://sumit-ghosh.com/articles/parsing-dictionary-key-value-pairs-kwargs-argparse-python/ – Baza86 Jul 15 '22 at 14:08
  • Adding in that if you are passing booleans in your "dictionary" to use JSON format on the CLI (true/false and NOT True/False). It will auto-format to pythonic True False when parsed. – Chris Aug 15 '22 at 17:58