Message180067
I took a good look at the 3.3 code. With respect to the main purpose of choices -- checking user input -- argparse does not require that choices be iterable, as it *does* use 'in', as it should. Line 2274:
if action.choices is not None and value not in action.choices:
So unless the usage message is generated even when not needed (I did not delve that far), non-iterables should work now as long as the user does not request the usage message or make an input mistake.
If that is so, then this issue is strictly about the string representation of non-iterable choices. A mis-behaving tail is not a reason to kill the dog ;-). The easy answer, and the only sensible one I see, is to use either str() or repr(). But how to do that? I think this and #16418 have to be fixed together.
The current format-by-iteration method, used for both usage and exceptions, works well for small iterable collections. But it is obnoxious for non-small collections. As I mentioned before, it will just hang for infinite iterables, which is even worse. So the method needs to be changed anyway. And to do that, it should be factored out of the three places where it is currently done in-line.
At 557:
elif action.choices is not None:
choice_strs = [str(choice) for choice in action.choices]
result = '{%s}' % ','.join(choice_strs)
To match the code below, so it can be factored out into a function,
change the last two lines to
choices_str = ','.join(str(c) for c in action.choices)
result = '{%s}' % choices_str
At 597: (note that 'params' is adjusted action.__dict__)
if params.get('choices') is not None:
choices_str = ', '.join([str(c) for c in params['choices']])
params['choices'] = choices_str
The intermediate list in the 2nd line is not needed
choices_str = ', '.join(str(c) for c in params['choices'])
I am aware of but do not understand ',' versus ', ' as joiner. I also do not understand why both versions of choices_str are needed. Are there two different usage messages?
At 2276:
'choices': ', '.join(map(repr, action.choices))}
or, replacing map with comprehension
choices_str = ', '.join(repr(c) for c in action.choices)
'choices': choices_str}
Now define choices_str(src, joiner, rep), delete 559 and 598, and modify
559: ... result = '{%s}' % choices_str(action.choices, ',', str)
599: ... params['choices'] = choices_str(param['choices'], ', ', str)
2276: ... 'choices': choices_str(action.choices, ', ', repr}
(I am assuming that the two joiners are really needed. If not, delete.)
Now we should be able to write choices_str to solve all 3 problems in the two issues. My coded suggestion:
from itertools import islice
N = 21 # maximum number (+1) of choices for the current nice string.
# This is just an illustrative value, experiment might show better #.
def choices_str(src, joiner, rep):
prefix = list(islice(src, N))
if len(prefix) < N: # short iterables
return joiner.join(rep(c) for c in prefix) # current string
else:
try: # non-short sliceable finite sequences
head = joiner.join(rep(c) for c in src[:N//2])
tail = joiner.join(rep(c) for c in src[N//4:])
return head + ' ..., ' + tail
except AttributeError: # no __getindex__(slice), everything else
return repr(src) |
|
Date |
User |
Action |
Args |
2013-01-16 01:10:16 | terry.reedy | set | recipients:
+ terry.reedy, bethard, r.david.murray, chris.jerdonek, wim.glenn |
2013-01-16 01:10:16 | terry.reedy | set | messageid: <1358298616.23.0.814706081214.issue16468@psf.upfronthosting.co.za> |
2013-01-16 01:10:16 | terry.reedy | link | issue16468 messages |
2013-01-16 01:10:15 | terry.reedy | create | |
|