command line input case sensitivity - python

I am using command line inputs to do some simple tasks in my .py script.
My inputs are .py (GPIO) (SERIAL) (Log) (Debug)(Can Include an integer) (Pin)(Can include an Integer) (Verbose) (Help)
Of course it is all user input so it is case sensitive.
I am using:
if "gpio" in [x.lower() for x in sys.argv]:
which works fine and
if str(['Debug','10'])[1:-1] in str([sys.argv]):
if str(['Pin','10'])[1:-1] in str([sys.argv]):
Which works fine for Case Sensitive and exact integer value but fails for Case Sensitivity and different integer values.
So I need to make this accept any case str.lower() and any integer value int()
But everything I try fails. str.lower wont accept lists and I'm not sure what to do with integer.
I'd like to do this without importing a module if possible.

An example for argparse. It isn't case insensitive but I see no point in allowing --gpio, --Gpio, --gPiO, …
from argparse import ArgumentParser
def main():
parser = ArgumentParser()
parser.add_argument(
'--gpio', action='store_true', default=False, help='Use GPIO.'
)
parser.add_argument(
'--serial', action='store_true', default=False,
help='Make a serial connection.'
)
parser.add_argument(
'--log', action='store_true', default=False, help='Log actions.'
)
parser.add_argument(
'--debug', metavar='LEVEL', type=int, default=0, help='Debug level.'
)
parser.add_argument('--pin', type=int, help='Pin number.')
parser.add_argument(
'--verbose', action='count', default=0,
help='Increase level of verbosity.'
)
options = parser.parse_args()
if options.gpio:
pass
if options.debug > 0:
pass
if options.pin is not None:
pass
if __name__ == '__main__':
main()
You get a --help for free:
usage: test.py [-h] [--gpio] [--serial] [--log] [--debug LEVEL] [--pin PIN]
[--verbose]
optional arguments:
-h, --help show this help message and exit
--gpio Use GPIO.
--serial Make a serial connection.
--log Log actions.
--debug LEVEL Debug level.
--pin PIN Pin number.
--verbose Increase level of verbosity.

Yes, as BlackJack says, the correct way to do this is use argparse. You can roll your own, but there's a reason argparse exists -- parsing gets complicated and the work has already been done. Despite that here's a simple argument parser that will do what you want, assuming I understood your question:
import sys
cmd = None
commands = {}
for arg in sys.argv[1:]:
# set the cmd for all the args that have a value
if arg.lower() == 'debug' or arg.lower() == 'pin':
cmd = arg
# next check for boolean args
elif arg.lower() == 'gpio':
commands[arg.lower()] = True
# take everything else to be a value associated with cmd
else:
commands[cmd] = int(arg)
cmd = None
print(commands)

What I ended up doing with Brad Dre's help "arg.lower() was:
if len(sys.argv) > 1:
for i in range(1,len(sys.argv)):
if str.isdigit(sys.argv[i]):
arg = sys.argv[i-1]
newval[arg.lower()] = int(sys.argv[i])
Creating a "Dictionary" and then calling:
if "debug" in newval:
if "pin" in newval:
Worked out for me.

Related

Simulating argparse command line arguments input while debugging

