Argparse multiple values with options - python-3.x

I am using argparse for argument parsing in Python script. In this case I have two values where I got from using narg=2.
But the problem is when printing help. Below is part of the script.
#!/usr/bin/python3
import argparse
parser = argparse.ArgumentParser(description='My App')
parser.add_argument('--add',default=None, action='store',help='Add new value', nargs=2,metavar='name')
args = parser.parse_args()
Output for above code is :
$ python3 sshconfig.py list --help usage: sshconfig.py [-h] [--add NAME NAME]
My App
optional arguments: -h, --help show this help message and exit
--add NAME NAME Add new value
I need to differentiate between argument of --add like NAME and VALUE, like below.
$ python3 sshconfig.py list --help usage: sshconfig.py [-h] [--add NAME VALUE]
My App
optional arguments: -h, --help show this help message and exit
--add NAME VALUE Add new value

To add multiple labels, set metavar to a tuple that has a value for each argument.
parser = argparse.ArgumentParser(description='My App')
parser.add_argument('--add', help='Add new value', nargs=2, metavar=('name', 'VALUE'))
args = parser.parse_args()
output:
usage: test.py [-h] [--add name VALUE]
My App
optional arguments:
-h, --help show this help message and exit
--add name VALUE Add new value

Related

Is there a way to pass options values during mod_wsgi server start up in Django app

I am using mod_wsgi to run my Django app. As there are a plethora of options to define when the server run command is fired, I was trying to create some kind of python script to pass the options and their pre-set values.
For example:
Instead of using:
$python3 manage.py runmodwsgi --processes 3 --threads 1
I am trying to use a python script and use it as:
$python3 runme.py
where runme.py is the script file I am trying to use to start the server.
What I have done so far?
Created the script file:
import os
from django.core.management import execute_from_command_line
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<proj>.settings')
myargs = 'runmodwsgi'
list_of_args = ['', myargs]
execute_from_command_line(list_of_args)
As expected the server started with preset options, for example:
Now what I am trying to achieve is the pass values of certain options like:
--processes 3 --threads 1
and so on.
Is there a way I may pass the preset values (as I may be able to define in my script file runme.py), say something like adding to the list of arguments:
list_of_args = ['', myargs, addl_args]
I have been checking SO queries posted in addition to help available on python site, but could not get my head around to the problem.
I tried the following which is not very helpful though:
import os
import argparse # New import
from django.core.management import execute_from_command_line
# Addition of new code lines
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-p", "--processes", action="store_true")
group.add_argument("-t", "--threads", action="store_true")
parser.add_argument("pint", type=int, help="no. of processes")
parser.add_argument("tint", type=int, help="no. of threads")
args = parser.parse_args()
# End of new code
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<proj>.settings')
myargs = 'runmodwsgi'
# At this line if I add ", args" (like shown below:])
list_of_args = ['', myargs, args]
execute_from_command_line(list_of_args)
and run the command, it comes up with error:
TypeError: 'Namespace' object does not support indexing
If I simply run: python3 runme.py, I get the following error:
usage: runme.py [-h] [-p | -t] pint tint
runme.py: error: the following arguments are required: pint, tint
Whereas, using
python3 runme.py 3 1
starts the server but the options integers "3" and "1" does not have any effect (as intended for no. of processes and threads).
If I use:
python3 runme.py --processes 3 --threads 1
I get the following error:
usage: runme.py [-h] [-p | -t] pint tint
runme.py: error: argument -t/--threads: not allowed with argument -p/--processes
Tried with a single arg like:
python3 runme.py --processes 3 1
The server starts at this time but the options value/s are not affected.
How do I define the option values and pass these preset values of options to the run command?
It looks like you're just misunderstanding/misusing argparse.
import os
import sys
import argparse
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<proj>.settings')
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--processes", type=int, default=1)
parser.add_argument("-t", "--threads", type=int, default=1)
args = parser.parse_args()
from django.core.management import execute_from_command_line
command = [sys.argv[0], 'runmodwsgi', '--processes', str(args.processes), '--threads', str(args.threads)]
execute_from_command_line(command)
might be closer to what you want; it will default processes and threads both to 1.

In argparse, how can I make positional arguments depend on the optional arguments?

