I have the following code to create a container which pretends to behave like the set of all prime numbers (actually hides a memoised brute-force prime test)
import math
def is_prime(n):
if n == 2 or n == 3:
return True
if n == 1 or n % 2 == 0:
return False
else:
return all(n % i for i in xrange(3, int(1 + math.sqrt(n)), 2))
class Primes(object):
def __init__(self):
self.memo = {}
def __contains__(self, n):
if n not in self.memo:
self.memo[n] = is_prime(n)
return self.memo[n]
That seems to be working so far:
>>> primes = Primes()
>>> 7 in primes
True
>>> 104729 in primes
True
>>> 100 in primes
False
>>> 100 not in primes
True
But it's not playing nicely with argparse
:
>>> import argparse as ap
>>> parser = ap.ArgumentParser()
>>> parser.add_argument('prime', type=int, choices=primes, metavar='p')
_StoreAction(option_strings=[], dest='prime', nargs=None, const=None, default=None, type=<type 'int'>, choices=<__main__.Primes object at 0x7f4e21783f10>, help=None, metavar='p')
>>> parser.parse_args(['7'])
Namespace(prime=7)
>>> parser.parse_args(['11'])
Namespace(prime=11)
>>> parser.parse_args(['12'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/argparse.py", line 1688, in parse_args
args, argv = self.parse_known_args(args, namespace)
File "/usr/lib/python2.7/argparse.py", line 1720, in parse_known_args
namespace, args = self._parse_known_args(args, namespace)
File "/usr/lib/python2.7/argparse.py", line 1929, in _parse_known_args
stop_index = consume_positionals(start_index)
File "/usr/lib/python2.7/argparse.py", line 1885, in consume_positionals
take_action(action, args)
File "/usr/lib/python2.7/argparse.py", line 1778, in take_action
argument_values = self._get_values(action, argument_strings)
File "/usr/lib/python2.7/argparse.py", line 2219, in _get_values
self._check_value(action, value)
File "/usr/lib/python2.7/argparse.py", line 2267, in _check_value
tup = value, ', '.join(map(repr, action.choices))
TypeError: argument 2 to map() must support iteration
The docs just say that
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.
Obviously I don't want to iterate the infinite "set" of primes. So why the heck is argparse
trying to map
my primes? Doesn't it just need in
and not in
?
You can set nargs='+' , which will allow the argument to take in any number of values. Using the same example above, with the only change being nargs=3 to nargs='+' , you can run the script with however many input values you want.
Python argparse optional argument The example adds one argument having two options: a short -o and a long --ouput . These are optional arguments. The module is imported. An argument is added with add_argument .
parse_args() returns two values: options, an object containing values for all of your options— e.g. if "--file" takes a single string argument, then options. file will be the filename supplied by the user, or None if the user did not supply that option.
The argparse module provides a convenient interface to handle command-line arguments. It displays the generic usage of the program, help, and errors. The parse_args() function of the ArgumentParser class parses arguments and adds value as an attribute dest of the object.
The source where you're getting the exception is pretty clear, check it out:
if action.choices is not None and value not in action.choices:
tup = value, ', '.join(map(repr, action.choices))
msg = _('invalid choice: %r (choose from %s)') % tup
raise ArgumentError(action, msg)
The check itself is fine. It borks on trying to print out a helpful error message, where it's trying to give you all the possible choices. I suppose if you define the iterator to return just something that has as its repr
the string primes
, you might hack it to do the rightish thing.
It was a documentation bug. This was issue16468, fixed in August 2019 by PR 15566.
The library as written requires the choices
argument to be not just a container but also iterable, It tries to list the available options, which isn't going to work for your case. You could try to hack it by giving it a fake __iter__
that just returns some informational string.
choices
are for arguments that you can enumerate all allowed values (a finite (small) set). The docs should be more clear about it.
primes
is an infinite set. You could set type
parameter to raise ValueError for non-primes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With