This thread is an extension from the previous that can be found here.
Say, I have a code that serve two purpose, 1) print a max number from a list of integer; 2) make a new dir.
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=sum,
help='sum the integers (default: find the max)')
parser.add_argument("--output_dir", type=str, default="data/xx")
def main(args):
os.makedirs(args.output_dir)
print args.accumulate(args.integers)
if __name__=='__main__':
args = parser.parse_args() # Disable during debugging # Run through terminal
# args = argparse.Namespace(integers = 1, output_dir= 'mydata_223ss32') # Disable when run through terminal: For debugging process
main(args)
These statement can be executed from terminal by
python test_file.py --output_dir data/xxxx 2 2 5 --sum
However, for debugging process, I want to skip the usage of terminal. The idea by hpaulj as can be found from here was simply modify the
if __name__=='__main__':
into
if __name__=='__main__':
args = argparse.Namespace(output_dir= 'mydata') # Disable when run through terminal: For debugging process
main(args)
However, I also want to include a list of integer during the debugging process. Including both the list of integer and dir address as below output an error
args = argparse.Namespace(integers = "2 2 5", output_dir= 'mydata')
May I know where did I do wrong.
Thanks in advance
By slightly changing your code to:
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=sum,
help='sum the integers (default: find the max)')
parser.add_argument("--output_dir", type=str, default="data/xx")
def main(args):
#os.makedirs(args.output_dir) # XXX: Commented out for debugging
print(args.accumulate(args.integers))
if __name__=='__main__':
print(sys.argv)
args = parser.parse_args() # Disable during debugging # Run through terminal
# args = argparse.Namespace(integers = 1, output_dir= 'mydata_223ss32') # Disable when run through terminal: For debugging process
print(args)
main(args)
We can see that if we call the script with: ./test3.py --output_dir foo 1 2 3
Our output is:
['test3.py', '--output_dir', 'foo', '1', '2', '3']
Namespace(accumulate=<built-in function sum>, integers=[1, 2, 3], output_dir='foo')
6
So, if you want to emulate command line arguments, you have two options:
1) Edit sys.argv (personally preferred):
if __name__=='__main__':
# Override sys.argv
sys.argv = ['test3.py', '--output_dir', 'foo', '1', '2', '3']
args = parser.parse_args()
main(args)
2) Create a Namespace object:
if __name__=='__main__':
#args = parser.parse_args() # Disable during debugging # Run through terminal
args = argparse.Namespace(accumulate=sum, integers=[1,2,3], output_dir='foo')
main(args)
The problem with the second approach is two-fold. First, you're skipping a very important part of the debugging process by doing this, debugging the argparse configuration and ensuring the arguments are parsed as you expect. Second, implicit defaults added by your argparse configuration must be defined here (e.g. your --sum argument/accumulate). If you took the first approach, argparse would process sys.argv and add accumulate without you having to make any changes. In the second approach, we have to add accumulate=sum for it to run as expected.
To add some more information to [jedwards Answer][1]:
I was using pycharm to achieve the same and I needed to alter jedwards proposition slightly, such that sys.argv is not overwritten but modified. The first entry of the list sys.argv[0] should be left, since it contains the path of the file that is being run. Without doing that the variable parser.prog would be messed up and therefore non functional.
Instead of adding the filename manually as the first list entry as jedwards did you can just append to sys.argv and extend the list:
if __name__=='__main__':
# Append additional arguments to sys.argv
sys.argv = sys.argv + ['--output_dir', 'foo', '1', '2', '3']
args = parser.parse_args()
main(args)
This makes it resilient to changes of the file name.
[1]: https://stackoverflow.com/a/50886791/7705525

Argument parsing in Python (required vs. optional)

