Argparse - error handling the required argument - python

I have a script which basically asks the user for the argument to be passed to the program as below:
parser = argparse.ArgumentParser()
parser.add_argument("-t" , required = True)
args , unknown = parser.parse_known_args()
#rest of the code
Now I wanna do error handling so it shows the message I want and run the functions I want in case the user didn't enter the argument or entered it incorrectly. I have tried putting everything in try and except however I couldn't find the proper error type to do it for me
try:
parser = argparse.ArgumentParser()
parser.add_argument("-t" , required = True)
args , unknown = parser.parse_known_args()
#rest of the code
except argparse.ArgumentError:
myfunc()
However, this way if I get any other errors in my code, it still caught here and not in their own try and except.

Argparse raises a SystemExit when a required argument is missing.
You could use the else clause in the try/except to execute the rest of your code only if the arguments were parsed correctly:
try:
parser = argparse.ArgumentParser()
parser.add_argument("-t" , required = True)
args , unknown = parser.parse_known_args()
except SystemExit:
func_for_wrong_args()
else:
func_for_correct_args()

Related

parser.parse_args does not recognize arguments in celery task

I have been Googling this problem most of the morning and find no solution.
Using the following:
parser.add_argument('-t', '--task', dest='task', action='store')
args = parser.parse_args(cmd_args)
When cmd_args contains "['-t my_task']" everything works as I expect.
When cmd_args contains "['--task my_task']" an exception is thrown.
The important part of the error says: "error: unrecognized arguments: --task mytask"

Python ArgParse add help message when subparsers are omitted [duplicate]

With python's argparse, how do I make a subcommand a required argument? I want to do this because I want argparse to error out if a subcommand is not specified. I override the error method to print help instead. I have 3-deep nested subcommands, so it's not a matter of simply handling zero arguments at the top level.
In the following example, if this is called like so, I get:
$./simple.py
$
What I want it to do instead is for argparse to complain that the required subcommand was not specified:
import argparse
class MyArgumentParser(argparse.ArgumentParser):
def error(self, message):
self.print_help(sys.stderr)
self.exit(0, '%s: error: %s\n' % (self.prog, message))
def main():
parser = MyArgumentParser(description='Simple example')
subs = parser.add_subparsers()
sub_one = subs.add_parser('one', help='does something')
sub_two = subs.add_parser('two', help='does something else')
parser.parse_args()
if __name__ == '__main__':
main()
There was a change in 3.3 in the error message for required arguments, and subcommands got lost in the dust.
http://bugs.python.org/issue9253#msg186387
There I suggest this work around, setting the required attribute after the subparsers is defined.
parser = ArgumentParser(prog='test')
subparsers = parser.add_subparsers()
subparsers.required = True
subparsers.dest = 'command'
subparser = subparsers.add_parser("foo", help="run foo")
parser.parse_args()
update
A related pull-request: https://github.com/python/cpython/pull/3027
In addition to hpaulj's answer: you can also use the required keyword argument with ArgumentParser.add_subparsers() since Python 3.7. You also need to pass dest as argument. Otherwise you will get an error: TypeError: sequence item 0: expected str instance, NoneType found.
Example file example.py:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='command', required=True)
foo_parser = subparsers.add_parser("foo", help="command foo")
args = parser.parse_args()
Output of the call without an argument:
$ python example.py
usage: example.py [-h] {foo} ...
example.py: error: the following arguments are required: command
How about using required=True? More info here.
You can use the dest argument, which is documented in the last example in the documentation for add_subparsers():
# required_subparser.py
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subparser_name')
one = subparsers.add_parser('one')
two = subparsers.add_parser('two')
args = parser.parse_args()
Running with Python 2.7:
$python required_subparser.py
usage: required_subparser.py [-h] {one,two} ...
required_subparser.py: error: too few arguments
$python required_subparser.py one
$# no error

try and except code block in python to raise error for missing argument

So I am trying to implement basic try and except logic in my code to read and hand exceptions while reading the command line args:
I have been able to accomplish this. but I want to explicitly say while throwing an error which argument is missing like 1st or 2nd.
Here is my code.
import sys
import logging
try:
job_name = sys.argv[1]
odate = sys.argv[2]
except IndexError as err:
print('OS error: {0}'.format(err))
else:
print('Correct arguments were provided')
how could I produce an error to let the user know which arg is missing?
The exception IndexError doesn't include the accessed index that resulted to error, you can examine the stack traceback via traceback.format_exc() or sys.exc_info() or something else just to capture the accessed index but that would be too complicated.
Alternative Solution 1
Check first if the length of the arguments is correct and act accordingly:
ARG_COUNT = 2
assert len(sys.argv) == ARG_COUNT + 1
# No more need of try-except block
job_name = sys.argv[1]
odate = sys.argv[2]
Alternative Solution 2
This is the preferable solution. Use argparse instead. It handles the behavior that you need. You can also customize the settings for each argument further e.g. specifying the expected data type of an argument.
import argparse
parser = argparse.ArgumentParser(description='Process some inputs.')
parser.add_argument('job_name')
parser.add_argument('odate')
args = parser.parse_args()
print(args, args.job_name, args.odate)
Output
$ python script.py
usage: script.py [-h] job_name odate
script.py: error: the following arguments are required: job_name, odate
$ python script.py some
usage: script.py [-h] job_name odate
script.py: error: the following arguments are required: odate
$ python script.py some thing
Namespace(job_name='some', odate='thing') some thing

