This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author terry.reedy
Recipients bethard, chris.jerdonek, r.david.murray, terry.reedy, wim.glenn
Date 2013-01-16.01:10:15
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
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
    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:16terry.reedysetrecipients: + terry.reedy, bethard, r.david.murray, chris.jerdonek, wim.glenn
2013-01-16 01:10:16terry.reedysetmessageid: <>
2013-01-16 01:10:16terry.reedylinkissue16468 messages
2013-01-16 01:10:15terry.reedycreate