I'm currently working on a script that will be able to take multiple flags. I want it so that no matter what the last argument should be 'start|stop|status'.
#!/usr/bin/env python
from argparse import ArgumentParser
def argument_analysis():
"""
This will analyze arguments, and return the region as a string, the filter as a dictionary, and the command as a string.
:return: region,filters,command
"""
parser_options = ArgumentParser()
parser_options.add_argument("-r", "--region", dest='region',
help="Filter by region.")
parser_options.add_argument("-n", "--name", dest='name',
help="Filter by hostname.")
parser_options.add_argument("-P", "--project", dest='project',
help="Filter by Project tag.")
parser_options.add_argument("-U", "--usage", dest='usage',
help="Filter by Usage tag.")
parser_options.add_argument("-i", "--instance_id", dest='instance_id',
help="Filter by instance_id.")
parser_options.add_argument("-t", "--type", dest='type',
help="Filter by instance_size.")
parser_options.add_argument("-p", "--ip", dest='internal_ip',
help="Filter by internal_ip")
parser_options.add_argument("-c", "--command", dest='command',
help="stop/start, or check the status of instances.")
parser_options.add_argument("-a", "--all", dest='all', default=False, action='store_true',
help="No filter, display status of all servers.")
arguments = vars(parser_options.parse_args())
return arguments
if __name__ == '__main__':
print argument_analysis()
I want it so that ./argument_analysis_script.py will require a 'stop|start|status' at the end. I haven't had much luck getting help with the ARgumentParser(). If anybody has any suggestions it would be very helpful.
Thanks in advance for your time.
NOTE: I would like for the script to stop if [stop|start|restart|status] is not entered, and explain that [stop|start|restart|status] is required.
**UPDATE** **UPDATE** **UPDATE**
After doing some more digging, to be able to analyze/use command line options, and arguments, I stumbled upon OptionParser, which I avoided as docs.python.org states it's deprecated. Anyway, since that is the only thing that I could find to give me exactly what I wanted, here is an update of what I've got:
#!/usr/bin/env python
from optparse import OptionParser
def argument_analysis():
"""
This will analyze arguments, and return the region as a string, the filter as a dictionary, and the command as a string.
:return: region,filters,command
"""
parser = OptionParser()
parser.add_option("-r", "--region", dest='region',
help="Filter by region.")
parser.add_option("-n", "--name", dest='name',
help="Filter by hostname.")
parser.add_option("-P", "--project", dest='project',
help="Filter by Project tag.")
parser.add_option("-U", "--usage", dest='usage',
help="Filter by Usage tag.")
parser.add_option("-i", "--instance_id", dest='instance_id',
help="Filter by instance_id.")
parser.add_option("-t", "--type", dest='type',
help="Filter by instance_size.")
parser.add_option("-p", "--ip", dest='internal_ip',
help="Filter by internal_ip")
parser.add_option("-c", "--command", dest='command',
help="stop/start, or check the status of instances.")
parser.add_option("-a", "--all", dest='all', default=False, action='store_true',
help="No filter, display status of all servers.")
(options, args) = parser.parse_args() # Grab Options specifed from above, as well as actual Arguments.
options = vars(options) # Convert 'options' into dictionary: key=dest_name, value=dest_value
# Getting variables for dictionary below.
region_filter = options['region']
name_filter = options['name']
project_filter = options['project']
usage_filter = options['usage']
instance_filter = options['instance_id']
type_filter = options['type']
ip_filter = options['internal_ip']
all_filter = options['all']
region = region_filter if region_filter else 'us-east-1' # Return 'us-east-1' region is not specified.
filters = {'tag:Name': name_filter, 'tag:Project': project_filter, 'tag:Usage': usage_filter,
'instance-id': instance_filter, 'instance_type': type_filter, 'private-ip-address': ip_filter,
'all': all_filter}
command = 'No commands.' if not args else args #Return "No commands" if no command is specified.
return region, filters, command
if __name__ == '__main__':
opts_and_args = argument_analysis()
print "Region: " + str(opts_and_args[0])
print "Filters: " + str(opts_and_args[1])
print "Command: " + str(opts_and_args[2])
As you can see, you can apply whatever logic you want to based on the returned object, or within the definition. Thanks everybody for your assistance on this one.
Maybe you can do this with argparse, but another option is to use the sys module
import sys
print sys.argv # prints command line arguments
sys.argv has the command line arguments in a list, so you can check the last one for whatever:
if sys.argv[-1] != 'start':
print "Error, expected <something>"
// quit
Partially the same as Ben's answer, but extended with regex to avoid declaring an if statement for each case, e.g. start, stop.
import sys
import re
a = sys.argv[-1]
if not re.search(r"^(start|stop|restart|status)$", a):
raise Exception("start, stop, restart or status should be passed to this script instead of: \""+a+"\"")
I recommend taking a look at docopt. It's not in the standard library, but it's definitely more powerful and intuitive. For example, in your case you could just do this:
docstring='''My Program.
Usage: my_prog [options] (flag1|flag2|flag3) (start|stop|restart|status)
Options:
-a stuff about this
-b more stuff
-c yet more stuff
-d finally stuff'''
from docopt import docopt
parsed = docopt(docstring)

Argument parsing python using ArgParse

