python- helper list for sys.argv CLI argument - python

I have to read a CLI argument for my python script and then pass that input argument by the user to a method. I want to provide user with a list of valid arguments that she can enter, sort of when you run the script with an invalid argument or with no argument passed- it shows you that you can only enter these arguments.
I know we can achieve this with argparse module, but argparse in my case will be an overkill- since I just want to pass whatever (valid)argument the user gave to a method. How should I achieve this?
parser.add_argument('--system-status', '-st', metavar='<host>', type=str)
if args.system-status:
method(args.system-status)
And the same stuff repeated for 5 other variables?

The argparse lib is the way to go, if you dont want to check for each parameter you could use something like this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.add_argument('--bar')
parser.add_argument('--baz')
args = parser.parse_args()
def f(foo=None, bar=None, baz=None, **kwargs):
print(foo)
print(bar)
print(baz)
f(**args.__dict__)
**args.__dict__ will turn args in a dictionary turn it into kwargs.
If you really absolutly don't want to use argparse then sys.argv will give you the list of parameters passed to the function.

Related

Disable argparse arguments from being overwritten

I have a legacy Python application which uses some options in its CLI, using argparse, like:
parser = argparse.ArgumentParser()
parser.add_argument('-f', default='foo')
Now I need to remove this option, since now its value cannot be overwritten by users but it has to assume its default value (say 'foo' in the example). Is there a way to keep the option but prevent it to show up and be overwritten by users (so that I can keep the rest of the code as it is)?
It's not entirely clear what you can do, not with the parser. Can you edit the setup? Or just modify results of parsing?
If you can edit the setup, you could replace the add_argument line with a
parser.setdefaults(f='foo')
https://docs.python.org/3/library/argparse.html#parser-defaults
The -f won't appear in the usage or help, but it will appear in the args
Or you could leave it in, but suppress the help display
parser.add_argument('-f', default='foo', help=argparse.SUPPRESS)
https://docs.python.org/3/library/argparse.html#help
Setting the value after parsing is also fine.
Yes you can do that. After the parser is parsed (args = parser.parse_args()) it is a NameSpace so you can do this:
parser = argparse.ArgumentParser()
args = parser.parse_args()
args.foo = 'foo value'
print(args)
>>> Namespace(OTHER_OPTIONS, foo='foo value')
I assumed that you wanted to add test to your parser, so your original code will still work, but you do not want it as an option for the user.
I think it doesn't make sense for the argparse module to provide this as a standard option, but there are several easy ways to achieve what you want.
The most obvious way is to just overwrite the value after having called parse_args() (as already mentioned in comments and in another answer):
args.f = 'foo'
However, the user may not be aware that the option is not supported anymore and that the application is now assuming the value "foo". Depending on the use case, it might be better to warn the user about this. The argparse module has several options to do this.
Another possibility is to use an Action class to do a little magic. For example, you could print a warning if the user provided an option that is not supported anymore, or even use the built-in error handling.
import argparse
class FooAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if values != 'foo':
print('Warning: option `-f` has been removed, assuming `-f foo` now')
# Or use the built-in error handling like this:
# parser.error('Option "-f" is not supported anymore.')
# You could override the argument value like this:
# setattr(namespace, self.dest, 'foo')
parser = argparse.ArgumentParser()
parser.add_argument('-f', default='foo', action=FooAction)
args = parser.parse_args()
print('option=%s' % args.f)
You could also just limit the choices to only "foo" and let argparse create an error for other values:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-f', default='foo', choices=['foo'])
args = parser.parse_args()
print('option=%s' % args.f)
Calling python test.py -f bar would then result in:
usage: test.py [-h] [-f {foo}]
test.py: error: argument -f: invalid choice: 'bar' (choose from 'foo')

Python argparse empty

