Ignoring empty arguments for certain flags - python

I have a command that takes 1 argument and can take several flags.
#click.command()
#click.argument('item')
#click.option('--list-items', help='list items', is_flag=True)
def cli(item, list_items):
if list_items:
click.echo(ITEMS)
return
currently it returns:
Error: Missing argument "item".
How can I make so that I could access the functionality of --list-items even if I don't provide an argument? Just like --help flag does.

You'd have to make item optional:
#click.argument('item', required=False)
then do error handling in the function (e.g. raise a BadParameter() exeception).

You made it mandatory argument, make it optional by either adding required= False or by adding a default value to it

Related

How to use optional argumnets without - or --(dash)

i want to use the optional arguments without - or --,
want to achive something like this:
scriptname install <other options>
scriptname uninstall <other options>
my code:
parser = argparse.ArgumentParser()
parser.add_argument("install","INSTALL",action='store_true',help="INSTALL SOMETHING",default="")
parser.add_argument("uninstall","UNINSTALL",action='store_true',help="UNINSTALL SOMETHING",default="")
args = parser.parse_args()
if args.install:
install logic
if args.uninstall:
uninstall logic
getting the error below
ValueError: invalid option string 'install': must start with a character '-'
A 'store_true' action does not take any arguments (nargs=0). A positional with that action is always true. And it will reject commandline strings like 'install' as unrecognized.
The dash is part of the definition of an optional. It identifies strings that serve as flags or names, as opposed to values. Without it you are defining a positional, an argument that is identified by position rather than a flag string.
So the normal optionals definitions would be:
parser.add_argument("--install",action='store_true',help="INSTALL SOMETHING")
parser.add_argument("--uninstall",action='store_true',help="UNINSTALL SOMETHING")
You could put those in a mutually exclusive group. With store_true the default is False, and if the flag is provided, without any argument, the attribute is set of True.
store_true is allowed with positionals, but doesn't make sense. A positional is required, so you can't get a False value.
You could define a positional with choices:
parser.add_argument('foo', choices=['install', 'uninstall'], help='...')
Then args.foo will have ones of those two string values.
The suggested use of subparsers is a variant on this choices positional - one where the action type is a special one that triggers a new parser.
What about using "sys" module instead of "argparse"? Then answer would be
import sys
if sys.argv[1] == "install":
install logic
elif sys.argv[2] == "uninstall":
uninstall logic
else:
exit

Argparse optional required arguments produce error if missing, but the action is still performed