I am creating a python script and for parsing the arguments I would need this:
the script will accept three parameters, only one always mandatory, the second one will only be mandatory depending on certain values of the first one and the third one may or may not appear.
This is my try:
class pathAction(argparse.Action):
folder = {'remote':'/path1', 'projects':'/path2'}
def __call__(self, parser, args, values, option = None):
args.path = values
print "ferw %s " % args.component
if args.component=='hos' or args.component=='hcr':
print "rte %s" % args.path
if args.path and pathAction.folder.get(args.path):
args.path = pathAction.folder[args.path]
else:
parser.error("You must enter the folder you want to clean: available choices[remote, projects]")
def main():
try:
# Arguments parsing
parser = argparse.ArgumentParser(description="""This script will clean the old component files.""")
parser.add_argument("-c", "--component", help="component to clean", type=lowerit, choices=["hos", "hcr", "mdw", "gui"], required=True)
parser.add_argument("-p", "--path", help="path to clean", action = pathAction, choices = ["remote", "projects"])
parser.add_argument("-d", "--delete", help="parameter for deleting the files from the filesystem", nargs='*', default=True)
args = parser.parse_args()
if works well except one case: if i have -c it should complain because there is no -p however it does not
Can you help me please?
Thanks
You can add some custom validation like this:
if args.component and not args.path:
parser.error('Your error message!')
Your special action will be used only if there is a -p argument. If you just give it a -c the cross check is never used.
Generally checking for interactions after parse_args (as Gohn67 suggested) is more reliable, and simpler than with custom actions.
What happens if your commandline was '-p remote -c ...'? pathAction would be called before the -c value is parsed and set. Is that what you want? Your special action only works if -p is given, and is the last argument.
Another option is to make 'component' a subparser positional. By default positionals are required. path and delete can be added to those subparsers that need them.
import argparse
parser = argparse.ArgumentParser(description="""This script will clean the old component files.""")
p1 = argparse.ArgumentParser(add_help=False)
p1.add_argument("path", help="path to clean", choices = ["remote", "projects"])
p2 = argparse.ArgumentParser(add_help=False)
p2.add_argument("-d", "--delete", help="parameter for deleting the files from the filesystem", nargs='*', default=True)
sp = parser.add_subparsers(dest='component',description="component to clean")
sp.add_parser('hos', parents=[p1,p2])
sp.add_parser('hcr', parents=[p1,p2])
sp.add_parser('mdw', parents=[p2])
sp.add_parser('gui', parents=[p2])
print parser.parse_args()
sample use:
1848:~/mypy$ python2.7 stack21625446.py hos remote -d 1 2 3
Namespace(component='hos', delete=['1', '2', '3'], path='remote')
I used parents to simplify adding arguments to multiple subparsers. I made path a positional, since it is required (for 2 of the subparsers). In those cases --path just makes the user type more. With nargs='*', --delete has to belong to the subparsers so it can occur last. If it's nargs was fixed (None or number) it could be an argument of parser.

Python: How to make an option to be required in optparse?

