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.

classification
Title: argparse: type= doesn't honor nargs > 1
Type: behavior Stage: resolved
Components: Documentation Versions: Python 3.4, Python 3.5, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Chris.Bruner, docs@python, paul.j3, r.david.murray
Priority: normal Keywords:

Created on 2014-07-23 17:16 by Chris.Bruner, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
argparse_typedef_bug.py Chris.Bruner, 2014-07-23 17:16 Reproducer
Messages (6)
msg223748 - (view) Author: Chris Bruner (Chris.Bruner) Date: 2014-07-23 17:16
From the documentation, I think that argparse should pass the entire nargs-long string to the "type=" callable. Instead, it only passes the first argument (of nargs), making it impossible within argparse's framework to check for a tuple of mixed types, e.g., 2 ints and a float (my case), or a string and three numbers, etc.

See attached reproducer.
msg223756 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-07-23 18:20
Nope.  The documentation says:

    N (an integer). N arguments from the command line will be gathered together into a list

The type function is applied to each argument independently.  It would be easy enough to make this explicit in one of the nargs multi-argument examples by using type as well.
msg223916 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-25 03:42
Note that 

    '-t 1 2 3'.split()

becomes

    ['-t', '1', '2', '3']

Your 'type' function sees those 3 strings individually.  Try printing 'string' the first thing in your function to see what we mean.
msg223975 - (view) Author: Chris Bruner (Chris.Bruner) Date: 2014-07-25 17:29
Yes, I know. My function just sees '1', but I think it should see '1 2 3' so that it can figure out what to do. That's impossible (well, impossible without saving state between calls) when it sees the arguments piecemeal. 

Sent from my iPhone

> On Jul 24, 2014, at 9:42 PM, paul j3 <report@bugs.python.org> wrote:
> 
> 
> paul j3 added the comment:
> 
> Note that 
> 
>    '-t 1 2 3'.split()
> 
> becomes
> 
>    ['-t', '1', '2', '3']
> 
> Your 'type' function sees those 3 strings individually.  Try printing 'string' the first thing in your function to see what we mean.
> 
> ----------
> nosy: +paul.j3
> 
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue22049>
> _______________________________________
msg224009 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-25 22:17
What you want is a custom Action rather than a custom Type.  

from the documentation:

    >>> class FooAction(argparse.Action):
    ...     def __call__(self, parser, namespace, values, option_string=None):
    ...         print('%r %r %r' % (namespace, values, option_string))
    ...         setattr(namespace, self.dest, values)

'values' will be the list ['1','2','3'], which you test and manipulate, before finally saving it to the 'namespace'.

    ret = (int(values[0]), int(values[1]), float(values[2]))
    setattr(namespace, self.dest, ret)

Setting 'nargs=3' ensures that this action will always get a 3 item list.  If the parser can't give it 3 items, it will raise an error rather than call your Action.

'optparse' passed the remaining argument strings to Option's callback, which could consume as many as it wanted.  'argparse' does not give the Actions that power.  There is a fundamental difference in the parsing algorithm.
msg224240 - (view) Author: Chris Bruner (Chris.Bruner) Date: 2014-07-29 19:53
Just had a chance to try this, and this does exactly what I wanted from
"type=". Thank you!

On Fri, Jul 25, 2014 at 4:17 PM, paul j3 <report@bugs.python.org> wrote:

>
> paul j3 added the comment:
>
> What you want is a custom Action rather than a custom Type.
>
> from the documentation:
>
>     >>> class FooAction(argparse.Action):
>     ...     def __call__(self, parser, namespace, values,
> option_string=None):
>     ...         print('%r %r %r' % (namespace, values, option_string))
>     ...         setattr(namespace, self.dest, values)
>
> 'values' will be the list ['1','2','3'], which you test and manipulate,
> before finally saving it to the 'namespace'.
>
>     ret = (int(values[0]), int(values[1]), float(values[2]))
>     setattr(namespace, self.dest, ret)
>
> Setting 'nargs=3' ensures that this action will always get a 3 item list.
>  If the parser can't give it 3 items, it will raise an error rather than
> call your Action.
>
> 'optparse' passed the remaining argument strings to Option's callback,
> which could consume as many as it wanted.  'argparse' does not give the
> Actions that power.  There is a fundamental difference in the parsing
> algorithm.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue22049>
> _______________________________________
>
History
Date User Action Args
2022-04-11 14:58:06adminsetgithub: 66248
2017-03-28 16:50:13paul.j3setstatus: open -> closed
resolution: not a bug
stage: needs patch -> resolved
2014-07-29 19:53:26Chris.Brunersetmessages: + msg224240
2014-07-25 22:17:03paul.j3setmessages: + msg224009
2014-07-25 17:29:46Chris.Brunersetmessages: + msg223975
2014-07-25 03:42:03paul.j3setnosy: + paul.j3
messages: + msg223916
2014-07-23 18:20:10r.david.murraysetassignee: docs@python
components: + Documentation
versions: + Python 3.4, Python 3.5
nosy: + docs@python, r.david.murray

messages: + msg223756
stage: needs patch
2014-07-23 17:16:22Chris.Brunercreate