python argparse separate 2 options of comamnds - python

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()

Related

List of arguments specified by users with argparse

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.

Python argparse module usage

I have a program which is called like this:
program.py add|remove|show
The problem here is that depending on the add/remove/show command it takes a variable number of arguments, just like this:
program.py add "a string" "another string"
program.py remove "a string"
program.py show
So, 'add' command would take 2 string arguments, while 'remove' command would take 1 only argument and 'show' command wouldn't take any argument.
I know how to make a basic argument parser using the module argparse, but I don't have much experience with it, so I started from this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=["add", "remove", "show"])
But I don't know how to continue and how to implement this functionality depending on the command. Thanks in advance.
You're looking for argparse's subparsers...
parser = argparse.ArgumentParser(prog='PROG')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "add" command
parser_add = subparsers.add_parser('add', help='add help')
# [example] add an argument to a specific subparser
parser_add.add_argument('bar', type=int, help='bar help')
# create the parser for the "remove" command
parser_remove = subparsers.add_parser('remove', help='remove help')
# create the parser for the "show" command
parser_show = subparsers.add_parser('show', help='show help')
This example code is stolen with very little modification from the language reference documentation.

Is there a way to add a parameter that supress the need of other parameters using argparse on Python?

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.

argparse: How to make mutually exclusive arguments optional?

I want to use my script like this:
python test.py run
python test.py stop
and my code is like this:
parser = argparse.ArgumentParser()
command_group = parser.add_mutually_exclusive_group(required=True)
command_group.add_argument('run', help='run it', action='store_true')
command_group.add_argument('stop', help='stop it', action='store_true')
when I execute it, an exception is raised:
ValueError: mutually exclusive arguments must be optional
so I try to add required=False when I add each argument.Then I get another exception:
TypeError: 'required' is an invalid argument for positionals
I'm confused.
A better way to do this is to add a single positional argument that can have two choices. Since you want it to be optional, use nargs='?', which means zero or one times:
parser = argparse.ArgumentParser()
parser.add_argument('run', help='run or stop', nargs='?', choices=('run', 'stop'))
If run is given, the value will be 'run'. If stop is given, it will be 'stop'. If neither is given, it will be None.
If you really want to use a mutually-exclusive group, I'm not sure if you can do it exactly how you want. You can, however, make them optional arguments by adding a hyphen:
import argparse
parser = argparse.ArgumentParser()
command_group = parser.add_mutually_exclusive_group()
command_group.add_argument('-run', help='run it', action='store_true')
command_group.add_argument('-stop', help='stop it', action='store_true')
Of course the problem with that is that the user also needs to provide the hyphen, but that's the sort of problem you can expect if you limit yourself like that.
You can achieve this with nargs='?'
parser = argparse.ArgumentParser()
command_group = parser.add_mutually_exclusive_group()
command_group.add_argument('run', help='run it', nargs='?')
command_group.add_argument('stop', help='stop it', nargs='?')
Which would allow you to call the program with
python test.py run
or
python test.py stop
but not
python test.py run stop

Common positional arguments in different subparsers

I am trying to make a parser using argparse than can parse the following commands:
python prog.py update <DOMAIN> <ENVIRONMENT>
python prog.py pull <DOMAIN> <ENVIRONMENT>
python prog.py release <DOMAIN> <ENVIRONMENT>
As you can see, both update, pull and release take the same arguments <DOMAIN> and <ENVIRONMENT>.
All three of them are subparsers of the main parser.
I wrote the following:
import argparse
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG', add_help=False)
parser.add_argument('domain', type=str, help='domain help')
parser.add_argument('environment', type=str, help='environment help')
#subparsers
subparsers = parser.add_subparsers(help='sub-command help', parents=[parser])
parser_pull = subparsers.add_parser('pull', help='pull help')
parser_update = subparsers.add_parser('update', help='update help')
print parser_pull.parse_args(['pull', 'WEBAPPS', 'DEV'])
print parser.parse_args(['update', 'WEBAPPS', 'DEV'])
but it seems that domain and environment are expected BEFORE the subcommands update, pull and release, so it throws an error.
How can I make it required to accept those arguments after the subcommands, without duplicating code inside each subcommand ?
For the record, I use Python 2.7.
Go ahead and duplicate the code. A little cut and paste is not that much work.
Positional arguments have to be given in a certain order. And .add_subparsers creates one of those positionals (one that expects values like 'pull','update'. So the order of the subparse command, positionals defined for the main parser, and positionals for the subparsers matters.
There is a parents mechanism, which can save some typing. But I hesitate to recommend it because it can cause problems (previous SO questions demonstrate this). Simply biting the bullet and entering the positional arguments where they are expected is the surest approach.
Don't forget that you can create subparsers in a loop or with helper functions - saving one kind of typing for another.
For example, after creating the subparsers:
for p in parser_pull, parser_update:
p.add_argument('domain', type=str, help='domain help')
p.add_argument('environment', type=str, help='environment help')

Categories