I've read this http://docs.python.org/release/2.6.2/library/optparse.html
But I'm not so clear how to make an option to be required in optparse?
I've tried to set "required=1" but I got an error:
invalid keyword arguments: required
I want to make my script require --file option to be input by users. I know that the action keyword gives you error when you don't supply value to --file whose action="store_true".
You can implement a required option easily.
parser = OptionParser(usage='usage: %prog [options] arguments')
parser.add_option('-f', '--file',
dest='filename',
help='foo help')
(options, args) = parser.parse_args()
if not options.filename: # if filename is not given
parser.error('Filename not given')
Since if not x doesn't work
for some(negative,zero) parameters,
and to prevent lots of if tests,
i preferr something like this:
required="host username password".split()
parser = OptionParser()
parser.add_option("-H", '--host', dest='host')
parser.add_option("-U", '--user', dest='username')
parser.add_option("-P", '--pass', dest='password')
parser.add_option("-s", '--ssl', dest='ssl',help="optional usage of ssl")
(options, args) = parser.parse_args()
for r in required:
if options.__dict__[r] is None:
parser.error("parameter %s required"%r)
On the help message of each required variable Im writting a '[REQUIRED]' string at the beggining, to tag it to be parsed later, then I can simply use this function to wrap it around:
def checkRequiredArguments(opts, parser):
missing_options = []
for option in parser.option_list:
if re.match(r'^\[REQUIRED\]', option.help) and eval('opts.' + option.dest) == None:
missing_options.extend(option._long_opts)
if len(missing_options) > 0:
parser.error('Missing REQUIRED parameters: ' + str(missing_options))
parser = OptionParser()
parser.add_option("-s", "--start-date", help="[REQUIRED] Start date")
parser.add_option("-e", "--end-date", dest="endDate", help="[REQUIRED] End date")
(opts, args) = parser.parse_args(['-s', 'some-date'])
checkRequiredArguments(opts, parser)
The current answer with the most votes would not work if, for example, the argument were an integer or float for which zero is a valid input. In these cases it would say that there is an error. An alternative (to add to the several others here) would be to do e.g.
parser = OptionParser(usage='usage: %prog [options] arguments')
parser.add_option('-f', '--file', dest='filename')
(options, args) = parser.parse_args()
if 'filename' not in options.__dict__:
parser.error('Filename not given')
I'm forced to use python 2.6 for our solution so I'm stick to optparse module.
Here is solution I found to check for required options that works without specifying second time list of required options. Thus when you add new option you don't have to add it's name into the list of options to check.
My criteria for required option - option value should be not None and this options doesn't have default (user didn't specified add_option(default="...",...).
def parse_cli():
"""parse and check command line options, shows help message
#return: dict - options key/value
"""
import __main__
parser = OptionParser(description=__main__.__doc__)
parser.add_option("-d", "--days", dest="days",
help="Number of days to process")
parser.add_option("-p", "--period", dest="period_length",default="2",
help="number or hours per iteration, default value=%default hours")
(options, args) = parser.parse_args()
"""get dictionary of options' default values.
in this example: { 'period_length': '2','days': None}"""
defaults = vars(parser.get_default_values())
optionsdict = vars(options)
all_none = False
for k,v in optionsdict.items():
if v is None and defaults.get(k) is None:
all_none = True
if all_none:
parser.print_help()
sys.exit()
return optionsdict
There are at least two methods of implementing required options with optparse. As mentioned in the docs page, optparse doesn’t prevent you from implementing required options, but doesn’t give you much help at it either. Find below the examples found in files distributed with the source.
Although please note that optparse module is deprecated since version 2.7 and will not be developed further. You should use argparse module instead.
Version 1: Add a method to OptionParser which applications must call after parsing arguments:
import optparse
class OptionParser (optparse.OptionParser):
def check_required (self, opt):
option = self.get_option(opt)
# Assumes the option's 'default' is set to None!
if getattr(self.values, option.dest) is None:
self.error("%s option not supplied" % option)
parser = OptionParser()
parser.add_option("-v", action="count", dest="verbose")
parser.add_option("-f", "--file", default=None)
(options, args) = parser.parse_args()
print "verbose:", options.verbose
print "file:", options.file
parser.check_required("-f")
Source: docs/lib/required_1.txt
Version 2: Extend Option and add a required attribute; extend OptionParser to ensure that required options are present after parsing:
import optparse
class Option (optparse.Option):
ATTRS = optparse.Option.ATTRS + ['required']
def _check_required (self):
if self.required and not self.takes_value():
raise OptionError(
"required flag set for option that doesn't take a value",
self)
# Make sure _check_required() is called from the constructor!
CHECK_METHODS = optparse.Option.CHECK_METHODS + [_check_required]
def process (self, opt, value, values, parser):
optparse.Option.process(self, opt, value, values, parser)
parser.option_seen[self] = 1
class OptionParser (optparse.OptionParser):
def _init_parsing_state (self):
optparse.OptionParser._init_parsing_state(self)
self.option_seen = {}
def check_values (self, values, args):
for option in self.option_list:
if (isinstance(option, Option) and
option.required and
not self.option_seen.has_key(option)):
self.error("%s not supplied" % option)
return (values, args)
parser = OptionParser(option_list=[
Option("-v", action="count", dest="verbose"),
Option("-f", "--file", required=1)])
(options, args) = parser.parse_args()
print "verbose:", options.verbose
print "file:", options.file
Source: docs/lib/required_2.txt
I'm also stuck on python 2.6 (pining for python2.7 and argparse, which not only has required arguments, but lets me specify that one of a set must be supplied); my approach requires a second pass, but lets me prompt for missing arguments unless running in batch mode:
# from myscript
import helpers
import globalconfig
parser = optparse.OptionParser(usage=myheader,epilog=myfooter)
parser.add_option("-L","--last",
action="store",dest="last_name",default="",
help="User's last (family) name; prompted for if not supplied"
)
parser.add_option("-y","--yes",
action="store_true",dest="batch_flag",default=False,
help="don't prompt to confirm actions (batch mode)"
)
[...]
(options, args) = parser.parse_args()
globalconfig.batchmode = options.batch_flag
[...]
last = prompt_if_empty(options.last_name,
"Last name (can supply with \"-L\" or \"--last\" option):")
# from helpers.py
def prompt_if_empty(variable,promptstring):
if not variable:
if globalconfig.batchmode:
raise Exception('Required variable missing.')
print "%s" %promptstring
variable = raw_input(globalconfig.prompt)
return variable
(I'm thinking of making my own parser class that has common options for global configs baked in.)
Another answer to this question cited parser.error, which I was unfamiliar with when I wrote the code, but might have been a better choice.
As the optparse module is deprecated since version 2.7, you will probably find some more up to date examples here: Dead simple argparse example wanted: 1 argument, 3 results
I would use argparse library that has this functionality embedded:
PARSER.add_argument("-n", "--namespace", dest="namespace", required=True,
help="The path within the repo to the data base")
argparse reference

Display help message with Python argparse when script is called without any arguments

Assume I have a program that uses argparse to process command line arguments/options. The following will print the 'help' message:
./myprogram -h
or:
./myprogram --help
But, if I run the script without any arguments whatsoever, it doesn't do anything. What I want it to do is to display the usage message when it is called with no arguments. How is that done?
This answer comes from Steven Bethard on Google groups. I'm reposting it here to make it easier for people without a Google account to access.
You can override the default behavior of the error method:
import argparse
import sys
class MyParser(argparse.ArgumentParser):
def error(self, message):
sys.stderr.write('error: %s\n' % message)
self.print_help()
sys.exit(2)
parser = MyParser()
parser.add_argument('foo', nargs='+')
args = parser.parse_args()
Note that the above solution will print the help message whenever the error
method is triggered. For example, test.py --blah will print the help message
too if --blah isn't a valid option.
If you want to print the help message only if no arguments are supplied on the
command line, then perhaps this is still the easiest way:
import argparse
import sys
parser=argparse.ArgumentParser()
parser.add_argument('foo', nargs='+')
if len(sys.argv)==1:
parser.print_help(sys.stderr)
sys.exit(1)
args=parser.parse_args()
Note that parser.print_help() prints to stdout by default. As init_js suggests, use parser.print_help(sys.stderr) to print to stderr.
Instead of writing a class, a try/except can be used instead
try:
options = parser.parse_args()
except:
parser.print_help()
sys.exit(0)
The upside is that the workflow is clearer and you don't need a stub class. The downside is that the first 'usage' line is printed twice.
This will need at least one mandatory argument. With no mandatory arguments, providing zero args on the commandline is valid.
With argparse you could use ArgumentParser.print_usage():
parser.argparse.ArgumentParser()
# parser.add_args here
# sys.argv includes a list of elements starting with the program
if len(sys.argv) < 2:
parser.print_usage()
sys.exit(1)
Printing help
ArgumentParser.print_usage(file=None)
  Print a brief description of how the ArgumentParser should be invoked on the command line. If file is None, sys.stdout is assumed.
The cleanest solution will be to manually pass default argument if none were given on the command line:
parser.parse_args(args=None if sys.argv[1:] else ['--help'])
Complete example:
import argparse, sys
parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost', help='Host to connect to')
# parse arguments
args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])
# use your args
print("connecting to {}".format(args.host))
This will print complete help (not short usage) if called w/o arguments.
If you associate default functions for (sub)parsers, as is mentioned under add_subparsers, you can simply add it as the default action:
parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda x: parser.print_usage())
args = parser.parse_args()
args.func(args)
Add the try-except if you raise exceptions due to missing positional arguments.
If you have arguments that must be specified for the script to run - use the required parameter for ArgumentParser as shown below:-
parser.add_argument('--foo', required=True)
parse_args() will report an error if the script is run without any arguments.
Throwing my version into the pile here:
import argparse
parser = argparse.ArgumentParser()
args = parser.parse_args()
if not vars(args):
parser.print_help()
parser.exit(1)
You may notice the parser.exit - I mainly do it like that because it saves an import line if that was the only reason for sys in the file...
There are a pair of one-liners with sys.argv[1:] (a very common Python's idiom to refer the command line arguments, being sys.argv[0] the script's name) that can do the job.
The first one is self-explanatory, clean and pythonic:
args = parser.parse_args(None if sys.argv[1:] else ['-h'])
The second one is a little hackier. Combining the previously evaluated fact that an empty list is False with the True == 1 and False == 0 equivalences you get this:
args = parser.parse_args([None, ['-h']][not sys.argv[1:]])
Maybe too many brackets, but pretty clear if a previous argument selection was made.
_, *av = sys.argv
args = parser.parse_args([None, ['-h']][not av])
parser.print_help()
parser.exit()
The parser.exit method also accept a status (returncode), and a message value (include a trailing newline yourself!).
an opinionated example,
:)
#!/usr/bin/env python3
""" Example argparser based python file
"""
import argparse
ARGP = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawTextHelpFormatter,
)
ARGP.add_argument('--example', action='store_true', help='Example Argument')
def main(argp=None):
if argp is None:
argp = ARGP.parse_args() # pragma: no cover
if 'soemthing_went_wrong' and not argp.example:
ARGP.print_help()
ARGP.exit(status=64, message="\nSomething went wrong, --example condition was not set\n")
if __name__ == '__main__':
main() # pragma: no cover
Example calls:
$ python3 ~/helloworld.py; echo $?
usage: helloworld.py [-h] [--example]
Example argparser based python file
optional arguments:
-h, --help show this help message and exit
--example Example Argument
Something went wrong, --example condition was not set
64
$ python3 ~/helloworld.py --example; echo $?
0
Most of the answers here required another module, such as sys, to be imported or were using optional arguments. I wanted to discover an answer that used only argparse, worked with required arguments, and if possible worked without catching exceptions. I ended up with the following:
import argparse
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser(add_help=False)
arg_parser.add_argument('input_file', type=str, help='The path to the input file.')
arg_parser.add_argument('output_file', type=str, help='The path to the output file.')
arg_parser.add_argument('-h','--help', action='store_true', help='show this help message and exit')
arg_parser.usage = arg_parser.format_help()
args = arg_parser.parse_args()
The main idea was to use the format_help function in order to provide the help string to the usage statement. Setting add_help to False in the call to ArgumentParser() prevents the help statement from printing twice in certain circumstances. However, I had to create an argument for the optional help argument that mimicked the typical help message once it was set to False in order to display the optional help argument in the help message. The action is set to store_true in the help argument to prevent the help message from filling in a value like HELP for the parameter when it prints the help message.
So for a really simple answer. Most of the time with argparse you are checking to see if parameters are set anyway, to call a function that does something.
If no parameters, just else out at the end and print the help. Simple and works.
import argparse
import sys
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("--holidays", action='store_true')
group.add_argument("--people", action='store_true')
args=parser.parse_args()
if args.holidays:
get_holidays()
elif args.people:
get_people()
else:
parser.print_help(sys.stderr)
Here is another way to do it, if you need something flexible where you want to display help if specific params are passed, none at all or more than 1 conflicting arg:
import argparse
import sys
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--days', required=False, help="Check mapped inventory that is x days old", default=None)
parser.add_argument('-e', '--event', required=False, action="store", dest="event_id",
help="Check mapped inventory for a specific event", default=None)
parser.add_argument('-b', '--broker', required=False, action="store", dest="broker_id",
help="Check mapped inventory for a broker", default=None)
parser.add_argument('-k', '--keyword', required=False, action="store", dest="event_keyword",
help="Check mapped inventory for a specific event keyword", default=None)
parser.add_argument('-p', '--product', required=False, action="store", dest="product_id",
help="Check mapped inventory for a specific product", default=None)
parser.add_argument('-m', '--metadata', required=False, action="store", dest="metadata",
help="Check mapped inventory for specific metadata, good for debugging past tix", default=None)
parser.add_argument('-u', '--update', required=False, action="store_true", dest="make_updates",
help="Update the event for a product if there is a difference, default No", default=False)
args = parser.parse_args()
days = args.days
event_id = args.event_id
broker_id = args.broker_id
event_keyword = args.event_keyword
product_id = args.product_id
metadata = args.metadata
make_updates = args.make_updates
no_change_counter = 0
change_counter = 0
req_arg = bool(days) + bool(event_id) + bool(broker_id) + bool(product_id) + bool(event_keyword) + bool(metadata)
if not req_arg:
print("Need to specify days, broker id, event id, event keyword or past tickets full metadata")
parser.print_help()
sys.exit()
elif req_arg != 1:
print("More than one option specified. Need to specify only one required option")
parser.print_help()
sys.exit()
# Processing logic here ...
Cheers!
I like to keep things as simple as possible, this works great:
#!/usr/bin/env python3
Description = """Tool description"""
Epilog = """toolname.py -a aflag -b bflag with these combined it does blah"""
arg_parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=Description,
epilog=Epilog,
)
try:
if len(sys.argv) == 1:
arg_parser.print_help()
except Exception as e:
print(e)
This is how I start all my tools as its always good to include some examples
When call add_subparsers method save the first positional argument to dest= and check value after argparse has been initialized, like this:
subparsers = parser.add_subparsers(dest='command')
And just check this this variable:
if not args.command:
parser.print_help()
parser.exit(1) # If exit() - exit code will be zero (no error)
Full example:
#!/usr/bin/env python
""" doc """
import argparse
import sys
parser = argparse.ArgumentParser(description=__doc__)
subparsers = parser.add_subparsers(dest='command',
help='List of commands')
list_parser = subparsers.add_parser('list',
help='List contents')
list_parser.add_argument('dir', action='store',
help='Directory to list')
create_parser = subparsers.add_parser('create',
help='Create a directory')
create_parser.add_argument('dirname', action='store',
help='New directory to create')
create_parser.add_argument('--read-only', default=False, action='store_true',
help='Set permissions to prevent writing to the directory')
args = parser.parse_args()
if not args.command:
parser.print_help()
parser.exit(1)
print(vars(args)) # For debug
This approach is a lot more elegant than most others. Instead of overriding error(), you can control the behaviour a lot more precisely by wrapping the parse_args() method:
import sys
import argparse
HelpFlags = ('help', '--help', '-h', '/h', '?', '/?', )
class ArgParser (argparse.ArgumentParser):
def __init__(self, *args, **kws):
super().__init__(*args, **kws)
def parse_args(self, args=None, namespace=None):
if args is None:
args = sys.argv[1:]
if len(args) < 1 or (args[0].lower() in HelpFlags):
self.print_help(sys.stderr)
sys.exit()
return super().parse_args(args, namespace)
Set your positional arguments with nargs, and check if positional args are empty.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?')
args = parser.parse_args()
if not args.file:
parser.print_help()
Reference Python nargs
If your command is something where a user needs to choose some action, then use a mutually exclusive group with required=True.
This is kind of an extension to the answer given by pd321.
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--batch", action='store', type=int, metavar='pay_id')
group.add_argument("--list", action='store_true')
group.add_argument("--all", action='store_true', help='check all payments')
args=parser.parse_args()
if args.batch:
print('batch {}'.format(args.batch))
if args.list:
print('list')
if args.all:
print('all')
Output:
$ python3 a_test.py
usage: a_test.py [-h] (--batch pay_id | --list | --all)
a_test.py: error: one of the arguments --batch --list --all is required
This only give the basic help. And some of the other answers will give you the full help. But at least your users know they can do -h
This isn't good (also, because intercepts all errors), but:
def _error(parser):
def wrapper(interceptor):
parser.print_help()
sys.exit(-1)
return wrapper
def _args_get(args=sys.argv[1:]):
parser = argparser.ArgumentParser()
parser.error = _error(parser)
parser.add_argument(...)
...
Here is definition of the error function of the ArgumentParser class.
As you see, the following signature takes two arguments. However, functions outside the class know nothing about first argument self, because, roughly speaking, this argument is for the class.
def _error(self, message):
self.print_help()
sys.exit(-1)
def _args_get(args=sys.argv[1:]):
parser = argparser.ArgumentParser()
parser.error = _error
...
will output:
"AttributeError: 'str' object has no attribute 'print_help'"
You can pass parser (self) in _error function, by calling it:
def _error(self, message):
self.print_help()
sys.exit(-1)
def _args_get(args=sys.argv[1:]):
parser = argparser.ArgumentParser()
parser.error = _error(parser)
...
But if you don't want exit the program right now, return it:
def _error(parser):
def wrapper():
parser.print_help()
sys.exit(-1)
return wrapper
Nonetheless, parser doesn't know that it has been modified. Thus, when an error occurs, it will print the cause of it (by the way, it's a localized translation). So intercept it:
def _error(parser):
def wrapper(interceptor):
parser.print_help()
sys.exit(-1)
return wrapper
Now, when an error occurs, parser will print the cause of it, and you'll intercept it, look at it, and... throw out.

Categories