python argparse stopped working - python

I had a perfectly fine and working piece of code, which used argparse. I've been using it for months for work without any issue. Below is an excrept.
import argparse
import sys
import math
import random
# Setup command line arguments
parser = argparse.ArgumentParser(description='RF Profile Generator', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-o', dest='OutputFile', help='Output filename', required=True, type=argparse.FileType('w', encoding='utf-8'))
parser.add_argument('-a', dest='APPositions', nargs='+', type=float, help='Trackside AP position(s)', required=True)
parser.add_argument('-l', dest='TotalTrackLength', type=float, help='Length of the track (m)', required=True)
parser.add_argument('-p', dest='AttenuatorAddr', nargs='+', help='Programmable attenuator IP address(es)', required=True)
input("A")
# parse the command line arguments
# the parsed values will be stored in the corresponding variables defined by 'dest'
args = parser.parse_args()
input("B")
#More code to follow
However, I ran the code today with the following arguments (which i have been doing all along):
rf.py -o OutputFile -a 10 20 30 40 -l 600 -p 10.0.1.55
and the console output tells me
"error: the following arguments are required: -o, -a, -l, -p"
I do not understand why this has stopped working. To troubleshoot, i added 2 input commands, but the code never reaches input("B")
Could someone please advise me on a possible reason for this to happen. The version of python is 3.4.1, and has not been updated between the last time i ran this code and now.
Thank you very much

Since required=True is present on all the calls to add_argument, my conclusion is that either
somebody else has changed your program, or
this is the first time you have tried to run it without providing those arguments
I do, however, find it odd that running your program with the --help option gives the following output:
usage: so16.py [-h] -o OUTPUTFILE -a APPOSITIONS [APPOSITIONS ...] -l
TOTALTRACKLENGTH -p ATTENUATORADDR [ATTENUATORADDR ...]
RF Profile Generator
optional arguments:
-h, --help show this help message and exit
-o OUTPUTFILE Output filename (default: None)
-a APPOSITIONS [APPOSITIONS ...]
Trackside AP position(s) (default: None)
-l TOTALTRACKLENGTH Length of the track (m) (default: None)
-p ATTENUATORADDR [ATTENUATORADDR ...]
Programmable attenuator IP address(es) (default: None)
I'm puzzled as to why the message implies that required arguments are optional.

Related

Require at least one argument from a list of arguments [duplicate]

I've been using argparse for a Python program that can -process, -upload or both:
parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload', action='store_true')
args = parser.parse_args()
The program is meaningless without at least one parameter. How can I configure argparse to force at least one parameter to be chosen?
UPDATE:
Following the comments: What's the Pythonic way to parametrize a program with at least one option?
if not (args.process or args.upload):
parser.error('No action requested, add -process or -upload')
args = vars(parser.parse_args())
if not any(args.values()):
parser.error('No arguments provided.')
I know this is old as dirt, but the way to require one option but forbid more than one (XOR) is like this:
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-process', action='store_true')
group.add_argument('-upload', action='store_true')
args = parser.parse_args()
print args
Output:
>opt.py
usage: multiplot.py [-h] (-process | -upload)
multiplot.py: error: one of the arguments -process -upload is required
>opt.py -upload
Namespace(process=False, upload=True)
>opt.py -process
Namespace(process=True, upload=False)
>opt.py -upload -process
usage: multiplot.py [-h] (-process | -upload)
multiplot.py: error: argument -process: not allowed with argument -upload
If not the 'or both' part (I have initially missed this) you could use something like this:
parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('--process', action='store_const', const='process', dest='mode')
parser.add_argument('--upload', action='store_const', const='upload', dest='mode')
args = parser.parse_args()
if not args.mode:
parser.error("One of --process or --upload must be given")
Though, probably it would be a better idea to use subcommands instead.
Requirements Review
use argparse (I will ignore this one)
allow one or two actions to be called (at least one required).
try to by Pythonic (I would rather call it "POSIX"-like)
There are also some implicit requirements when living on command line:
explain the usage to the user in a way which is easy to understand
options shall be optional
allow specifying flags and options
allow combining with other parameters (like file name or names).
Sample solution using docopt (file managelog.py):
"""Manage logfiles
Usage:
managelog.py [options] process -- <logfile>...
managelog.py [options] upload -- <logfile>...
managelog.py [options] process upload -- <logfile>...
managelog.py -h
Options:
-V, --verbose Be verbose
-U, --user <user> Username
-P, --pswd <pswd> Password
Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
from docopt import docopt
args = docopt(__doc__)
print args
Try to run it:
$ python managelog.py
Usage:
managelog.py [options] process -- <logfile>...
managelog.py [options] upload -- <logfile>...
managelog.py [options] process upload -- <logfile>...
managelog.py -h
Show the help:
$ python managelog.py -h
Manage logfiles
Usage:
managelog.py [options] process -- <logfile>...
managelog.py [options] upload -- <logfile>...
managelog.py [options] process upload -- <logfile>...
managelog.py -h
Options:
-V, --verbose Be verbose
-U, --user <user> Username
-P, --pswd <pswd> P managelog.py [options] upload -- <logfile>...
Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
And use it:
$ python managelog.py -V -U user -P secret upload -- alfa.log beta.log
{'--': True,
'--pswd': 'secret',
'--user': 'user',
'--verbose': True,
'-h': False,
'<logfile>': ['alfa.log', 'beta.log'],
'process': False,
'upload': True}
Short alternative short.py
There can be even shorter variant:
"""Manage logfiles
Usage:
short.py [options] (process|upload)... -- <logfile>...
short.py -h
Options:
-V, --verbose Be verbose
-U, --user <user> Username
-P, --pswd <pswd> Password
Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
from docopt import docopt
args = docopt(__doc__)
print args
Usage looks like this:
$ python short.py -V process upload -- alfa.log beta.log
{'--': True,
'--pswd': None,
'--user': None,
'--verbose': True,
'-h': False,
'<logfile>': ['alfa.log', 'beta.log'],
'process': 1,
'upload': 1}
Note, that instead of boolean values for "process" and "upload" keys there are counters.
It turns out, we cannot prevent duplication of these words:
$ python short.py -V process process upload -- alfa.log beta.log
{'--': True,
'--pswd': None,
'--user': None,
'--verbose': True,
'-h': False,
'<logfile>': ['alfa.log', 'beta.log'],
'process': 2,
'upload': 1}
Conclusions
Designing good command line interface can be challenging sometime.
There are multiple aspects of command line based program:
good design of command line
selecting/using proper parser
argparse offers a lot, but restricts possible scenarios and can become very complex.
With docopt things go much shorter while preserving readability and offering high degree of flexibility. If you manage getting parsed arguments from dictionary and do some of conversions (to integer, opening files..) manually (or by other library called schema), you may find docopt good fit for command line parsing.
For http://bugs.python.org/issue11588 I am exploring ways of generalizing the mutually_exclusive_group concept to handle cases like this.
With this development argparse.py, https://github.com/hpaulj/argparse_issues/blob/nested/argparse.py
I am able to write:
parser = argparse.ArgumentParser(prog='PROG',
description='Log archiver arguments.')
group = parser.add_usage_group(kind='any', required=True,
title='possible actions (at least one is required)')
group.add_argument('-p', '--process', action='store_true')
group.add_argument('-u', '--upload', action='store_true')
args = parser.parse_args()
print(args)
which produces the following help:
usage: PROG [-h] (-p | -u)
Log archiver arguments.
optional arguments:
-h, --help show this help message and exit
possible actions (at least one is required):
-p, --process
-u, --upload
This accepts inputs like '-u', '-up', '--proc --up' etc.
It ends up running a test similar to https://stackoverflow.com/a/6723066/901925, though the error message needs to be clearer:
usage: PROG [-h] (-p | -u)
PROG: error: some of the arguments process upload is required
I wonder:
are the parameters kind='any', required=True clear enough (accept any of the group; at least one is required)?
is usage (-p | -u) clear? A required mutually_exclusive_group produces the same thing. Is there some alternative notation?
is using a group like this more intuitive than phihag's simple test?
The best way to do this is by using python inbuilt module add_mutually_exclusive_group.
parser = argparse.ArgumentParser(description='Log archiver arguments.')
group = parser.add_mutually_exclusive_group()
group.add_argument('-process', action='store_true')
group.add_argument('-upload', action='store_true')
args = parser.parse_args()
If you want only one argument to be selected by command line just use required=True as an argument for group
group = parser.add_mutually_exclusive_group(required=True)
If you require a python program to run with at least one parameter, add an argument that doesn't have the option prefix (- or -- by default) and set nargs=+ (Minimum of one argument required). The problem with this method I found is that if you do not specify the argument, argparse will generate a "too few arguments" error and not print out the help menu. If you don't need that functionality, here's how to do it in code:
import argparse
parser = argparse.ArgumentParser(description='Your program description')
parser.add_argument('command', nargs="+", help='describe what a command is')
args = parser.parse_args()
I think that when you add an argument with the option prefixes, nargs governs the entire argument parser and not just the option. (What I mean is, if you have an --option flag with nargs="+", then --option flag expects at least one argument. If you have option with nargs="+", it expects at least one argument overall.)
This achieves the purpose and this will also be relfected in the argparse autogenerated --help output, which is imho what most sane programmers want (also works with optional arguments):
parser.add_argument(
'commands',
nargs='+', # require at least 1
choices=['process', 'upload'], # restrict the choice
help='commands to execute'
)
Official docs on this:
https://docs.python.org/3/library/argparse.html#choices
Maybe use sub-parsers?
import argparse
parser = argparse.ArgumentParser(description='Log archiver arguments.')
subparsers = parser.add_subparsers(dest='subparser_name', help='sub-command help')
parser_process = subparsers.add_parser('process', help='Process logs')
parser_upload = subparsers.add_parser('upload', help='Upload logs')
args = parser.parse_args()
print("Subparser: ", args.subparser_name)
Now --help shows:
$ python /tmp/aaa.py --help
usage: aaa.py [-h] {process,upload} ...
Log archiver arguments.
positional arguments:
{process,upload} sub-command help
process Process logs
upload Upload logs
optional arguments:
-h, --help show this help message and exit
$ python /tmp/aaa.py
usage: aaa.py [-h] {process,upload} ...
aaa.py: error: too few arguments
$ python3 /tmp/aaa.py upload
Subparser: upload
You can add additional options to these sub-parsers as well. Also instead of using that dest='subparser_name' you can also bind functions to be directly called on given sub-command (see docs).
For cases like
parser.add_argument("--a")
parser.add_argument("--b")
We can use the following
if not args.a and not args.b:
parser.error("One of --a or --b must be present")
Use append_const to a list of actions and then check that the list is populated:
parser.add_argument('-process', dest=actions, const="process", action='append_const')
parser.add_argument('-upload', dest=actions, const="upload", action='append_const')
args = parser.parse_args()
if(args.actions == None):
parser.error('Error: No actions requested')
You can even specify the methods directly within the constants.
def upload:
...
parser.add_argument('-upload', dest=actions, const=upload, action='append_const')
args = parser.parse_args()
if(args.actions == None):
parser.error('Error: No actions requested')
else:
for action in args.actions:
action()
Using
parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload', action='store_true')
args = parser.parse_args()
Maybe try:
if len([False for arg in vars(args) if vars(args)[arg]]) == 0:
parsers.print_help()
exit(-1)
At least this is what I just used; hopefully this helps someone in the future!

Python argparse: Store parameters not matching any subparser

I am using argparse library to parse arguments to my python script. This is my code:
parser = argparse.ArgumentParser(
prog="confgit",
description="Git overhead for version control of your config files",
formatter_class=argparse.RawTextHelpFormatter, )
parser.add_argument(
"-c", "--config",
type=str,
default=DEFAULT_CONFIG_PATH,
dest="CONFIG_PATH",
help="load alternative config")
subparsers = parser.add_subparsers(help="Commands:")
subparsers.add_parser("include", help="Include file or directory in to repository").add_argument(
"file_to_include",
type=str,
action="store",
nargs="?",
const="",
default=False,
help="include file or directory in to repository")
subparsers.add_parser("exclude", help="Exclude file or directory in to repository").add_argument(
"exclude",
type=str,
action="store",
help="exclude file or directory from repository")
print(parser.parse_args())
I would like to be able to store parameters not matching any subparser as a string. For example
running myprogram include test.txt --config .config/cfg.txt will result in:
Namespace(CONFIG_PATH='.config/cfg.txt', file_to_include='test.txt')
and running myprogram some text here will result in:
Namespace(CONFIG_PATH='.config/default.txt', input="some other text")
How can I achieve this ?
Thank you for help
The helps from your code:
1940:~/mypy$ python3 stack65119253.py -h
usage: confgit [-h] [-c CONFIG_PATH] {include,exclude} ...
Git overhead for version control of your config files
positional arguments:
{include,exclude} Commands:
include Include file or directory in to repository
exclude Exclude file or directory in to repository
optional arguments:
-h, --help show this help message and exit
-c CONFIG_PATH, --config CONFIG_PATH
load alternative config
So you can provide a optional '-c' with value.
The subparsers argument is a positional with 2 choices. It isn't required, but if you do provide a string it will be tested against those strings.
1941:~/mypy$ python3 stack65119253.py include -h
usage: confgit include [-h] [file_to_include]
positional arguments:
file_to_include include file or directory in to repository
optional arguments:
-h, --help show this help message and exit
1941:~/mypy$ python3 stack65119253.py exclude -h
usage: confgit exclude [-h] exclude
positional arguments:
exclude exclude file or directory from repository
optional arguments:
-h, --help show this help message and exit
For example:
1946:~/mypy$ python3 stack65119253.py -c foobar
Namespace(CONFIG_PATH='foobar')
1946:~/mypy$ python3 stack65119253.py -c foobar test
usage: confgit [-h] [-c CONFIG_PATH] {include,exclude} ...
confgit: error: invalid choice: 'test' (choose from 'include', 'exclude')
1947:~/mypy$ python3 stack65119253.py -c foobar include
Namespace(CONFIG_PATH='foobar', file_to_include=False)
argparse assigns strings to positionals by position. It does not assign by value. That is, it does not test for some value, and based on that decide whether it qualifies. The choices testing comes after assignment. Use optionals if you want to assign by value.
parser.add_argument('--include', nargs='?')
parser.add_argument('--exclude')
parse+known_args is a way of dealing with unrecognized arguments, but it doesn't get around the invalid choices error.
If the user doesn't quote the string "some other text" you will simply have to treat it as 3 different arguments ["some", "other", "text"]. But to handle it as closely as what you seem to desire you simply need to use the nargs option on an argument called input. The argparse page has an example at the very top with the "number accumulator".
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')
Use * for 0 or more arguments instead of + and replace integers with whatever argument name you want.
Upon further investigation what I wrote above won't work when you have subparsers. I would suggest making your include and exclude subcommands into options. Wouldn't it be sensible to want to do both anyway? In your current configuration you could only either include or exclude.

myscript.py: error: unrecognized arguments

I seem to be having an issue with a program I am working on in python. I have tried using sys.argv, getopt and now argparse and I am still having the same issues. Whenever I try to call myscript.py from the command line with any working arguments, the program appears to start up then it just exits the code and prints the following in the command line:
Loading configurations... <---- indicates program is starting
usage: myprogram.py [-h] [-c string] [-v]
miner.py: error: unrecognized arguments: -i 0 -d 0
myprogram.py snippet
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i','--interval', action="store", type=int, dest='interval',help='Amount of time between each switch in seconds.')
parser.add_argument('-d','--donate', action="store", type=int,dest='donate',help='Amount of time to donate your mining rig in percentage per hour.')
results = parser.parse_args()
class globalbavars:
if len(sys.argv) == 1:
interval = 8
donate = 1
interval = results.interval
donate = results.donate
I usually almost always figure out issues with my program on my own through fierce debugging but this one legitimately has me scratching my head. I can't figure out what is causing this issue. I don't even think that it's an issue with my code considering I've tried parsing with sys.argv, get opt and argparse and got the same results. Can someone help me out?

python argparse: print epilog only when verbose

I define a parser with a description, options, and an epilog. When I run the app with --help, it outputs help with the epilog as expected. However, I only want to see the epilog if --help is accompanied with --verbose. What is the proper way to achieve this with argparse?
# example code in file test
import argparse
parser = argparse.ArgumentParser( description='description', epilog='epilog' )
parser.add_argument('-v', '--verbose', action='store_true', help='verbose help')
parser.parse_args()
When I run test as follows
$ python test -h
it yields
usage: test [-h] [-v]
description
optional arguments:
-h, --help show this help message and exit
-v, --verbose verbose help
epilog
However, what I want to see is
usage: test [-h] [-v]
description
optional arguments:
-h, --help show this help message and exit
-v, --verbose verbose help
with the epilog shown only when I run
$ python test -h -v
Ick. The only way I know of doing this is by writing the help output by yourself:
import argparse
parser = argparse.ArgumentParser(
description='description',
add_help=False )
parser.add_argument(
'-h', '--help',
action=store_true,
dest='show_help')
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='verbose help')
args = parser.parse_args()
if args.show_help:
if args.verbose:
print '%s\n%s' % (parser.format_help(), 'epilog')
else
parser.print_help()
sys.exit(0)
There's no provision in argparse for this. So you will have to write your own code to change the epilog before parsing, or perform your own help after parsing, or conceivably modifying the format_help method.
You can view and change the epilog attribute of the parser after creation.
parser = argparse.ArgumentParser(epilog='test')
print parser.epilog # should see 'test'
parser.epilog = None # or ''
One deleted answer suggested looking at sys.argv before parsing, and if the --verbose is present, modify the the epilog attribute. That may miss some ways of specifying the value (e.g. -hv), but it is relatively simple.
Acting on the --verbose during parsing is difficult. The parser will act on the -h as soon as it parses it, displaying the message and exiting. Thus any -v after -h will be missed.
Doing your own help after parsing is a viable option, if you turn off the regular help (thus preventing that print and exit action). You will know the final values of both help and verbose. But you will be responsible for your own exit.
Using the ideas suggested, here's what I came up with:
import argparse
parser = argparse.ArgumentParser( description='description', epilog='', add_help=False )
parser.add_argument('-h', '--help', action='store_true', help='show help')
parser.add_argument('-v', '--verbose', action='store_true', help='more help')
args = parser.parse_args()
if args.help:
if args.verbose:
parser.epilog += "epilog for %(prog)s"
else:
parser.epilog += "\nfor more help run '%(prog)s -h -v'"
parser.print_help()
parser.exit(0)
print 'the end'
The only difficulty I found with this approach is that it is no longer possible to add required options or positional arguments. A workaround for positional arguments is to use nargs='?' and do the checking manually.
I would suggest a different approach.
1 Build the parser as you have done right now.
do a pretty print on the parser and figure out how epilog is stored in an option. Or put a debug via pdb.set_trace() and use dirs and vars to look around.
i.e. figure out what the option data structure looks like with an epilog and without an epilog.
2 Instead of calling parser.parse_args() (standard use):
look at sys.argv yourself. If -h and -v leave the parser as is.
if -h but not -v, adjust your parser before calling it to look as if it had no epilog.
3 call with parser.parse_args()
You could even build 2 parsers, one with epilog, one without and dynamically decide which one to call depending on -v flag.
p.s. actually, you want to check
if "-h" in sys.argv and not "-v" in sys.argv
I also see the value of verbose help to add examples.
From Python 2.7 argparse printing help and Python 2.7 argument parser objects and comments above, I settled upon the following method:
import argparse
. . .
if __name__ == '__main__':
description_text = """
DESCRIPTION
This command ...
"""
epilog_text = """
After execution, the user can ...
"""
example_text = """
EXAMPLES
The following examples ...
"""
parser = argparse.ArgumentParser(
description=description_text,
epilog=epilog_text,
formatter_class=argparse.RawDescriptionHelpFormatter,
add_help=False)
parser.add_argument('-h', '--help', dest='help', action='store_true',
help='Show help and exit; see also --verbose')
parser.add_argument('--usage', dest='usage', action='store_true',
help='Show usage and exit')
. . .
parser.add_argument('-v', '--verbose', dest='verbose',
action='store_true',
help='Display additional help or logging')
arguments = parser.parse_args()
if arguments.usage:
print(parser.format_usage())
sys.exit(0)
if arguments.help:
help_string = parser.format_help()
if arguments.verbose:
help_string += example_text
print(help_string)
sys.exit(0)
. . .
The result is a flexible output which supports --help, --help --verbose and --usage controls for the command. Thanks to others above for the inspiration.