How to upgrade optparse() code to argparse() code

I am trying to upgrade from pythons now deprecated optparse module into the new argparse module. However I am having some trouble upgrading my code. I have been using pythons documentation on doing just that yet I seem to have hit a wall.
Here is the original snippet of code using the optparse module
if __name__ == "__main__":
parser = optparse.OptionParser(usage="%prog [options] hostname")
parser.add_option("-p","--port", dest="port",
help="Port to use for socket connection [default: %default]",
default=33434, metavar="PORT")
parser.add_option("-m", "--max-hops", dest="max_hops",
help="Max hops before giving up [default: %default]",
default=30, metavar="MAXHOPS")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error('Incorrect number of arguments')
else:
dest_name = args[0]
sys.exit(main(dest_name=dest_name,
port=int(options.port),
max_hops=int(options.max_hops)))
Now here is the partial upgraded code I was able to do
if __name__ == "__main__":
parser = argparse.ArgumentParser(usage="%(prog)s [options] hostname")
parser.add_argument("-p","--port", dest="port",
help="Port to use for socket connection [default: %(default)s]",
default=33434, metavar="PORT")
parser.add_argument("-m", "--max-hops", dest="max_hops",
help="Max hops before giving up [default: %(default)s]",
default=30, metavar="MAXHOPS")
args = parser.parse_args()
if len(sys.argv) != 1:
parser.error('Incorrect number of arguments')
else:
dest_name = sys.argv
sys.exit(main(dest_name=dest_name,
port=int(options.port),
max_hops=int(options.max_hops)))
When I try running the code I keep receiving this error:
*port=int(options.port),
NameError: name 'options' is not defined*
In the optparse() module I defined it here
(options, args) = parser.parse_args()
When I tried to define it the same way as the optparse module it gives me another error:
*TypeError: 'Namespace' object is not iterable*
(I understand that the (options, args) = parser.parse_args() was changed to args = parser.parse_args() in the argparse module. I was just messing around trying to find a solution. I was desperate at this point)
Finally I tried to change 'options' in port=int(options.port) to port=int(args.port) Which gave me even more errors. After reading the documentation about upgrading optparse to argparse I think I might know where my problems resides. The documentation states
"Replace options, args = parser.parse_args() with args = parser.parse_args() and add additional add_argument() calls for the positional arguments."
So I think my problem is that I'm not adding the additional add_argument() calls for the positional arguments. Since I am new and still trying to learn pythons parsing modules I do not know exactly how to go about accomplishing that.
Get rid of this optparse bollocks:
if len(sys.argv) != 1:
parser.error('Incorrect number of arguments')
else:
dest_name = sys.argv
And add a positional argument instead
parser.add_argument('hostname')
...
dest_name = args.hostname

python argparse: How can I display help automatically on error?

Currently when I enter invalid options or omit positional arguments, argparse kicks me back to the prompt and displays the usage for my app. This is ok, but I would rather automatically display the full help listing (that explains the options, etc) than require the user to type
./myscript.py -h
Thanks!
Jamie
To print help you might want to use: print_help function on ArgumentParser instance
parser = argparse.ArgumentParser()
(...)
parser.print_help()
To print help message on error you need to create own subclass of ArgumentParser instance, that overrides error() method. For example like that:
class MyParser(argparse.ArgumentParser):
def error(self, message):
sys.stderr.write('error: %s\n' % message)
self.print_help()
sys.exit(2)
When this parser encounters unparseable argument line it will print help.
This thread over at Google groups has the following code snippet which seems to do the trick (modified slightly).
class DefaultHelpParser(argparse.ArgumentParser):
def error(self, message):
sys.stderr.write('error: %s\n' % message)
self.print_help()
sys.exit(2)
I just fixed this same problem myself using the following syntax:
parser = ArgumentParser()
... add arguments ...
parser.usage = parser.format_help()
args = parser.parse_args()
Suppress printing of usage with usage=argparse.SUPPRESS. Then catch the SystemExit exception that ArgumentParser raises on error, print the help, and exit by raising the exception again.
parser = argparse.ArgumentParser(usage=argparse.SUPPRESS)
parser.add_argument(...)
try:
args = parser.parse_args()
except SystemExit:
parser.print_help()
raise
You, also, can print help without using a class or exception:
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(...)
...
. Just wrap ArgumentParser.error function in your and intercept message argument. I answered, there, earlier:
https://stackoverflow.com/a/60714163/10152015

Categories

Resources