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 - problematic 'default' behavior
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: iritkatriel, matej.tyc, paul.j3
Priority: normal Keywords:

Created on 2013-07-15 22:33 by matej.tyc, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
issue18467.py paul.j3, 2013-07-17 03:17
Messages (6)
msg193135 - (view) Author: Matěj Týč (matej.tyc) Date: 2013-07-15 22:33
I have an issue with passing default values to arguments.
Basically, I would like to have a 'choices' argument (indicating something using capital letters) and perform custom action (adding boolean values to the args namespace).
See the bottom of this post for rationale.

According to the documentation ( http://docs.python.org/2.7/library/argparse.html#default ),
...
If the default value is a string, the parser parses the value as if it were a command-line argument.
...

If this was true, I could implement a custom action. However, even if the default value is a string (as the documentation suggests), the action is bypassed.

My goal is simple - I want to allow short choice-type argument (such as something.py --algorithm Q, the help would say --algorithhm Q|S, Q is quick, S is slow). Since the implementation shouldn't know about the mean one is communicating with the user, I would like to have the 'args' namespace either like (type='Q', quick=True, slow=False) or (type='quick') without giving up usage of the 'choices' pattern.

If I change my mind and add a 'type' function that would expand capital letters abbreviations to full names, I will be OK with default values.
However, if I pass the argument on the command-line, the type() call is performed first and the query whether the result is in the choices list fails.

I consider the current behavior as a bug, IMO the action shouldn't be bypassed if the "default" is string.
msg193206 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-07-17 03:17
I think this example illustrates your issue:

    class FooAction(argparse.Action):
        def __call__(self, parser, namespace, values, 
            option_string = None):
            if values=='Q':
                setattr(namespace, self.dest, 
                    {'type':values, 'quick':True, 'slow':False})
            else:
                setattr(namespace, self.dest, 
                    {'type':values, 'quick':False, 'slow':True})

    def bar(string):
        return string.upper()[0]

    parser = argparse.ArgumentParser()
    parser.add_argument('-a','--algorithm', choices=['Q','S'],
        default='s', action=FooAction, type=bar)
    parser.add_argument('foo', choices=['Q','S'],
        default='s', action=FooAction, nargs='?', type=bar)
    print(parser.parse_args([]))
    # Namespace(algorithm='S',
    #           foo={'slow': True, 'type': 'S', 'quick': False})

For both the optional and positional, the lower case default is converted into an upper case letter.

For the positional with nargs='?', the action is called with the converted default value.

But for the optional, the converted default is assigned to the dest
   algorithm='S'
but action is not called, which would have assigned
   algorithm={'type': 'S', 'quick': False, 'slow': True}

That is consistent with what I remember from the code.  A next function 'take_action' is called only if there is an appropriate argument string.  For this optional that would be '-a' (or the likes).  For the '?' positional and empty argument list enough.

There is a relatively recent change that delayed when a default was converted by type (so a filetype wouldn't be opened unnecessarily).  But I can't think anyway that the action would be invoked on the default in the absence of '-a'.

In this example, would it be possible shift code from the custom action to the custom type?
msg193398 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-07-20 04:26
Since parse_args takes an optional Namespace argument, you can set the its initial value to almost anything.  For example, with the functions defined previously:

    parser = argparse.ArgumentParser()
    parser.add_argument('-a','--algorithm', choices=['Q','S'], action=FooAction, type=bar)
    NS = parser.parse_args(['-a','S'])
    args = parser.parse_args(None, NS)

The first parse_args uses FooAction to create a default Namespace, which is then passed to the second.
msg408530 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-12-14 14:22
Matěj, did Paul's suggestion solve your use case?
msg408555 - (view) Author: Matěj Týč (matej.tyc) Date: 2021-12-14 18:35
Thanks for reaching out. I have no idea whether it helped me or not, sorry for not replying back in a timely manner :-(

I think that the feature request/bugreport originates from bad coding practices, when I wanted argparse to perform something more than just plain parsing of arguments into a data structure.

Nowadays I would tell myself to solve it by introducing a layer between argparse and the rest of the code without putting additional pressure on argparse.
msg408556 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-12-14 18:36
Thanks, I think we can close this then.
History
Date User Action Args
2022-04-11 14:57:48adminsetgithub: 62667
2021-12-14 18:36:48iritkatrielsetstatus: open -> closed
resolution: not a bug
messages: + msg408556

stage: resolved
2021-12-14 18:35:22matej.tycsetstatus: pending -> open

messages: + msg408555
2021-12-14 14:22:51iritkatrielsetstatus: open -> pending
nosy: + iritkatriel
messages: + msg408530

2013-07-20 04:26:46paul.j3setmessages: + msg193398
2013-07-17 03:17:51paul.j3setfiles: + issue18467.py

messages: + msg193206
2013-07-16 21:17:52paul.j3setnosy: + paul.j3
2013-07-15 22:33:10matej.tyccreate