argparse: Emulating GCC's "-fno-<option>" semantics
<p>One nice feature of GCC's command line parsing is that most flags of the form "-fmy-option" have a negative version called "-fno-my-option". The rightmost occurrence takes precedence, so you can just append "-fno-my-option" to your CFLAGS or similar in a Makefile to disable an option without clobbering the other flags.</p>

<p>I'd like to support something similar in a tool whose wrapper script is Python and uses argparse. The obvious hack of just defining both versions of the argument with an action of <code>store_true</code> doesn't work, because that won't let me ask for the rightmost occurrence.</p>

<p>Obviously, it's easy to support a syntax like <code>--my-option=yes</code> / <code>--my-option=no</code>, but it would be nice for the user not to have to pass the parameter.</p>

<p>Is there a way to get argparse to have an on/off switch for a boolean flag like this?</p>

<p>Without any fancy foot work I can setup a pair of arguments that write to the same <code>dest</code>, and take advantage of the fact that the last write is the one that sticks:</p>

<pre><code>In [765]: parser=argparse.ArgumentParser()
In [766]: a1=parser.add_argument('-y',action='store_true')
In [767]: a2=parser.add_argument('-n',action='store_false')
</code></pre>

<p>Without a <code>dest</code> parameter these use a name deterived from the option strings.  But I can give a <code>dest</code>, or change that value after creation:</p>

<pre><code>In [768]: a1.dest
Out[768]: 'y'
In [769]: a2.dest
Out[769]: 'n'
In [770]: a1.dest='switch'
In [771]: a2.dest='switch'
</code></pre>

<p>Now use of either will set the <code>switch</code> attribute.</p>

<pre><code>In [772]: parser.parse_args([])
Out[772]: Namespace(switch=False)
</code></pre>

<p>The default comes from the first defined argument.  That's a function of how defaults are set at the start of parsing.  For all other inputs, it's the last argument that sets the value</p>

<pre><code>In [773]: parser.parse_args(['-y'])
Out[773]: Namespace(switch=True)
In [774]: parser.parse_args(['-n'])
Out[774]: Namespace(switch=False)
In [775]: parser.parse_args(['-n','-y','-n','-y'])
Out[775]: Namespace(switch=True)
In [776]: parser.parse_args(['-n','-y','-n'])
Out[776]: Namespace(switch=False)
</code></pre>

<p>The default could also be set with a separate command:</p>

<pre><code>parser.set_defaults(switch='foo')
</code></pre>

<p>If you wanted to use this sort of feature a lot you could write a little utility function that creates the pair of arguments with any flags and dest you want.  There's even a bug/issues request for such an enhancement, but I doubt if it will be implemented.</p>

python command-line-arguments argparse