I'm writing a program that can either take one flag-argument --list OR two or more positional arguments SOURCE [SOURCE ...] DESTINATION. Ideally with when SRC/DST is used it should also accept --recursive but that can be a global option simply ignored with --list.
For now I have this:
group = parser.add_argument_group('Source / Dest Selection')
group.add_argument('--list', action="store_true")
group.add_argument('--recursive', action="store_true")
group.add_argument('SOURCE', nargs='+')
group.add_argument('DESTINATION')
However it always requires SOURCE and DESTINATION. I don't want to make each optional, instead I would like to either require both SRC and DST or none of them and then require --list.
I would also settle for both or none of SRC/DST and simply ignore them if --list was used.
Any idea how to express that with argparse? Thanks!
Very hackish but you could use multiple parsers. May be something like:
import argparse
parser1 = argparse.ArgumentParser()
parser1.add_argument('--list', action="store_true")
parser1.add_argument('DUMMY_POSITIONAL', nargs='*')
args1 = parser1.parse_args()
if not args1.list:
parser2 = argparse.ArgumentParser()
parser2.add_argument('SOURCE', nargs='+')
parser2.add_argument('DESTINATION')
args2 = parser2.parse_args()
if len(args2.SOURCE) == 0:
print("Must specify SOURCE")
else:
print(args2.SOURCE, args2.DESTINATION)
Related
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 want to write a argparse command that needs two postional arguments when I don't set a optional argument. In my case it's like I want to call it with two necessary parameters but when I say python3 test.py -gui I want that you don't need this two arguments, because then you are using the gui.
Thx
This is what I was proposing in the comments:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--gui', action='store_true', help="use GUI")
parser.add_argument('args', nargs='*')
cmdargs = parser.parse_args()
nargs = len(cmdargs.args)
nargs_expected = 0 if cmdargs.gui else 2
if nargs != nargs_expected:
parser.error(f"{nargs_expected} arguments were expected, but got {nargs}")
I've read through quite a few tutorials, questions and documentation and could not figure out how to achieve this:
prog.py [-h] [-d DEVICE -l | (-m M -s S -p P)]
When using the -l argument, prog.py reads from a device interface. When providing -m, -s and/or -p, prog.py writes to that interface. Reading and writing is not possible at the same time, so reading and writing arguments are mutually exclusive.
Once DEVICE has been specified, either -l or at least one of -m, -s or -p needs to be provided, it should also be possible to provide any combination of those three.
So far I approached this problem from various angles:
I tried mutually exclusive groups which do not work as only single arguments can mutually exclude each other (please correct me if I'm wrong)
I tried to set up sub parsers but I failed...
This is what I tried last:
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument("-d","--device",
default="system",
help="foo")
subparsers = parser.add_subparsers(help='foo')
read_parser = subparsers.add_parser('read', help='foo')
read_parser.add_argument("-l", "--list",
action="store_true")
write_parser = subparsers.add_parser("write", help="bar")
write_parser.add_argument("-m","--mode",
type=str,
choices=["on","off","auto"],
help="foo")
write_parser.add_argument("-s", "--season",
type=str,
choices=["winter","summer"],
help="foo")
write_parser.add_argument("-p","--present",
type=int,
choices=[0,1],
help="foo")
parser.parse_args()
This is not even close as with subparsers argparse expects either read or write. What I need to do is add groups that are mutually exclusive. Anyone has an idea of how to solve this?
You can check on your own (with basic argparse functionalities) if correct arguments combination was passed:
parser = argparse.ArgumentParser()
parser.add_argument("-d","--device",
default="system",
help="foo")
parser.add_argument("-l", "--list",
action="store_true")
parser.add_argument("-m","--mode",
type=str,
choices=["on","off","auto"],
help="foo")
parser.add_argument("-s", "--season",
type=str,
choices=["winter","summer"],
help="foo")
parser.add_argument("-p","--present",
type=int,
choices=[0,1],
help="foo")
args = parser.parse_args()
# if user defines device but not any read/write argument raise exception
if args.device is not None and not args.list and all(arg is None for arg in (args.season, args.present, args.mode)):
parser.error('Wrong arguments passed')
# if user defines read and write arguments at the same time raise exception
if args.list and not all(arg is None for arg in (args.season, args.present, args.mode)):
parser.error('Wrong arguments passed')
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 want to create a command parser mycommand, using argparse, with two subcommands read and write: read should have just one argument which is some path, and write should have two arguments one of which is a path and the other a value. It should be possible to execute the command in the following way:
mycommand read <path>
mycommand write <path> <value>
without using labels for the <path>, <value> arguments, i.e. without requiring --path. How can I do this?
This is pretty straight forward following the docs:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
read = subparsers.add_parser('read')
read.add_argument('path')
write = subparsers.add_parser('write')
write.add_argument('path')
write.add_argument('value')
print(parser.parse_args(['read', 'foo']))
print(parser.parse_args(['write', 'foo', 'bar']))
Note that this doesn't tell you what parser parsed the arguments. If you want that, you can simply add a dest to the add_subparsers command:
subparsers = parser.add_subparsers(dest='subparser')
Finally, you can add a default attribute for each subparser that you can use to perform the actions specific to that subparser. This is spelled out in the docs too, but for completeness, in our example, it might look something like this1:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subparser')
def handle_read(args):
print('Handling read')
print(args)
read = subparsers.add_parser('read')
read.add_argument('path')
read.set_defaults(handler=handle_read)
def handle_write(args):
print('Handling write')
print(args)
write = subparsers.add_parser('write')
write.add_argument('path')
write.add_argument('value')
write.set_defaults(handler=handle_write)
args = parser.parse_args()
args.handler(args)
1I added the dest to subparsers in this example too for illustrative purposes -- Using argparse with handler functions probably makes that attribute on args obsolete.