How can I make positional arguments depend on the optional arguments?
For example:
parser.add_argument("-A", action="store_true", help = "add")
parser.add_argument("-U", action="store_true", help = "update")
parser.add_argument("-D", action="store_true", help = "delete")
parser.add_argument("-L", action="store_true", help = "list")
If I choose -A , I want it to require arguments "name , address, cp number"
But if I choose -L, I don't want it to require anything or when I choose -U it requires another set of arguments.
My end goal is to create a contact book where I can add new contacts, update existing contacts, delete contacts and list contacts. I can do this if I use if else statements but I want to try using argparse.
If I'm using argparse incorrectly, please give me some advice!
This is a good opportunity to use subparsers:
This flips (I think) what you're trying to acheive.
The help text looks like this:
😊 /tmp python3 test.py --help
usage: PROG [-h] {add,update,delete,list} ...
positional arguments:
{add,update,delete,list}
Sub-commands
add ADD help text
update UPDATE help text
delete DELETE help text
list LIST help text
optional arguments:
-h, --help show this help message and exit
See '<command> --help' to read about a specific sub-command.
😊 /tmp python3 test.py add -h
usage: PROG add [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO
This skeleton should get you started:
import argparse
def run_command(parser, args):
if args.command == 'add':
print(args)
elif args.command == 'update':
print(args)
elif args.command == 'delete':
print(args)
elif args.command == 'list':
print(args)
parser = argparse.ArgumentParser(
prog='PROG',
epilog="See '<command> --help' to read about a specific sub-command."
)
subparsers = parser.add_subparsers(dest='command', help='Sub-commands')
add_parser = subparsers.add_parser('add', help='ADD help text')
add_parser.add_argument("--foo")
add_parser.set_defaults(func=run_command)
update_parser = subparsers.add_parser('update', help='UPDATE help text')
update_parser.add_argument('--bar')
update_parser.set_defaults(func=run_command)
delete_parser = subparsers.add_parser('delete', help='DELETE help text')
delete_parser.add_argument('--baz')
delete_parser.set_defaults(func=run_command)
list_parser = subparsers.add_parser('list', help='LIST help text')
list_parser.add_argument('--qux')
list_parser.set_defaults(func=run_command)
args = parser.parse_args()
if args.command is not None:
args.func(parser, args)
else:
parser.print_help()
What the code above does is:
Creates an argparse.ArgumentParser
Adds a subparser called command
Then we create 4 parsers (one for each action) that we can add arguments to
Finally we set a func to run_command which we pass the parser and the args.

python argparse if argument selected then another argument required =True

Is there a way to make an argument required to be true if another specific argument choice is present otherwise argument required is false?
For example the following code if argument -act choice select is 'copy' then the argument dp required is true otherwise required is false:
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("-act", "--act", required=True, choices=['tidy','copy'],type=str.lower,
help="Action Type")
ap.add_argument("-sp", "--sp", required=True,
help="Source Path")
args = vars(ap.parse_args())
if args["act"] == 'copy':
ap.add_argument("-dp", "--dp", required=True,
help="Destination Path")
else:
ap.add_argument("-dp", "--dp", required=False,
help="Destination Path")
args = vars(ap.parse_args())
### Tidy Function
def tidy():
print("tidy Function")
### Copy Function
def copy():
print("copy Function")
### Call Functions
if args["act"] == 'tidy':
tidy()
if args["act"] == 'copy':
copy()
I am currently getting an error unrecognized arguments: -dp with the above code.
The expected result would be to move on to call function. Thanks
I would use ArgumentParser.add_subparsers to define the action type {tidy, copy} and give the command specific arguments. Using a base parser with parents allows you to define arguments that are shared by both (or all) your sub-commands.
import argparse
parser = argparse.ArgumentParser(
prog='PROG',
epilog="See '<command> --help' to read about a specific sub-command."
)
base_parser = argparse.ArgumentParser(add_help=False)
base_parser.add_argument("--sp", required=True, help="source")
subparsers = parser.add_subparsers(dest='act', help='Sub-commands')
A_parser = subparsers.add_parser('copy', help='copy command', parents=[base_parser])
A_parser.add_argument('--dp', required=True, help="dest, required")
B_parser = subparsers.add_parser('tidy', help='tidy command', parents=[base_parser])
B_parser.add_argument('--dp', required=False, help="dest, optional")
args = parser.parse_args()
if args.act == 'copy':
pass
elif args.act == 'tidy':
pass
print(args)
Which produces the following help pages, note that instead of needing to use -act the command is given as a positional parameter.
~ python args.py -h
usage: PROG [-h] {tidy,copy} ...
positional arguments:
{tidy,copy} Sub-commands
tidy tidy command
copy copy command
optional arguments:
-h, --help show this help message and exit
See '<command> --help' to read about a specific sub-command.
~ python args.py copy -h
usage: PROG copy [-h] --sp SP [--dp DP]
optional arguments:
-h, --help show this help message and exit
--sp SP source
--dp DP dest, optional
~ python args.py tidy -h
usage: PROG tidy [-h] --sp SP --dp DP
optional arguments:
-h, --help show this help message and exit
--sp SP source
--dp DP dest, required
ap.add_argument("-dp", "--dp", help="Destination Path")
args = parser.parse_args()
if args.copy is in ['copy']:
if args.dp is None:
parser.error('With copy, a dp value is required')
Since a user can't set a value to None, is None is a good test for arguments that haven't been used.
The parser has a list of defined arguments, parser._actions. Each has a required attribute. During parsing it maintains a set of seen-actions. At the end of parsing it just checks this set against the actions for which required is True, and issues the error message if there are any.
I would argue that testing after parsing is simpler than trying to set the required parameter before hand.
An alternative is to provide dp with a reasonable default. Then you won't care whether the user provides a value or not.
I can imagine defining a custom Action class for copy that would set the required attribute of dp, but overall that would be more work.

Is it possible to inherit required options in argparse subparsers?

I am trying to write a command line application that has several modes in which it can run (similar to git having clone, pull, etc.). Each of my subcommands have their own options, but I also wanted them to share a set of required options, so I tried using a parent parser to implement this. However, it seems that inheriting a required option is causing the subparser to keep asking for it. Here is an example recreating the behavior:
import argparse
parent_parser = argparse.ArgumentParser(description="The parent parser")
parent_parser.add_argument("-p", type=int, required=True,
help="set the parent required parameter")
subparsers = parent_parser.add_subparsers(title="actions", required=True, dest='command')
parser_command1 = subparsers.add_parser("command1", parents=[parent_parser],
add_help=False,
description="The command1 parser",
help="Do command1")
parser_command1.add_argument("--option1", help="Run with option1")
parser_command2 = subparsers.add_parser("command2", parents=[parent_parser],
add_help=False,
description="The command2 parser",
help="Do command2")
args = parent_parser.parse_args()
So now if I run python test.py I get:
usage: test.py [-h] -p P {command1,command2} ...
test.py: error: the following arguments are required: -p, command
Ok, so far so good. Then if I try to specify just the -p option with python test.py -p 3 I get:
usage: test.py [-h] -p P {command1,command2} ...
test.py: error: the following arguments are required: command
But then if I run python test.py -p 3 command1
I get:
usage: test.py command1 [-h] -p P [--option1 OPTION1] {command1,command2} ...
test.py command1: error: the following arguments are required: -p, command
If I add another -p 3 it still asks to specify command again, and then if I add that again it asks for another -p 3 etc.
If I don't make the -p option required the problem is fixed, but is there a way to share required options among multiple subparsers without just copy pasting them within each subparser? Or am I going about this entirely the wrong way?
Separate the main parser and parent parser functionality:
import argparse
parent_parser = argparse.ArgumentParser(description="The parent parser", add_help=False)
parent_parser.add_argument("-p", type=int, required=True,
help="set the parent required parameter")
main_parser = argparse.ArgumentParser()
subparsers = main_parser.add_subparsers(title="actions", required=True, dest='command')
parser_command1 = subparsers.add_parser("command1", parents=[parent_parser],
description="The command1 parser",
help="Do command1")
parser_command1.add_argument("--option1", help="Run with option1")
parser_command2 = subparsers.add_parser("command2", parents=[parent_parser],
description="The command2 parser",
help="Do command2")
args = main_parser.parse_args()
The main parser and subparser both write to the same args namespace. If both define a 'p' argument, the subparser's value (or default) will overwrite any value set by the main. So it's possible to use the same 'dest' in both, but it is generally not a good idea. And each parser has to meet its own 'required' specifications. There's no 'sharing'.
The parents mechanism copies arguments from the parent to the child. So it saves typing or copy-n-paste, but little else. It's most useful when the parent is defined elsewhere and imported. Actually it copies by reference, which sometimes raises problems. In general it isn't a good idea to run both the parent and child. Use a 'dummy' parent.

Nature of optional arguments argparse Python

As I have started to learn argparse module, I try to get my head around optional arguments in Python argparse. I would like to add my own arguments, as I present below:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--sense", help = "What you are seeking")
arg = parser.parse_args()
if arg.sense:
print("42")
However, the help doesn't seem to accept the new argument in output:
"""python3 file.py -h"""
usage: file.py [-h]
optional arguments:
-h, --help show this help message and exit
What surprises me is the fact, that when I copied the code from a tutorial:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
https://docs.python.org/3/howto/argparse.html
the help changed. Is there a list of possible optional arguments? Am I allowed to create new ones?

Resources