Print help when positional arguments not provided - python

I'm trying to execute my Python program. It uses one positional argument. When the positional argument is not provided I want to print help. But all I get is
error : too few arguments
Here is the Python code :
parser = argparse.ArgumentParser(
description = '''My Script ''')
parser.add_argument('I', type=str, help='Provide the release log file')
args = parser.parse_args()
I'm expecting the following output when no positional arguments are specified:
usage: script.py [-h] I
My Script
positional arguments:
I Provide the release log file
optional arguments:
-h, --help show this help message and exit
Any thoughts how to achieve this would be appreciated.

argparse doesn't work that way. You need to ask for help with the -h argment. Otherwise it just gives the usage along with the error message.
0015:~/mypy$ python3 stack41671660.py
usage: stack41671660.py [-h] I
stack41671660.py: error: the following arguments are required: I
0015:~/mypy$ python stack41671660.py
usage: stack41671660.py [-h] I
stack41671660.py: error: too few arguments
0015:~/mypy$ python stack41671660.py -h
usage: stack41671660.py [-h] I
My Script
positional arguments:
I Provide the release log file
optional arguments:
-h, --help show this help message and exit
You could make the positional argument 'optional' with nargs='?', and add a test for the default value:
print(args)
if args.I is None:
parser.print_help()
sample runs:
0016:~/mypy$ python stack41671660.py
Namespace(I=None)
usage: stack41671660.py [-h] [I]
My Script
positional arguments:
I Provide the release log file
optional arguments:
-h, --help show this help message and exit
0019:~/mypy$ python stack41671660.py 2323
Namespace(I='2323')
Another option is to customize the parser.error method, so that it does print_help instead of print_usage. That will affect all parsing errors, not just this missing positional.
def error(self, message):
"""error(message: string)
Prints a usage message incorporating the message to stderr and
exits.
If you override this in a subclass, it should not return -- it
should either exit or raise an exception.
"""
# self.print_usage(_sys.stderr)
self.print_help(_sys.stderr)
args = {'prog': self.prog, 'message': message}
self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
`

Related

Better help for argparse subcommands

Given the following code snippet:
import argparse
import sys
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help="subcommand help")
command1 = subparsers.add_parser("foo", description="Run foo subcommand")
command2 = subparsers.add_parser("bar", description="Run bar subcommand")
opts = parser.parse_args(sys.argv[1:])
When I print help for this I get this:
usage: test.py [-h] {foo,bar} ...
positional arguments:
{foo,bar} subcommand help
optional arguments:
-h, --help show this help message and exit
Is there a way to make it print something like this instead:
usage: test.py [-h] {foo,bar} ...
subcommands:
foo Run foo subcommand
bar Run bar subcommand
optional arguments:
-h, --help show this help message and exit
without supplying a custom formatter? If I change the formatter then it also changes everything else about how the help is printed, but in my case I just want to change the way that subcommand help is printed from the parent (sub)command.
You need to set the help parameter, not the description parameter, to get the output you desire:
import argparse
import sys
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help="subcommand help")
command1 = subparsers.add_parser("foo", help="Run foo subcommand")
command2 = subparsers.add_parser("bar", help="Run bar subcommand")
opts = parser.parse_args(sys.argv[1:])
Output:
usage: test.py [-h] {foo,bar} ...
positional arguments:
{foo,bar} subcommand help
foo Run foo subcommand
bar Run bar subcommand
optional arguments:
-h, --help show this help message and exit
The argparse docs have this to say about the help value:
The help value is a string containing a brief description of the argument. When a user requests help (usually by using -h or --help at the command line), these help descriptions will be displayed with each argument.
And this to say to about the description value:
This argument gives a brief description of what the program does and how it works. In help messages, the description is displayed between the command-line usage string and the help messages for the various arguments.

Python argparse --help description in sentence case

Extremely nitpicky question, but it annoys me that the default argparse help message is a sentence fragment. For example, for a script that contains
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
The -h and --help flag message shows:
$ tmp.py --help
usage: tmp.py [-h]
optional arguments:
-h, --help show this help message and exit
However I prefer complete sentences in documentation and "sentence case" for headers:
$ tmp.py --help
Usage: tmp.py [-h]
Optional arguments:
-h, --help Show this help message and exit.
How can I keep the behavior of script -h and script --help but change the message?
Welp found the answer 5 seconds later.
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser(add_help=False)
parser.parse_args()
parser.add_argument(
'-h', '--help', action='help', help='Show this help message and exit.')
Defining your own action='help' argument is probably the best answer. But it is possible to edit the default help.
All the defined Actions are collected in the parser._actions list. Yes, it is marked hidden, but people do access it as needed. Normally the help action is the first one created (as default), so it is element [0] of that list.
In [15]: parser._actions[0].help
Out[15]: 'show this help message and exit'
In [16]: parser._actions[0].help = "Show this help message and exit."
testing:
In [17]: parser.print_help()
usage: ipython3 [-h] {mySubcommand,m} ...
positional arguments:
{mySubcommand,m} sub-command help
mySubcommand (m)
Subcommand help
optional arguments:
-h, --help Show this help message and exit.
def main():
parser = argparse.ArgumentParser()
# specific file
parser.add_argument('-f', '--file', type=str, default='file',
help=Fore.BLUE + '--file access_log -extract-ip' + Style.RESET_ALL)
# http get file
parser.add_argument('-hgf', '--http-get-file', type=str, default='http-get-file',
help=Fore.BLUE + '--http-get-file URL' + Style.RESET_ALL)

argparser.print_help() not printing the full message

I have a program where I tried to put help in my code using argparse:
import argparse,sys
parser = argparse.ArgumentParser(description='prog desc')
parser.add_argument('path', help='name of directory')
args = parser.parse_args()
parser.print_help()
this prints:
>python testArgs.py
usage: testArgs.py [-h] path
testArgs.py: error: too few arguments
but I'm expecting same as if I entered -h:
>python testArgs.py -h
usage: testArgs.py [-h] path
prog desc
positional arguments:
path name of directory
optional arguments:
-h, --help show this help message and exit
But if I switch the position of the print_help() before parse_args(), then it works right:
import argparse,sys
parser = argparse.ArgumentParser(description='prog desc')
parser.add_argument('path', help='name of directory')
parser.print_help()
args = parser.parse_args()
output:
>python testArgs.py
usage: testArgs.py [-h] path
prog desc
positional arguments:
path name of directory
optional arguments:
-h, --help show this help message and exit
usage: testArgs.py [-h] path
testArgs.py: error: too few arguments
What am I doing wrong?
In your first example your program doesn't reach the parser.print_help() method, it fails on parser.parse_args(), prints the default error message (which is testArgs.py: error: too few arguments) and exits the program.
In your second example, when you switch between the functions, it still behaves the same but you see the help details because you called the print_help() function before the program fails (you can see it fails because it still prints the error message at the end).
If you want to print the help message when an argparse error occurred, read this post:
Display help message with python argparse when script is called without any arguments

Add command line arguments with existing one

Getting an error
"error: unrecognized arguments: "
For built in arguments when trying to build my own command line arguments. I am defining my arguments as:
import argparse
def fn_a(parsed_args):
print("hello i am in function fn_a")
def fn_b(parsed_args):
print("hello i am in function fn_b")
parser=argparse.ArgumentParser(description="my first arg scripting")
parser.add_argument('--a', dest='action', action='store_const',const=fn_a)
parser.add_argument('--b', dest='action', action='store_const', const=fn_b)
parsed_args = parser.parse_args()
if parsed_args.action is None:
parser.parse_args(['-h'])
parsed_args.action(parsed_args)
What may be the problems?
You could change your code to use a try-except block and only parse known arguments, for example:
try:
parsed_args, unknown_args = parser.parse_known_args()
except:
parsed_args = None
if parsed_args and parsed_args.action:
parsed_args.action(parsed_args)
else:
try:
parser.parse_known_args(['-h'])
except:
pass
Changing the last bit to:
if parsed_args.action is None:
parser.parse_args(['-h'])
parsed_args.action(parsed_args)
lets me do:
1218:~/mypy$ python stack42067791.py
usage: stack42067791.py [-h] [--a] [--b]
my first arg scripting
optional arguments:
-h, --help show this help message and exit
--a
--b
1218:~/mypy$ python stack42067791.py --a
hello i am in function fn_a
1218:~/mypy$ python stack42067791.py --b
hello i am in function fn_b
1218:~/mypy$ python stack42067791.py -h
usage: stack42067791.py [-h] [--a] [--b]
my first arg scripting
optional arguments:
-h, --help show this help message and exit
--a
--b
That is it responds to --a and --b, and replicates -h if neither is provided.
Anything else produces a standard argparse error message with usage
1218:~/mypy$ python stack42067791.py foo
usage: stack42067791.py [-h] [--a] [--b]
stack42067791.py: error: unrecognized arguments: foo
1220:~/mypy$ python stack42067791.py --b foo
usage: stack42067791.py [-h] [--a] [--b]
stack42067791.py: error: unrecognized arguments: foo
Using parse_known_args lets you throw away the foo in that last case. But why would you want to do that?
parsed_args,extras = parser.parse_known_args()
print(parsed_args, extras)
prints things like
(Namespace(action=None), [])
(Namespace(action=<function fn_b at 0xb71f1b54>), ['foo'])
(Namespace(action=<function fn_a at 0xb744d25c>), [])

How to make positional argument optional in argparser based on some condition in python

I want to write a python code in which, based on some arguments passed from command line I want to make positional argument optional.
For example,
My python program is test.py, and with it I can give --init, --snap, --check options. Now if I have given --snap and --check option, then file name is compulsory i.e.
test.py --snap file1
but if I have given --init option then it should not take any other arguments. i.e. in this case file name is optional:
test.py --init
How to implement this condition
If you are ok with changing the command line you are passing a little bit, then a set of subparsers should work.
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='sub-command help')
init_parser = subparsers.add_parser('init', help='do the init stuff')
snap_parser = subparsers.add_parser('snap', help='do the snap stuff')
snap_parser.add_argument('--file', '-f', required=True)
check_parser = subparsers.add_parser('check', help='do the check stuff')
check_parser.add_argument('--file', '-f', required=True)
args = parser.parse_args()
print args
And then the output...
> python foobar.py init
Namespace()
> python foobar.py check
usage: foobar.py check [-h] --file FILE
foobar.py check: error: argument --file/-f is required
> python foobar.py check --file foobar.txt
Namespace(file='foobar.txt')
General help:
> python foobar.py --help
usage: foobar.py [-h] {init,snap,check} ...
positional arguments:
{init,snap,check} sub-command help
init do the init stuff
snap do the snap stuff
check do the check stuff
optional arguments:
-h, --help show this help message and exit
And specific sub-command help
> python foobar.py snap -h
usage: foobar.py snap [-h] --file FILE
optional arguments:
-h, --help show this help message and exit
--file FILE, -f FILE
Your other option is to use nargs as #1.618 has already mentioned.
argparse allows you to specify that certain args have their own args, like so:
parser.add_argument("--snap", nargs=1)
or use a + to allow for an arbitrary number of "subargs"
After you call parse_args(), the values will be in a list:
filename = args.snap[0]

Categories

Resources