How can I make it a required argument only if I don't select another option, so when I select version it shouldn't require -f, but the rest of the time it should be required?
parser = argparse.ArgumentParser(
description="This script will check the uri's from XXX")
parser.add_argument(
"-f", "--file", help="XXX export file to use", required=True)
parser.add_argument("-c", "--check", action="store_true",
help="Check the uri's")
parser.add_argument("-p", "--passwords", action="store_true",
help="Check the weak passwords")
parser.add_argument("-V", "--version", action="store_true",
help="Show version")
self.params = parser.parse_args(self.get_params())
So, two things:
In the general case, this is done with add_mutually_exclusive_group(required=True) and adding the mutually exclusive arguments to that group (this isn't exactly what you want, since it won't allow you to pass both, but it's close enough).
In this specific case, you should be using the action='version' action for displaying the version, which exists specifically for this purpose, and leave -f plain required=True as you've already written it.
Related
I've read the documentation but still can't figure out how to achieve the following behavior:
1.likes. Give a specified number of likes to users in a tinder (in the future, this may contain more options, such as "gender", "frequency", "age", etc.)
2. Write a given text to a given number of people on tinder (in the future, there may also be more options).
There is my code:
parser = argparse.ArgumentParser(description='Badoo liker', epilog='Enjoy the program! :)')
# I also tried "add_mutually_exclusive_group" instead of "add_argument_group"
chat_args = parser.add_argument_group(title='chat_args')
chat_args.add_argument('-c', '--chat', help='chat help')
chat_args.add_argument('-t', '--text', help='text help')
chat_args.add_argument('-n', '--number', help='n help')
like_args = parser.add_argument_group(title='like_args')
like_args.add_argument('-l', '--like', help='like help')
like_args.add_argument('-n', '--number', help='n help')
args = parser.parse_args()
Usage:
$script.py chat --text 'Hello world' -n 20 # Var 1
$script.py liking -n 20 # Var 2
Obviously, I'm waiting for arguments either for a chat or either for liking
P.S. I'm getting an error because of -n common argument, but even if comment it it will not working as expecting
It's a little hard to tell what you understand and what has worked or not for you.
This is more of a comment, but long enough that I'll make it answer.
Are those usage lines samples of what you use when calling this script? What error(s) do you get?
Have you tried scipt.py -h to see the help?
I don't see a positional argument that would accept a string like "chat" or "liking". I suspect you want to use the subcommands mechanism, but get the basic argparse working.
I often suggest including a print(args) line to get a clear idea of what the parser has done. Though obviously you won't see that while argparse is raising errors.
Postpone the use of groups until you get the basics down. argument_group just groups arguments in the help display. mutually_exclusive_group is a parsing tool, that complains if you try to use more than one item in the group.
Eventually, I did it, the next code is (apparently) meets my question
# "metavar=''" -just hide redundant double --key KEY word
parser = argparse.ArgumentParser(description='Badoo liker', epilog='Enjoy the program! :)')
subparsers = parser.add_subparsers(help='')
parser_like = subparsers.add_parser('like', help='like help')
parser_like.add_argument('-n', '--number', metavar='', help='Set count of likes', default=49)
parser_like.add_argument('-f', '--frequency', metavar='', help='Set chance to like/dislike', default=70)
parser_chat = subparsers.add_parser('chat', help='chat help')
parser_chat.add_argument('-n', '--number', metavar='', help='number help', required=True)
parser_chat.add_argument('-t', '--text', metavar='', help='text help', required=True)
args = parser.parse_args()
I am using argparse to parse user args. There are defaults provided for each argument, but due to my special need, I would need to also get a list of args that are specified by the user.
Example:
parser = argparse.ArgumentParser(description="Generic Program")
parser.add_argument('-a', type=int, default=1, )
parser.add_argument('-b', type=int, default=2, )
parser.add_argument('-c', type=int, default=3, )
args = parser.parse_args()
Calling:
main.py -a 1 -b 2
I would like to know that the user actually only specified -a and -b but not -c. I cannot seem to see how that is done through the argparse official docs. Using sys.argv would've been too low level and too messy to deal with the raw parsing.
Don't supply a default. You can set the value to 3 once you have determined that it was unset. This adds more work for you, but it seems like the easiest way to do what you are asking.
I have a requirement as follows:
./xyifier --prox --lport lport --rport rport
for the argument prox , I use action='store_true' to check if it is present or not.
I do not require any of the arguments. But, if --prox is set I require rport and lport as well. Is there an easy way of doing this with argparse without writing custom conditional coding.
More Code:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', type=int, help='Listen Port.')
non_int.add_argument('--rport', type=int, help='Proxy port.')
No, there isn't any option in argparse to make mutually inclusive sets of options.
The simplest way to deal with this would be:
if args.prox and (args.lport is None or args.rport is None):
parser.error("--prox requires --lport and --rport.")
Actually there's already an open PR with an enhancement proposal :
https://github.com/python/cpython/issues/55797
You're talking about having conditionally required arguments. Like #borntyping said you could check for the error and do parser.error(), or you could just apply a requirement related to --prox when you add a new argument.
A simple solution for your example could be:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', required='--prox' in sys.argv, type=int)
non_int.add_argument('--rport', required='--prox' in sys.argv, type=int)
This way required receives either True or False depending on whether the user as used --prox. This also guarantees that -lport and -rport have an independent behavior between each other.
How about using parser.parse_known_args() method and then adding the --lport and --rport args as required args if --prox is present.
# just add --prox arg now
non_int = argparse.ArgumentParser(description="stackoverflow question",
usage="%(prog)s [-h] [--prox --lport port --rport port]")
non_int.add_argument('--prox', action='store_true',
help='Flag to turn on proxy, requires additional args lport and rport')
opts, rem_args = non_int.parse_known_args()
if opts.prox:
non_int.add_argument('--lport', required=True, type=int, help='Listen Port.')
non_int.add_argument('--rport', required=True, type=int, help='Proxy port.')
# use options and namespace from first parsing
non_int.parse_args(rem_args, namespace = opts)
Also keep in mind that you can supply the namespace opts generated after the first parsing while parsing the remaining arguments the second time. That way, in the the end, after all the parsing is done, you'll have a single namespace with all the options.
Drawbacks:
If --prox is not present the other two dependent options aren't even present in the namespace. Although based on your use-case, if --prox is not present, what happens to the other options is irrelevant.
Need to modify usage message as parser doesn't know full structure
--lport and --rport don't show up in help message
Do you use lport when prox is not set. If not, why not make lport and rport arguments of prox? e.g.
parser.add_argument('--prox', nargs=2, type=int, help='Prox: listen and proxy ports')
That saves your users typing. It is just as easy to test if args.prox is not None: as if args.prox:.
The accepted answer worked great for me! Since all code is broken without tests here is how I tested the accepted answer. parser.error() does not raise an argparse.ArgumentError error it instead exits the process. You have to test for SystemExit.
with pytest
import pytest
from . import parse_arguments # code that rasises parse.error()
def test_args_parsed_raises_error():
with pytest.raises(SystemExit):
parse_arguments(["argument that raises error"])
with unittests
from unittest import TestCase
from . import parse_arguments # code that rasises parse.error()
class TestArgs(TestCase):
def test_args_parsed_raises_error():
with self.assertRaises(SystemExit) as cm:
parse_arguments(["argument that raises error"])
inspired from: Using unittest to test argparse - exit errors
I did it like this:
if t or x or y:
assert t and x and y, f"args: -t, -x and -y should be given together"
I am using Argparse on python to do a script on command line. I have this for my script:
parser = argparse.ArgumentParser(prog = 'manageAdam')
parser.add_argument("-s", action='store_true', default=False, help='Shows configuration file')
parser.add_argument("d", type=str, help="device")
parser.add_argument("o", type=str, help="operation")
parser.add_argument("-v", "--value", type=int, nargs='*', help="value or list to send in the operation")
I am looking that if I call manageAdam -s it would work and don't ask for the positional arguments, something like the -h, which can be called without any other positional argument that is defined. Is it possible?
There is no built-in way to do this. You might be able to achieve something by writing some custom Action classes that keep track on the parser about their state, but I believe it will become quite messy and buggy.
I believe the best bet is to simply improve your UI. The -s is not an option. It's a separate command that completely alters how your script executes. In such cases you should use the subparsers functionality which allows to introduce sub-commands. This is a better interface then the one you thought, and is used by a lot of other tools (e.g. Git/mercurial).
In this case you'd have a config command to handle the configuration and a run (or how you want to call it) command to perform the operations on the device:
subparsers = parser.add_subparsers(dest='command')
parser_config = subparsers.add_parser('config', help='Configuration')
parser_run = subparsers.add_parser('run', help='Execute operation on device')
parser_run.add_argument('d', type=str, ...)
parser_run.add_argument('o', type=str, ...)
parser_run.add_argument('-v', type=int, nargs='*', ...)
# later:
args = parser.parse_args()
if args.command == 'config':
print('Configuration')
else:
print('Run operation')
Used from the command line as:
$ manageAdam config
# or
$ manageAdam run <device> <operation> <values...>
No, there are no such way.
You can make all arguments optional and set default value to None then perform check that all of them aren't None otherwise raise argparse.ArgumentError, if manageAdam provided skip check for other arguments.
I have a requirement as follows:
./xyifier --prox --lport lport --rport rport
for the argument prox , I use action='store_true' to check if it is present or not.
I do not require any of the arguments. But, if --prox is set I require rport and lport as well. Is there an easy way of doing this with argparse without writing custom conditional coding.
More Code:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', type=int, help='Listen Port.')
non_int.add_argument('--rport', type=int, help='Proxy port.')
No, there isn't any option in argparse to make mutually inclusive sets of options.
The simplest way to deal with this would be:
if args.prox and (args.lport is None or args.rport is None):
parser.error("--prox requires --lport and --rport.")
Actually there's already an open PR with an enhancement proposal :
https://github.com/python/cpython/issues/55797
You're talking about having conditionally required arguments. Like #borntyping said you could check for the error and do parser.error(), or you could just apply a requirement related to --prox when you add a new argument.
A simple solution for your example could be:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', required='--prox' in sys.argv, type=int)
non_int.add_argument('--rport', required='--prox' in sys.argv, type=int)
This way required receives either True or False depending on whether the user as used --prox. This also guarantees that -lport and -rport have an independent behavior between each other.
How about using parser.parse_known_args() method and then adding the --lport and --rport args as required args if --prox is present.
# just add --prox arg now
non_int = argparse.ArgumentParser(description="stackoverflow question",
usage="%(prog)s [-h] [--prox --lport port --rport port]")
non_int.add_argument('--prox', action='store_true',
help='Flag to turn on proxy, requires additional args lport and rport')
opts, rem_args = non_int.parse_known_args()
if opts.prox:
non_int.add_argument('--lport', required=True, type=int, help='Listen Port.')
non_int.add_argument('--rport', required=True, type=int, help='Proxy port.')
# use options and namespace from first parsing
non_int.parse_args(rem_args, namespace = opts)
Also keep in mind that you can supply the namespace opts generated after the first parsing while parsing the remaining arguments the second time. That way, in the the end, after all the parsing is done, you'll have a single namespace with all the options.
Drawbacks:
If --prox is not present the other two dependent options aren't even present in the namespace. Although based on your use-case, if --prox is not present, what happens to the other options is irrelevant.
Need to modify usage message as parser doesn't know full structure
--lport and --rport don't show up in help message
Do you use lport when prox is not set. If not, why not make lport and rport arguments of prox? e.g.
parser.add_argument('--prox', nargs=2, type=int, help='Prox: listen and proxy ports')
That saves your users typing. It is just as easy to test if args.prox is not None: as if args.prox:.
The accepted answer worked great for me! Since all code is broken without tests here is how I tested the accepted answer. parser.error() does not raise an argparse.ArgumentError error it instead exits the process. You have to test for SystemExit.
with pytest
import pytest
from . import parse_arguments # code that rasises parse.error()
def test_args_parsed_raises_error():
with pytest.raises(SystemExit):
parse_arguments(["argument that raises error"])
with unittests
from unittest import TestCase
from . import parse_arguments # code that rasises parse.error()
class TestArgs(TestCase):
def test_args_parsed_raises_error():
with self.assertRaises(SystemExit) as cm:
parse_arguments(["argument that raises error"])
inspired from: Using unittest to test argparse - exit errors
I did it like this:
if t or x or y:
assert t and x and y, f"args: -t, -x and -y should be given together"