Print all argparse arguments including defaults - python

I want to log the usage of a python program which uses the argparse module. Currently, the logger records the command line usage similar to the answer given in this post. However, that only gives the command line arguments, and not including the defaults set later in argparse (which is the intended use, after all). Is there a simple way to print ALL the argparse options to create a nice, tidy usage log entry that includes the default values?
It isn't difficult to go into the argparse namespace to fetch each argument by name, but I am hoping that someone has a concise way of extracting the needed info.
In response to the accepted answer:
Great! If anyone is interest, here is my implementation with logging:
logger.info("Usage:\n{0}\n".format(" ".join([x for x in sys.argv])))
logger.debug("All settings used:") for k,v in sorted(vars(args).items()):
logger.debug("{0}: {1}".format(k,v))

I've done something similar in an application, hopefully the below snippets give you what you need. It is the call to vars that gave me a dictionary of all the arguments.
parser = argparse.ArgumentParser(description='Configure')
....
args = parser.parse_args()
......
options = vars(args)

Related

How do I suppress an argument when nothing is input on command line?

#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--selection', '-s')
parser.add_argument('--choice', '-c', default = argparse.SUPPRESS)
args = parser.parse_args()
def main(selection, choice):
print(selection)
print(choice)
if __name__=='__main__':
main(args.selection, args.choice)
The example provided is just to provide something simple and short that accurately articulates the actual problem I am facing in my project. My goal is to be able to ignore an argument within the code body when it is NOT typed into the terminal. I would like to be able to do this through passing the argument as a parameter for a function. I based my code off of searching 'suppress' in the following link: https://docs.python.org/3/library/argparse.html
When I run the code as is with the terminal input looking like so: python3 stackquestion.py -s cheese, I receive the following error on the line where the function is called:
AttributeError: 'Namespace' object has no attribute 'choice'
I've tried adding the following parameter into parser like so:
parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
I've also tried the above with
parser.add_argument('--choice', '-c')
But I get the same issue on the same line.
#Barmar answered this question in the comments. Using 'default = None' in parser.add_argument works fine; The code runs without any errors. I selected the anser from #BorrajaX because it's a simple solution to my problem.
According to the docs:
Providing default=argparse.SUPPRESS causes no attribute to be added if the command-line argument was not present:
But you're still assuming it will be there by using it in the call to main:
main(args.selection, args.choice)
A suppressed argument won't be there (i.e. there won't be an args.choice in the arguments) unless the caller specifically called your script adding --choice="something". If this doesn't happen, args.choice doesn't exist.
If you really want to use SUPPRESS, you're going to have to check whether the argument is in the args Namespace by doing if 'choice' in args: and operate accordingly.
Another option (probably more common) can be using a specific... thing (normally the value None, which is what argparse uses by default, anyway) to be used as a default, and if args.choice is None, then assume it hasn't been provided by the user.
Maybe you could look at this the other way around: You want to ensure selection is provided and leave choice as optional?
You can try to set up the arguments like this:
parser = argparse.ArgumentParser()
parser.add_argument('--selection', '-s', required=True)
parser.add_argument('--choice', '-c')
args = parser.parse_args()
if __name__ == '__main__':
if args.choice is None:
print("No choice provided")
else:
print(f"Oh, the user provided choice and it's: {args.choice}")
print(f"And selection HAS TO BE THERE, right? {args.selection}")

Argparse, displaying custom help text without any of the boilerplate argparse text

