classification
Title: Make argparse.SUPPRESS work as an argument "dest"
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.1, Python 3.2, Python 3.3, Python 3.4, Python 3.5, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: jzwinck, paul.j3
Priority: normal Keywords:

Created on 2014-01-29 02:52 by jzwinck, last changed 2016-04-25 05:24 by paul.j3.

Files
File name Uploaded Description Edit
issue20430.py paul.j3, 2014-05-28 21:29
Messages (5)
msg209611 - (view) Author: (jzwinck) Date: 2014-01-29 02:52
argparse.SUPPRESS is a special object which can be used in various parts of argparse to say "do nothing."  One place where it does not seem to work is in an argument's "dest".  This is despite some of the plumbing using "dest=SUPPRESS" internally.  It can be made to work by patching argparse._StoreAction.__call__, like this:

 def __call__(self, parser, namespace, values, option_string=None):
-    setattr(namespace, self.dest, values)
+    if self.dest is not argparse.SUPPRESS:
+        setattr(namespace, self.dest, values)

Once that's done, this works as one might expect:

parser.add_argument('--foo', dest=argparse.SUPPRESS)

With the patch, 'foo' will not appear if you parse args containing "--foo bar".  Without the patch, args looks like this, which is not really useful:

Namespace(==SUPPRESS==='bar')

Note that the _SubParsersAction.__call__ code has something like the above patch already.
msg219254 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-05-28 05:49
If we make this change to '_StoreAction', we need to do it to 4 other subclasses which take the same 'setattr(namespace, self.dest, values)'.

An alternative would be to define a 'Namespace' function that does this conditional 'setattr'.

How should this 'SUPPRESS' affect the usage and help?  I'm seeing "--foo ==SUPPRESS==".

Do you have practical need for such a 'SUPPRESS', or are you just trying to make behavior consistent?  'SUPPRESS' is used for '--help' and subparsers because those Actions have important side effects, and we don't want default values to appear in the Namespace.  With '_StoreAction', the only effect is to store a value in the Namespace.
msg219264 - (view) Author: (jzwinck) Date: 2014-05-28 11:34
Yes, I have a practical need for dest=SUPPRESS.  One of the reasons is basically what you said: to implement things analogous to --help.  In those cases you want to be able to invoke a function (e.g. via type=myfunc) but not store anything.  Or you may need to ignore an argument completely for compatibility, but you have logic which expects all the arguments returned by parse_args() to be known and meaningful.  In any case, I wrote this ticket because I ran into a concrete need that argparse didn't readily support (which almost never happens!).

As for what the usage statement should show: it should behave as if dest=SUPPRESS were not present.  If metavar is specified, use that, otherwise use the option name as if dest were not passed in.  And we already have the behavior that help=SUPPRESS will hide it from the usage entirely, so that should still work (i.e. dest=SUPPRESS, help=SUPPRESS should hide the option both from --help and the result of parse_args()).
msg219302 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-05-28 21:29
In the attached file I tried another approach - a custom Action class.  It gives you the custom behavior now, instead of 2-3 releases in the future.

    class Action2(Action):
        # custom action
        def __init__(self, *args, **kwargs):
            super(Action2, self).__init__(*args, **kwargs)
            self.default = SUPPRESS 
        def __call__(self, parser, namespace, values, option_string=None):
           print('Action2', argparse._get_action_name(self), values)

'self.default=SUPPRESS' keeps the dest out of the namespace without affecting help display.  And as with '_HelpAction', the __call__ can do its own thing without modifying the namespace.

One thing that this custom Action class does not do well is let you modify the handling of the argument after it is created.  For example if an argument is created by an imported parent parser, attributes like 'dest' and 'default' can be changed after the fact, but the argument class can't.

In   http://bugs.python.org/issue14191  I wrestled with the issue of temporarily disabling subsets of the arguments, first the positionals, and then the optionals, so I could run 'parse_known_args' separately on the two groups.

For optionals it was enough to ensure that 'required=False'.  For positionals I first used 'nargs=0', and latter enabled a 'nargs=SUPPRESS' option to suppress them.
msg264153 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2016-04-25 05:24
I just found a case where `dest` is `SUPPRESS` - the default subparsers setup.

      _SubParsersAction(option_strings=[], dest='==SUPPRESS==', nargs='A...', ...

If I make this Action 'required' (in the current distribution 'subparsers' are not required - there's a bug/issue for that)

     parser._actions[1].required=True

and call the parser without the required subparser argument I should get an error message.  In earlier versions this would have been a non specific,  `error: too few arguments', but the new one tries to name the missing argument, e.g.

    program: error: the following arguments are required: choice

But with SUPPRESS I get a further error as _parse_known_args tries to format this error message:

C:\Users\paul\Miniconda3\lib\argparse.py in _parse_known_args(self, arg_strings, namespace)
   1991         if required_actions:
   1992             self.error(_('the following arguments are required: %s') %
-> 1993                        ', '.join(required_actions))

TypeError: sequence item 0: expected str instance, NoneType found

(This error may have been reported elsewhere.  This issue is the first one I found that deals with `dest=argparse.SUPPRESS`.)
History
Date User Action Args
2016-04-25 05:24:44paul.j3setmessages: + msg264153
2014-05-28 21:29:48paul.j3setfiles: + issue20430.py

messages: + msg219302
2014-05-28 11:34:05jzwincksetmessages: + msg219264
2014-05-28 05:50:00paul.j3setnosy: + paul.j3
messages: + msg219254
2014-01-29 02:56:53jzwincksettitle: argparse.SUPPRESS -> Make argparse.SUPPRESS work as an argument "dest"
2014-01-29 02:52:34jzwinckcreate