parsing argument to python script

I want to create a script like this
./myscript.py -g parser -a parserapp
and whenever the script is missing one option such as ./myscript.py -g parser, it will print out usage and exit the script
The script should be: ./myscript.py -g parser -a parserapp
So, my question is how can I check if option -a or-g is missing; so, it can print out the usage for the script and exit.
Thank you for your help
Use argparse
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-g", "--gggg", help="g parameter",required=True)
parser.add_argument("-a", "--aaaa", help="a parameter",required=True)
args = parser.parse_args()
print args.g, args.a
When you run it with -h it will show help:
~#:python myscript.py -h
usage: myscript.py [-h] -g GGGG -a AAAA
optional arguments:
-h, --help show this help message and exit
-g GGGG, --gggg GGGG g parameter
-a AAAA, --aaaa AAAA a parameter
If you miss a parameter it will print error:
myscript.py: error: argument -g/--gggg is required
For your need argparse can check wether an argument is missing or not like this :
import argparse
parser = argparse.ArgumentParser(description='My Super Script')
parser.add_argument(
'--parser', '-p',
required=True,
help='Parser Type'
)
parser.add_argument(
'--application', '-a',
required=True,
help='Application Name'
)
args = parser.parse_args()
By default argparse will consider a named argument optional unless you specify required=True
If the two args are not provided this will print a default help.
ref
Parsing of parameters can be done with optparse or argparse. You can make nifty help options and so on. It's also easy to make different kinds of parameters to be accepted.
Use argparse: https://docs.python.org/2/howto/argparse.html
You can use argparse module.
For Python3.x, you can look here.
For Python2.x, you can look here.
Apart from argparse, you can also use getopt module.
Use getopt:
https://docs.python.org/3.1/library/getopt.html(Python3.x)
https://docs.python.org/2/library/getopt.html(Python2.x)

Categories

Resources