Correct way to get allowed arguments from ArgumentParser
<p><strong>Question:</strong> What is the intended / official way of accessing possible arguments from an existing <code>argparse.ArgumentParser</code> object?</p>

<p><strong>Example:</strong> Let's assume the following context:</p>

<pre><code>import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', '-f', type=str)
</code></pre>

<p>Here I'd like to get the following list of allowed arguments:</p>

<pre><code>['-h', '--foo', '--help', '-f']
</code></pre>

<p>I found the following workaround which does the trick for me</p>

<pre><code>parser._option_string_actions.keys()
</code></pre>

<p>But I'm not happy with it, as it involves accessing a <code>_</code>-member that is not officially documented. Whats the correct alternative for this task?</p>

<p>This started as a joke answer, but I've learned something since - so I'll post it.</p>

<p>Assume, we know the maximum length of an option allowed. Here is a nice answer to the question in this situation:</p>

<pre><code>from itertools import combinations

def parsable(option):
    try:
        return len(parser.parse_known_args(option.split())[1]) != 2
    except:
        return False

def test(tester, option):
    return any([tester(str(option) + ' ' + str(v)) for v in ['0', '0.0']])

def allowed_options(parser, max_len=3, min_len=1):
    acceptable = []
    for l in range(min_len, max_len + 1):
        for option in combinations([c for c in [chr(i) for i in range(33, 127)] if c != '-'], l):
            option = ''.join(option)
            acceptable += [p + option for p in ['-', '--'] if test(parsable, p + option)]
    return acceptable
</code></pre>

<p>Of course this is very pedantic as the question doesn't require any specific runtime. So I'll ignore that here. I'll also disregard, that the above version produces a mess of output because <a href="http://stackoverflow.com/a/2829036/2747160">one can get rid of it easily</a>.</p>

<p>But more importantly, this method detected the following interesting <code>argparse</code> "features":</p>

<ul>
<li>In in the OP example, <code>argparse</code> would also allow <code>--fo</code>. This has to be a bug.</li>
<li>But further, in the OP example again, <code>argparse</code> would also allow <code>-fo</code> (ie. setting <code>foo</code> to <code>o</code> without space or anything). This is documented and intended, but I didn't know it.</li>
</ul>

<p>Because of this, a correct solution is a bit longer and would look something like this (only <code>parsable</code> changes, I'll omit the other methods):</p>

<pre><code>def parsable(option):
    try:
        default = vars(parser.parse_known_args(['--' + '0' * 200])[0])
        parsed, remaining = parser.parse_known_args(option.split())
        if len(remaining)  == 2:
            return False
        parsed = vars(parsed)
        for k in parsed.keys():
            try:
                if k in default and default[k] != parsed[k] and float(parsed[k]) != 0.0:
                    return False  # Filter '-fx' cases where '-f' is the argument and 'x' the value.
            except:
                return False
        return True
    except:
        return False
</code></pre>

<p><strong>Summary</strong>: Besides all the restrictions (runtime and fixed maximum option length), this is the only answer that correctly respects the real <code>parser</code> behavior - however buggy it may even be. So here you are, a perfect answer that is absolutely useless.</p>

python argparse