After looking at about a dozen questions, I can't seem to find an answer.
I have a python CLI i've written using argparse. I have a main command that does nothing but regurgitate help text and then 4 subcommands. My boss wants a very specific output for the help text. He has me write it out as a text file and then we use that text file to display the help text.
However, in some circumstances, it STILL outputs parts of the argparse help text.
For example, if I run my program with no subcommands, it just outputs our help text from the file. But if I use "-h" or "--help" it will output our help text, followed by the list of positional and optional arguments and other argparse stuff. We don't want that.
I could use "add_help=False" but we want the user to be able to type -h and still get our help text. If we set add help to false, it will display our help text followed by the error "-h not recognized".
Also doing this does nothing for when the user uses -h after a subcommand. I set help=None and usage is set to my custom help text for each subcommand, but it still shows the boilerplate argparse info at the end.
This is what I want to happen: user types in the main command with no subcommands prints my custom help text and nothing else. The user types the main command, no subcommand, followed by -h/--help and it prints my custom help text and nothing else. User types in the main command, one of the subcommands, followed by -h/--help and it outputs my help text and nothing else. User types the main command, a subcommand, and then wrong arguments or too many/ too few arguments displays my help text and nothing else. Basically I only ever want it to print nothing, or print just my help text.
how do I do that? here is my main function where the parsers and subparsers are all configured:
def main():
# Import help text from file
p = Path(__file__).with_name("help.txt")
with p.open() as file:
help_text = file.read()
# Configure the top level Parser
parser = argparse.ArgumentParser(prog='myprog', description='My program', usage=help_text)
subparsers = parser.add_subparsers()
# Create Subparsers to give subcommands
parser_one = subparsers.add_parser('subcommandone', prog='subcommandone', usage=help_text, help=None)
parser_one.add_argument('arg1', type=str)
parser_one.add_argument('-o', '--option1', default='mydefault', type=str)
parser_two= subparsers.add_parser('subcommandtwo', usage=help_text, help=None, prog='subcommandtwo')
parser_three= subparsers.add_parser('subcommandthree', usage=help_text, help=None, prog='subcommandthree')
parser_four= subparsers.add_parser('subcommandfour', usage=help_text, help=None, prog='subcommandfour')
# Assign subparsers to their respective functions
parser_one.set_defaults(func=functionone)
parser_two.set_defaults(func=functiontwo)
parser_three.set_defaults(func=functionthree)
parser_four.set_defaults(func=functionfour)
parser.set_defaults(func=base_case)
# Parse the arguments and call appropriate functions
args = parser.parse_args()
if len(sys.argv) == 1:
args.func(args, parser)
else:
args.func(args)
Any thoughts?
You can use sys.exit() after the help text has been displayed, and before the parsing has begun, to avoid problems with "-h not recognized".
So anywhere before the line
# Parse the arguments and call appropriate functions
add
if len(sys.argv) == 1 or '-h' in sys.argv or '--help' in sys.argv:
print(help_text)
sys.exit(1)
In situations where that is not good enough you can subclass argparse.HelpFormatter like so
usage_help_str = 'myscript command [options]'
epilog_str = "More info can be found at https://..."
class Formatter(argparse.HelpFormatter):
# override methods and stuff
def formatter(prog):
return Formatter(prog)
parser = argparse.ArgumentParser(formatter_class=formatter, epilog=epilog_str, usage=usage_help_str, add_help=False)
I tried looking around for documentation on subclassing the helpFormatter, but I couldn't find anything. It looks like people are just looking at the source code to figure out how to subclass it.

Looking to set a global default argument with argparse [duplicate]

This question already has answers here:
Display help message with Python argparse when script is called without any arguments
(18 answers)
Closed 3 years ago.
I am writting a new script and would like for the -h or --help argument to be called by default when the script is called without any parameters. So for example if someone calls command_line_utility.py then I want it to print the output you would get with command_line_utility.py -h.
I have dug around in the docs and looked at some examples, but all of them were specifying default argument values and not actually having arg parse call a default argument.
# Setting up Main Argument Parser
main_parser = argparse.ArgumentParser(description="A set of python web utility scripts")
main_parser.add_argument("-v",'--version', action='version', version='kuws V0.0.1')
# Setting up the main subparser
subparsers = main_parser.add_subparsers(help="Available commands found below, for more info on a command use: python command_line_utility.py <command> -h or kuws <command> -h")
"""Code below handles 'redirects' command in the main script
i.e. >python command_line_utility.py redirects or kuws redirects
"""
redirects_parser = subparsers.add_parser('redirects', argument_default='-u',
help='Allows you to trace redirects and get other information')
redirects_parser.add_argument('-u', "--url",
help='usage: python main.py redirects -u <url>; Lets you see the trace for a url', nargs='?', dest="trace_url")
As it stands when I run the file nothing actually gets printed to the command line. No help text or errors or anything.
I'm afraid argparse doesn't have any built-in support for this, but you can identify this situation and print the help message:
import sys
if len(sys.argv)==1:
parser.print_help(sys.stderr)
sys.exit(1)
Checking that len(sys.argv)==1 and in that case calling the print_help method of the parser as described in this answer to a similar question is a possible way to print the help message defined in the parser when no arguments are given.
When using subparsers, a common scheme is using set_defaults(func=<function to be called>) and then calling this function (as explained in sub-commands).
You can simply define a first set_defaults(func=help) at first that will be overwritten with the functions of your command.
Note that you can also make the command required when you declare your subparsers (add_subparsers(..., required='True')) and thus, when the user invokes without a command, she will get an error with the usage.