I have the following snippet:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--p', required=True)
parser.add_argument('arg', action=MyAction)
parser.parse_args()
, where MyAction is a simple custom action class.
As you see, I want to enforce the presence of the p argument. However, my action is performed even if the argument is not present, and then an error message is shown that indicates the fact the the argument is missing.
Obviously, I could check in my action class for the presence of the p argument, but this defies the purpose of having a required parameter in the first place. Why is my action being run if the argument is not present?
Parsing is driven by the commandline strings, and tries to be order agnostic. Within those rules, it alternates between parsing an optional and a positional.
For example, with myprog --p one two:
'--p' - pass the 'one' string to the p action (e.g. setattr(namespace, 'p', 'one')
'two' - matches the nargs for 'arg'. Calls your MyAction.__call__ with values='one'.
at the end of parsing it checks if all required actions have been 'seen'. With your setup both '--p' and 'arg' are required.
With myprog two --p one it does the same, except arg is processed first. The namespace may have a default value for p.
With myprog two, arg is processed, and the required test will raise an error. error: the following arguments are required: --p
Since you have written a custom Action, you can easily explore how the namespace contents vary depending on the commandline arguments and their order.
So the --p and the arg will be processed independently, and in either order, depending on the commandline strings. required testing is performed at the end, using a list of seen_actions. And default values are set at the start of parsing. It is difficult to implement reliable inter-action tests within custom Actions. Usually it is better to perform such tests after parsing.
The defined Actions only change the args namespace. So parsing does not change anything beyond what it returns. Unless there's an error and it forces an sys.exit. A custom MyAction class can change that, but at your own risk.

Argparser with default value and required

I try to build an argparser, where one of the parsers should have a default value, and is also a required.
I have the following so far:
fulllist_parser.add_argument(
'--type',
required=True,
default="VirtualMachine",
type=str,
help='Object type, e.g. Network, VirtualMachine.'
When I run it from CLI, I get an error:
supdeploy fulllist: error: argument --type is required
I see why this is coming up, as I'm not including --type on the CLI.
And that is what I want to achieve, that the default is set even if I don't include the parser option on CLI.
Anyway to run this?
You just have to use default and no required
fulllist_parser.add_argument(
'--type',
default="VirtualMachine",
type=str,
help='Object type, e.g. Network, VirtualMachine.'
Because then if it isn't specified you get the default value back (so you always get a value). Have a look at the documentation:
The default keyword argument of add_argument(), whose value defaults to None, specifies what value should be used if the command-line argument is not present. For optional arguments, the default value is used when the option string was not present at the command line

Setting options from environment variables to positional arguments when using argparse

I have a small program that uses argparse and a positional argument. I'm trying to allow that argument to be set by using an environment variable, but are not getting it to work.
I have seen this post: Setting options from environment variables when using argparse which mentions the same problem, but not for positional arguments.
This is the code so far:
import argparse
import os
class EnvDefault(argparse.Action):
def __init__(self, envvar, required=True, default=None, **kwargs):
if not default and envvar:
if envvar in os.environ:
default = os.environ[envvar]
if required and default:
required = False
super(EnvDefault, self).__init__(default=default, required=required, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('testvar', help="Test variable", action=EnvDefault, envvar='TEST_VAR')
parser.add_argument('--othervar', help="Other variable", action='store_true')
args = parser.parse_args()
if not args.testvar: exit(parser.print_usage())
print args.testvar
Which returns this:
$ TEST_VAR="bla" ./test.py
usage: test.py [-h] [--othervar] testvar
test.py: error: too few arguments
You need to make positional argument optional, try nargs='?':
...
parser.add_argument('testvar', help="Test variable", action=EnvDefault,
envvar='TEST_VAR', nargs='?')
...
Note that output changes slightly:
$ python test.py
usage: test.py [-h] [--othervar] [testvar]
Note: There's one side effect - it doesn't return error, even if env variable is not set.
The too few error message indicates that you have slightly older version of Python/argparse. Here's the code that generates the message. It occurs at the end of parsing:
# if we didn't use all the Positional objects, there were too few
# arg strings supplied.
if positionals:
self.error(_('too few arguments'))
# make sure all required actions were present
for action in self._actions:
if action.required:
if action not in seen_actions:
name = _get_action_name(action)
self.error(_('argument %s is required') % name)
positionals starts a list of all the positional arguments, which are removed as they get matched up with argument strings. So it is testing if any were not matched.
Note that the test of the required attribute occurs after this positional test, so changing it, as your Action does, does not help.
The only way to make a positional optional is with nargs - ? or *. An empty list of strings matches those, so such a positional is always consumed.
Check the docs for these. The const parameter might be useful.
The latest version drops that if positionals test, using only the required test to generate a list of arguments that were not used. Your special Action might work in that code.

Triggering callback on default value in optparse

I'm using Python's optparse to do what it does best, but I can't figure out how to make the option callback trigger on the default argument value if no other is specified via command-line; is this even possible? This would make my code much cleaner.
I can't use argparse unfortunately, as the platform I'm running on has an outdated Python version.
Edit:
To provide more detail, I'm adding an option with a callback and a default value
parser.add_option(
"-f",
"--format",
type = "string",
action = "callback",
callback = format_callback,
default = "a,b,c,d")
The callback function is defined as follows:
def format_callback(option, opt, value, parser):
# some processing
parser.values.V = processed_value
Basically I'm processing the "--format" value and putting the result into the parser. This works fine, when "--format" is specified directly via command-line, but I'd like the callback to be triggered on the default "a,b,c,d" value as well.
It is simply not possible.
The optparse.OptionParser implementation of parse_args starts with:
def parse_args(self, args=None, values=None):
"""
parse_args(args : [string] = sys.argv[1:],
values : Values = None)
-> (values : Values, args : [string])
Parse the command-line options found in 'args' (default:
sys.argv[1:]). Any errors result in a call to 'error()', which
by default prints the usage message to stderr and calls
sys.exit() with an error message. On success returns a pair
(values, args) where 'values' is an Values instance (with all
your option values) and 'args' is the list of arguments left
over after parsing options.
"""
rargs = self._get_args(args)
if values is None:
values = self.get_default_values()
Default values are set before processing any arguments. Actual values then overwrite defaults as options are parsed; the option callbacks are called when a corresponding argument is found.
So callbacks simply cannot be invoked for defaults. The design of the optparse module makes this very hard to change.
You can inject the default when calling parse_args
options, args = parser.parse_args(args=["--option=default"] + sys.argv[1:])
Since flags passed later in the argument list override those passed earlier, this should work. It's possible you may need to modify your callback function to expect this depending on what it is doing.

Categories

Resources