I'm coding a script that I need to execute with the following line (this is a compulsory request for an assignment).
python3 my_program.py method_name
Where method_name is the name of the algorithm in the script that I want to run in this execution. To do this I'm using argparse like this:
parser = argparse.ArgumentParser(description=None)
parser.add_argument('--method', type=str, help='Method')
args = parser.parse_args([])
If I print the args this is the result:
print(args)
Namespace(method=None)
So apparently args is empty even it seems like it read the execution line correctly, because I did not get any error message. Why is my code not reading the name of the method? Should I be using a different method than argparse to read the method_name with the requested line? Or am I using argparse wrong?
Thank you! :)
Because you're explicitly passing an empty list to .parse_args(). Just use:
args = parser.parse_args()
to capture the passed --method into the argparse.Namespace.
Detail: if you look at the argparse.py code in CPython, you'll see the following:
def parse_known_args(self, args=None, namespace=None):
if args is None:
# args default to the system args
args = _sys.argv[1:]
else:
# make sure that args are mutable
args = list(args)
You want the if args is None branch (that's the implicit with parse_args()), which will actually capture sys.argv[:1], e.g. the options passed on the command line.

Python argaprse optional arguments handling

So, I have a python script for parsing and plotting data from text files. Argument handling is done with argparse module. The problem is, that some arguments are optional, e.g. one of the is used to add text annotations on the plot. This argument is sent to plotting function via **kwargs. My question is - what is the most pythonic way to handle those optional arguments? Some pseudo code here:
parser = argparse.ArgumentParser()
...
parser.add_argument("-o", "--options", nargs="+", help="additional options")
args = parser.parse_args()
...
def some_function(arguments, **kwargs):
doing something with kwargs['options']
return something
...
arguments = ...
some_function(arguments, options=args.options)
If options are not specified by default None value is assigned. And it causes some problems. What is more pythonic - somehow check 'options' within some_function? Or maybe parse arguments before some_function is called?
You can just provide an explicit empty list as the default.
parser.add_argument("-o", "--options", nargs="+", default=[])
use get and set a default value if the key is not found in the dict
def some_function(arguments, **kwargs):
something = kwargs.get('options', 'Not found')
return something
or an if statement
if 'option' in kwargs:
pass # do something

Python argparse : how to detect duplicated optional argument?

I'm using argparse with optional parameter, but I want to avoid having something like this : script.py -a 1 -b -a 2
Here we have twice the optional parameter 'a', and only the second parameter is returned. I want either to get both values or get an error message.
How should I define the argument ?
[Edit]
This is the code:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', dest='alpha', action='store', nargs='?')
parser.add_argument('-b', dest='beta', action='store', nargs='?')
params, undefParams = self.parser.parse_known_args()
append action will collect the values from repeated use in a list
parser.add_argument('-a', '--alpha', action='append')
producing an args namespace like:
namespace(alpha=['1','3'], b='4')
After parsing you can check args.alpha, and accept or complain about the number of values. parser.error('repeated -a') can be used to issue an argparse style error message.
You could implement similar functionality in a custom Action class, but that requires understanding the basic structure and operation of such a class. I can't think anything that can be done in an Action that can't just as well be done in the appended list after.
https://stackoverflow.com/a/23032953/901925 is an answer with a no-repeats custom Action.
Why are you using nargs='?' with flagged arguments like this? Without a const parameter this is nearly useless (see the nargs=? section in the docs).
Another similar SO: Python argparse with nargs behaviour incorrect

argparse key=value parameters

This first link has the same question in the first section, but it is unanswered
(python argparse: parameter=value). And this second question is similar, but I can't seem to get it working for my particular case
( Using argparse to parse arguments of form "arg= val").
So my situation is this -- I am re-writing a Python wrapper which is used by many other scripts (I would prefer not to modify these other scripts). Currently, the Python wrapper is called with command line arguments of the form --key=value for a number of different arguments, but was parsed manually. I would like to parse them with argparse.
N.B. The argument names are unwieldy, so I am renaming using the dest option in add_argument.
parser = argparse.ArgumentParser(description='Wrappin Ronnie Reagan')
parser.add_argument("--veryLongArgName1", nargs=1, dest="arg1", required=True)
parser.add_argument("--veryLongArgName2", nargs=1, dest="arg2")
parser.add_argument("--veryLongArgName3", nargs=1, dest="arg3")
userOpts = vars(parser.parse_args())
Which, while apparently parsing the passed command lines correctly, displays this as the help:
usage: testing_argsparse.py [-h] --veryLongArgName1 ARG1
[--veryLongArgName2 ARG2]
[--veryLongArgName3 ARG3]
testing_argsparse.py: error: argument --veryLongArgName1 is required
But what I want is that all parameters are specified with the --key=value format, not --key value. i.e.
usage: testing_argsparse.py [-h] --veryLongArgName1=ARG1
[--veryLongArgName2=ARG2]
[--veryLongArgName3=ARG3]
testing_argsparse.py: error: argument --veryLongArgName1 is required
testing_argsparse.py --veryLongArgName1=foo
works. argparse module accepts both --veryLongArgName1=foo and --veryLongArgName1 foo formats.
What exact command line arguments are you trying to pass to argparse that's causing it to not work?
A little late but for anyone with a similar request as the OP you could use a custom HelpFormatter.
class ArgFormatter(argparse.HelpFormatter):
def _format_args(self, *args):
result = super(ArgFormatter, self)._format_args(*args)
return result and '%%%' + result
def _format_actions_usage(self, *args):
result = super(ArgFormatter, self)._format_actions_usage(*args)
return result and result.replace(' %%%', '=')
This can then be passed to ArgumentParser to give the wanted behavior.
parser = argparse.ArgumentParser(
description='Wrappin Ronnie Reagan',
formatter_class=ArgFormatter)
This intercepts the args (ARG1, ARG2, ...) and adds a custom prefix which is later replaced (along with the unwanted space) for an = symbol. The and in the return statements makes sure to only modify the result if it's non-empty.

Categories