Subparser function calls - python

How do I call the following functions below based on user input?
For example, if they type:
python test.py cmd1 -n hostname
it will call def cmd1 and pass the -n parameter (hostname) to function
import argparse
def cmd1(node):
print('cmd1')
def cmd1_option1():
print('cmd2')
def cmd1_option1(template):
print('cmd3')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
command_1 = subparsers.add_parser('cmd1', help='...')
command_1.add_argument('-n', "--node", type=str, help='...')
command_2 = subparsers.add_parser('cmd2', help='...')
command_3 = subparsers.add_parser('cmd3', help='...')
command_3.add_argument('-t', "--template", type=int, help='...')
args = parser.parse_args()

Related

Different argparse arguments for each mode

main.py --mode a [--environment] [--db_server]
main.py --mode b [--environment] [--time] [--file] [--save]
main.py --mode c [--environment] [--render] [--file]
In each different modes, I need a different menu with some common arguments. The help must be specified for each different modes. Is possible to do this with argparse or I need a different module? It must first parse the mode and then decide what menu will choose from the existing 3 different menus (with some common arguments).
Thanks.
The solution is subparsers.
import argparse
if __name__ == "__main__":
# create the top-level parser
my_parser = argparse.ArgumentParser(
prog="PROG",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
# create sub-parser
sub_parsers = my_parser.add_subparsers(
title="Operating modes",
description="Select the operating mode",
dest="mode",
required=True,
)
# create the parser for the "agent" sub-command
parser_agent = sub_parsers.add_parser("agent", help="Agent mode")
parser_agent.add_argument(
"--db_server", type=str, help="DB server name", default="localhost"
)
parser_agent.add_argument(
"--update_interval",
type=int,
help="Interval of updating policy parameters",
default=64,
)
# create the parse for the "learner" sub-command
parser_learner = sub_parsers.add_parser("learner", help="Learner mode")
parser_learner.add_argument(
"-e",
"--environment",
type=str,
help="Only OpenAI Gym/PyBullet environments are available!",
required=True,
)
parser_learner.add_argument(
"-t",
"--max_steps",
type=int,
help="Number of agent's steps",
default=int(1e6),
)
# create the parse for the "tester" sub-command
parser_tester = sub_parsers.add_parser("tester", help="Tester mode")
parser_tester.add_argument(
"-t",
"--max_steps",
type=int,
help="Number of agent's steps",
default=int(1e6),
)
parser_tester.add_argument(
"--render", action="store_true", help="Render the environment"
)
parser_tester.add_argument(
"-f", "--model_path", type=str, help="Path to saved model"
)
args = my_parser.parse_args()
print(args)
if args.mode == "agent":
print('mode 1')
elif args.mode == "learner":
print('mode 2')
elif args.mode == "tester":
print('mode 3')

python get changed (non-default) cli arguments?

Given an argument parser with n arguments, where I change the default value of only a small subset every run from the command line, is there a clean way of extracting a dict/namespace of all the non-default k,v arguments?
parser = argparse.ArgumentParser()
parser.add_argument("--a",type=str,default='a')
parser.add_argument("--b",type=str,default='b')
parser.add_argument("--c",type=str,default='c')
parser.add_argument("--d",type=str,default='d')
And
python run.py --a "e"
I would like to have
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--a",type=str,default='a')
parser.add_argument("--b",type=str,default='b')
parser.add_argument("--c",type=str,default='c')
parser.add_argument("--d",type=str,default='d')
non_default = parse_non_default(parser) # non_default = {'a':'e'}
You could lookup the parser and compare which values differenciate:
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--a", type=str, default='a')
parser.add_argument("--b", type=str, default='b')
parser.add_argument("--c", type=str, default='c')
parser.add_argument("--d", type=str, default='d')
parser.add_argument("--n", type=int, default=999)
args = parser.parse_args(['--a', 'e']) # Test CLI arguments!
non_default = {
opt.dest: getattr(args, opt.dest)
for opt in parser._option_string_actions.values()
if hasattr(args, opt.dest) and opt.default != getattr(args, opt.dest)
}
print(non_default)
main()
Out:
{'a': 'e'}

argparse - how to pass argument from args into function?

import argparse
from queries import most_common_cities
parser = argparse.ArgumentParser(description='A script that does operations with database data and returns values')
parser.add_argument('-c', '--most_common_cities',
nargs=1,
type=positive_int,
help='Specify how many common cities.')
args = parser.parse_args()
if args.most_common_cities:
result = most_common_cities(n) # "n" should be an arg passed by user
print(result)
How could I pass arguments from CLI to my function arg?
When someone use command:
python argp.py --most_common_cities 5
It should return 5 most common cities.
Remove nargs=1, then args.most_common_cities will be the actual value passed in.
nargs=1 wraps it in a list.
parser.add_argument('-c', '--most_common_cities',
type=int,
help='Specify how many common cities.')
args = parser.parse_args(['-c', '5'])
n = args.most_common_cities
print(n)
print(type(n))
# 5
# <class 'int'>
I started your script with following command:
python3 test.py --most_common_cities 5
You can access the arguments with:
import argparse
parser = argparse.ArgumentParser(description='A script that does operations with database data and returns values')
parser.add_argument('-c', '--most_common_cities',
nargs=1,
type=int,
help='Specify how many common cities.')
args = parser.parse_args()
arguments = vars(parser.parse_args())
print(arguments) #{'most_common_cities': [5]}
#then you can access the value with:
arguments['most_common_cities']

How can I get an argument name from argparse?

This is my command line script
import argparse
parser = argparse.ArgumentParser('My command line app')
parser.add_argument('--start', type=start, metavar='HOST', dest='result', nargs='*', default='all', help='Start hosts')
parser.add_argument('--stop', type=stop, metavar='HOST', dest='result', nargs='*', default='all', help='Stop hosts')
parser.add_argument('--status', type=status, metavar='HOST', dest='result', nargs='*', default='all', help='Show hosts status')
args = parser.parse_args()
Currently I defined one function for each option, but I would like to wrap them in only one for these three options. Then I would need to know which argument was used. Is it possible with argparse?
Solution
At the end I took this way:
import argparse
def wrapper(command, hosts):
pass
parser = argparse.ArgumentParser('My script')
parser.add_argument('action', choices=['start', 'stop', 'status'], help='Action')
parser.add_argument('hosts', metavar='HOST', nargs='*', default='all')
args = parser.parse_args()
wrapper(args.action, args.hosts)
It sounds like the saner way to handle this would be the choices parameter:
commands = {'start': start, 'stop': stop, 'status': status}
parser = argparse.ArgumentParser('My command line app')
parser.add_argument('command', choices=commands.keys())
parser.add_argument('hosts', nargs='*', default=[])
args = parser.parse_args()
commands[args.command](*args.hosts)

argparse -- requiring either 2 values or none for an optional argument

I'm trying to make an optional argument for a script that can either take no values or 2 values, nothing else. Can you accomplish this using argparse?
# desired output:
# ./script.py -a --> works
# ./script.py -a val1 --> error
# ./script.py -a val1 val2 --> works
version 1 -- accepts 0 or 1 values:
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--action", nargs="?", const=True, action="store", help="do some action")
args = parser.parse_args()
# output:
# ./script.py -a --> works
# ./script.py -a val1 --> works
# ./script.py -a val1 val2 --> error
version 2 - accepts exactly 2 values:
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--action", nargs=2, action="store", help="do some action")
args = parser.parse_args()
# output:
# ./script.py -a --> error
# ./script.py -a val1 --> error
# ./script.py -a val1 val2 --> works
How do you combine these 2 different versions so that the script accepts 0 or 2 values for the argument, but rejects it when it only has 1 value?
You'll have to do your own error checking here. Accept 0 or more value, and reject anything other than 0 or 2:
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--action", nargs='*', action="store", help="do some action")
args = parser.parse_args()
if args.action is not None and len(args.action) not in (0, 2):
parser.error('Either give no values for action, or two, not {}.'.format(len(args.action)))
Note that args.action is set to None when no -a switch was used:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument("-a", "--action", nargs='*', action="store", help="do some action")
_StoreAction(option_strings=['-a', '--action'], dest='action', nargs='*', const=None, default=None, type=None, choices=None, help='do some action', metavar=None)
>>> args = parser.parse_args([])
>>> args.action is None
True
>>> args = parser.parse_args(['-a'])
>>> args.action
[]
Just handle that case yourself:
parser.add_argument("-a", "--action", nargs='*', action="store", help="do some action")
args = parser.parse_args()
if args.action is not None:
if len(args.action) not in (0, 2):
parser.error('Specify no or two actions')
# action was specified but either there were two actions or no action
else:
# action was not specified
Of course you should update the help text in that case so that the user has a chance to know this before running into the error.
Have your option take a single, optional comma-separated string. You'll use a custom type to convert that string to a list and verify that it has exactly two items.
def pair(value):
rv = value.split(',')
if len(rv) != 2:
raise argparse.ArgumentParser()
return rv
parser.add_argument("-a", "--action", nargs='?',
type=pair, metavar='val1,val2',
help="do some action")
print parser.parse_args()
Then you'd use it like
$ ./script.py -a
Namespace(action=None)
$ ./script.py -a val1,val2
Namespace(action=['val1','val2'])
$ ./script.py -a val1
usage: tmp.py [-h] [-a [ACTION]]
script.py: error: argument -a/--action: invalid pair value: 'val1'
$ ./script.py -a val1,val2,val3
usage: tmp.py [-h] [-a [ACTION]]
script.py: error: argument -a/--action: invalid pair value: 'val1,val2,val3'
You can adjust the definition of pair to use a different separator and to return something other than a list (a tuple, for example).
The metavar provides a better indication that the argument to action is a pair of values, rather than just one.
$ ./script.py -h
usage: script.py [-h] [-a [val1,val2]]
optional arguments:
-h, --help show this help message and exit
-a [val1,val2], --action [val1,val2]
do some action
How about the required argument:parser.add_argument("-a", "--action", nargs=2, action="store", help="do some action", required=False)

Categories