Argparse - Custom Action update to an dest dict
<p>I want to collect some options as a dict using <code>argparse</code>. I have wrote a custom Action class as follows. The problem is that the <code>__call__</code> method is never invoked. </p>

<pre><code>#!/usr/bin/env python3

import argparse

class UpdateDict(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, **kwargs):
        print('UpdateDict', option_strings, dest, nargs)
        if nargs != 1:
            raise ValueError("nargs must be 1")
        super(UpdateDict, self).__init__(option_strings, dest, nargs=nargs, **kwargs)
        print(self)

    def __call__(self, parser, namespace, values, option_string=None):
        print('%r %r %r' % (namespace, values, option_string))
        dest = getattr(namespace, self.dest)
        print(dest)
        key = option_string[1]
        dest.update(key = values[0])

def parse_args():
    parser = argparse.ArgumentParser(description='formula alpha')
    parser.add_argument('formula', nargs=1, type=str, help='alpha formula')
    parser.set_defaults(mydict={})

    parser.add_argument('-a', '--alpha', nargs=1, type=str, default=['alas'], action=UpdateDict, dest='mydict')
    parser.add_argument('-b', '--beta', nargs=1, type=int, default=[0], action=UpdateDict, dest='mydict')

if __name__ == '__main__':                                                                                                                                                                                        
    args = parse_args()
    print(args)
</code></pre>

<p>With your script, simplified to focus on putting values in <code>mydict</code>:</p>

<pre><code>import argparse

class UpdateDict(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        adict = getattr(namespace, 'mydict')
        adict.update({self.dest: values})

def parse_args():
    parser = argparse.ArgumentParser()
    parser.set_defaults(mydict={})
    parser.add_argument('-a', '--alpha', default='alas', action=UpdateDict)
    parser.add_argument('-b', '--beta', type=int, default=0, action=UpdateDict)
    return parser.parse_args()  # parse and return

if __name__ == '__main__':                                                                                                                                                                                        
    args = parse_args()
    print(args)
</code></pre>

<p>I get</p>

<pre><code>2333:~/mypy$ python3 stack39263156.py
Namespace(alpha='alas', beta=0, mydict={})
2337:~/mypy$ python3 stack39263156.py -a one
Namespace(alpha='alas', beta=0, mydict={'alpha': 'one'})
2337:~/mypy$ python3 stack39263156.py -a one -b 2
Namespace(alpha='alas', beta=0, mydict={'beta': 2, 'alpha': 'one'})
</code></pre>

<p>You can restore the <code>nargs=1</code> test if you want, but the default <code>None</code> implies 1 argument.  Most don't use <code>nargs=1</code>. It makes sure the value is a single element list; most people want multiple element lists (e.g. '+' or '*' nargs).</p>

<p>I like your use of <code>set_defaults</code> to put <code>mydict</code> in the namespace.  I then  retrieve it from the namespace, and update it, using the argument's <code>dest</code> as the <code>key</code>.  Your version confuses the <code>dest</code>, and overwrite the <code>mydict</code> attribute.</p>

<p>The default <code>store_action</code> uses <code>setattr(namespace, self.dest, values)</code>, to do the same thing, but with the namespace object rather than the embedded <code>mydict</code>.</p>

<p>The usual defaults handling fills the <code>alpha</code> and <code>beta</code> attributes (the <code>dest</code>).  To put those in <code>mydict</code> instead, you'd have to use something like</p>

<pre><code>parser.set_defaults(mydict={'alpha':'alas', 'beta':0})
</code></pre>

<p><code>SUPPRESS</code> can be used to keep defaults out of the usual <code>dest</code> attributes</p>

<pre><code>parser.add_argument('-a', '--alpha', action=UpdateDict, default=argparse.SUPPRESS)
parser.add_argument('-b', '--beta', type=int, action=UpdateDict)
</code></pre>

<p>the no-argument run would produce:</p>

<pre><code>2347:~/mypy$ python3 stack39263156.py
Namespace(beta=None, mydict={'beta': 0, 'alpha': 'alas'})
</code></pre>

<p>The use of an embedded dictionary is an interesting idea, though I'm not sure what it adds.  <code>vars(args)</code> is recommended as a way of turning a <code>namespace</code> into a dictionary.</p>

<pre><code>{'mydict': {'alpha': 'one', 'beta': 2}, 'beta': None}
</code></pre>

python argparse