Subparser -- show help for both parser and subparser

I've looked through dozens of similar SO questions but haven't find suitable solutions so please forgive me in case of a dublicate.
I have a problem similar to this one
I want to have a parser+subparser pair, with --help option causing help being shown for the both if subparser is "activated".
The only way I was able to get full (parser + subparser) help is:
common_parser = argparse.ArgumentParser(add_help=False)
common_parser.add_argument('-c', required = True)
parser = argparse.ArgumentParser(parents=[common_parser])
subparsers = parser.add_subparsers(dest="sub")
subparser = subparsers.add_parser("sub_option", parents=[common_parser])
subparser.add_argument('-o', required = False)
settings = parser.parse_args()
But then script requires the -c option to be entered twice (apparently for parser and subparser). If I don't use parents then I get normal behaviour but I don't the help I get for subparser contains only -o description (I want also -c to be shown)
P.S. Python 2.7
For a start let's distinguish between parsing behavior and help display.
I assume you have multiple subparsers (otherwise why use the subparser mechanism). -o is an argument specific to sub_option, and presumably the other subparsers have their own arguments.
What is the purpose of -c? Is it something that is common to all subparsers? Since subparsers are required, it doesn't make sense to talk about an argument that only matters to the main parser.
One way to deal with a common argument is to define it for all subparsers. The parents mechanism that you use saves you a bit of typing. Just omit it from the main parser definition. That gets rid of the problem with having to supply it twice.
If -c is defined for all the subparsers, then there isn't a need to show it in the main parser help, is there?
The whole subparser mechanism is cleanest when the subparser command is the 1st argument string, with all of its arguments, positionals and options following. It's possible to define arguments for the main parser, but it often complicates both use and help.
The issue of display the help for both the main parser and (all) the subparsers has come up before, both on SO, and on the python bug/issues. There isn't a simple solution. Some tools that may help are:
generate help under program control with parser.print_help(), and subparse.print_help().
add_subparsers() command takes parameters like prog, title and description which can be used to control the help, including the usage of the subparser help.
add_subparser() takes the same sort of parameters as ArgumentParser (since it defines a parser), description and usage may be useful.
Looking a previous question
How to show help for all subparsers in argparse?
I realized that if -c is not required, it can be defined for both the main and subparser, and appear as expected in the helps. But by making it 'required', both parsers have to see it - but only the value seen by the subparser appears in the namespace.
Also positionals defined in the main parser appear in the subparser usage.
Another subparsers help display question
argparse subparser monolithic help output
http://bugs.python.org/issue20333 argparse subparser usage message hides main parser usage discusses the question of how much of main parser usage should show up in the subparse usage line. Currently just positionals (defined before the subparsers) show up. In the patch I suggest adding required optionals as well. But you can always fudge this by defining your own prog for subparsers.

Using OptionParser vs sys.argv

For a script, I'm currently using OptionParser to add variables to an input. However, all of my current options are booleans, and it seems it would just be easier to parse using argv instead. For example:
$ script.py option1 option4 option6
And then do something like:
if 'option1' in argv:
do this
if 'option2' in argv:
do this
etc...
Would it be suggested to use argv over OptionParser when the optionals are all booleans?
"However, all of my current options are booleans, and it seems it
would just be easier to parse using argv instead."
There's nothing wrong with using argv, and if it's simpler to use argv, there's no reason not to.
OptionParser has been deprecated, and unless you're stuck on an older version of python, you should use the ArgParser module.
For one-off scripts, there's nothing wrong with parsing sys.argv yourself. There are some advantages to using an argument parsing module instead of writing your own.
Standardized. Do you allow options like "-test", because the standard is usually 2 underscores for multichar options (e.g. "--test"). With a module, you don't have to worry about defining standards because they're already defined.
Do you need error-catching and help messages? Because you get a lot of that for free with ArgParse.
Will someone else be maintaining your code? There's already lots of documentation and examples of ArgParse. Plus, it's somewhat self documenting, because you have to specify the type and number of arguments, which isn't always apparent from looking at a sys.argv parser.
Basically, if you ever expect your command line options to change over time, or expect that your code will have to be modified by someone else, the overhead of ArgParse isn't that bad and would probably save you time in the future.

Categories

Resources