Python arguments not being parsed when passed - python

I have the following code at the minute:
parser = argparse.ArgumentParser(prog='Tempus')
ex_group = parser.add_mutually_exclusive_group(required=True)
## Miscellaneous but needed args
parser.add_argument('--mode', type=str, choices=['xml', 'text', 'term'],
dest='mode', required=True, help='export mode')
parser.add_argument('-v', '--verbose', action='store_true',
dest='verbose', help='enable verbose/debug mode')
# Input methods
ex_group.add_argument('--i', action='store_true',
dest='interactive', help='enter interactive mode')
ex_group.add_argument('--p', metavar='I', type=float,
dest='integer', help='percentage to use')
args = parser.parse_args()
However when I pass the arguments that are needed, in any order, I get the error:
Tempus: error: argument --mode is required
And this happens, even when I pass that argument. Any way to sort this?
Thank you!
EDIT: Thanks everyone, I got it working, turns out executing it without first specifying the python exe before it doesn't pass arguments.
Just another quick question: is it possible to create an argument similar to --mode in my code, but let an additional argument be passed to one of the choices?
For example, have a command such as --input which can take two arguments 'integer' and 'interactive', but I can also pass a number to the --input integer command? So the command would read: --input integer 23 for example? Is this possible?

Try
1. to print sys.argv before parsing or
2. to explicitely add arguments inside the script as in http://docs.python.org/dev/library/argparse.html#parsing-arguments
So you can exclude any confusion about which arguments argparse actually sees.

Related

Argument for argparse always needed, I want to make it optional(without --)

I've done my research but still couldn't find a way to be able to run a python with a random string as an argument and without any arguments at the same time.(without --)
import argparse
parser = argparse.ArgumentParser(description="Argument list")
parser.add_argument('string', type=str, help='String for additional info')
args = parser.parse_args()
if args.string :
print("You passed an argument")
enter code here
Runs perfectly with argument - python main.py blablabla
Crashes - python main.py
main.py: error: the following arguments are required: string
I could use a key word like --string=blablabla, but I want to avoid key word part(--string=)
Is it possible to handle optional argument without using keywords?
Pass nargs='*' to accept zero or more of a positional argument
parser.add_argument('string', type=str, help='String for additional info', nargs='*')

Get arguments from ArgumentParser without calling parse_args

I have the following code and I want to use to extract the config parameter.
parser = argparse.ArgumentParser()
parser.add_argument(
"--config",
type=str,
default="src/config.yml",
dest="config"
)
My issue is that I cannot use parser.parse_args() (because I'm running the script from uvicorn and the parse_args is raising an error. Is there a way to retrieve the config parameter without the use of parse_args?
Other answers I've seen make use of parse_args.
If you want to parse an argument array that is not passed via sys.argv, for example one that you created, simply pass an array to the parse_args() function.
my_args = ["--config", "my_value"]
parsed_args = parser.parse_args(my_args)
print(parsed_args.config) # Prints "my_value"

Python 3.7 ArgumentParser.add_subparsers require positional before optionals

I'm trying to create a python script that will execute another script, depending on the first positional parameter. Think along the lines of how git add behaves.
Problem is that ArgumentParser appears to want the positional sub-command to be listed... at the end. Which is pretty counter-intuitive. (When you want to list all files, you do ls -a [FILE positional], not -a ls [FILE positional], so why would it require scriptname [optionals] subcommand instead of scriptname subcommand [optionals] since 'subcommand' is the 'real' command?)
Toy example:
def get_arg_parser():
parser = argparse.ArgumentParser()
# set up subprocessors
subparser = parser.add_subparsers(required=True)
parser.add_argument('--verbose', action='store_const', const=True, default=False, help="Enable verbose output.")
subcommand1_subparser = subparser.add_parser('subcommand1')
subcommand1_subparser.add_argument('--foo1', type=float)
subcommand2_subparser = subparser.add_parser('subcommand2')
subcommand2_subparser.add_argument('--foo2', type=float)
return parser
if __name__ == "__main__":
if len(sys.argv) > 1:
get_arg_parser().parse_args()
# more
else:
get_arg_parser().print_help()
Problem is that if I try to run python toyexample.py subcommand1 --verbose, it complains about error: unrecognized arguments: --verbose. Meanwhile, python toyexample.py --verbose subcommand1 works, but it's requiring the optionals before the name of the command you're actually intending to run.
How do I override this?
Thanks to #hpaulj, I found a solution: simply add the shared arguments to both subparsers.
I put the parser.add_argument('--verbose', action='store_const', const=True, default=False, help="Enable verbose output.") line in a add_shared_args_to_parser to function, which I then call twice, passing the subparsers.
Net result is that the subparsers have some unfortunate (but not terrible) duplication and the main parser has nothing but subparsers.

python argparse: 'Namespace' error

I'm trying to build a process with some parsing option, some mandatory and others optinal.
I have a problem with the following code:
bandlist=[1,2,3,4,5,6,7,8]
process=['rad', 'ref', 'radref']
sensors=['LC', 'LO', 'LE', 'LT']
series=['4', '5', '7', '8']
usage = "usage: %prog [options] "
parser = argparse.ArgumentParser(usage=usage)
parser.add_argument('-d', '--directory', dest='directory', action='store', type=str, \
help='complete path of landsat product folder: mydir/filename/')
parser.add_argument('-p', '--process', dest='operation', action='store', choices = process, \
help='process requested: radiance, reflectance, both', default='rad')
parser.add_argument('-l', '--series', dest='satellite', action='store', choices = series , \
help='Landsat series:4, 5, 7, 8')
parser.add_argument('-s', '--sensor', dest='sensor', action='store', choices = sensors, \
help='sensor acronymous, for example LO for Landsat OLI, or LE for Landsat ETM+, etc..', default=None)
parser.add_argument('-o', '--output', dest='output', type=str, \
help='Directory of output raster. \n \
Unless specified, output directory will be workDirectory/results/datafolder/. \n \
If specified, the output directory wil be mydirectory/results/filename/rad (and/or ref)/', default=None)
parser.add_argument('-x', action='store_true', dest='bool', help='activate iterative radiance and/or reflectance computation for all the bands', default=False)
parser.add_argument('-b', '--bands', dest='bands', choices = bandlist, type=int, \
help='bands to process', nargs='*', default=None)
(options, args) = parser.parse_args()
and there is the following error:
Traceback (most recent call last):
File "C:\Users\lbernardi\Desktop\extract\LandsatTMroutine_RadiometricCorrection_1.0.py", line 1210, in <module>
main()
File "C:\Users\lbernardi\Desktop\extract\LandsatTMroutine_RadiometricCorrection_1.0.py", line 784, in main
(options, args) = parser.parse_args()
TypeError: 'Namespace' object is not iterable
I don't understand what the error is about.
Thank for your help
parse_args doesn't return two items, it returns one.
args = parser.parse_args()
The error was issued by the interpreter while performing the (options, args) = ... assignment. The parser returned one object, a argparse.Namespace. But the assignment tries to split it into two items, e.g.
(options, args) = (args[0], args[1])
But the Namespace class definition does not implement a list or tuple like iteration. A custom Namespace class could, in theory, do so.
That's the technical detail behind the error. The practical issue is that argparse differs from optparse.
From the end of the argparse docs:
Replace (options, args) = parser.parse_args() with args = parser.parse_args() and add additional ArgumentParser.add_argument() calls for the positional arguments. Keep in mind that what was previously called options, now in argparse context is called args.
optparse processes all the flagged strings, and puts their values in an options object (I forget its exact nature). Strings that it can't process are returned as a list as the 2nd return value.
argparse expects you to define all arguments, both the flagged ones (called 'optionals') and unflagged ones (called 'positionals'). So values that in optparse would appear in the args list, appear under their own 'name/dest' in the argparse namespace.
There is another way of calling the argparse parser, parser.parse_known_args that behaves more like optparse. Strings it can't handle are returned in an extras list.
You mention that some of your arguments are required and some are not. At a first glance, your code makes everything 'optional'. That is, if you don't include the relevant flag in the commandline, that argument will get its default value.
One of your arguments uses nargs='*'. If that isn't specified the default will be None or [] (I forget the details).
You can also specify required=True parameter which makes an 'optional' required. Sorry about the confusing terminology. In that case, the parser will raise an error if you don't supply the flag in the commandline.
I didn't look much at your previous optparse question to see whether you expected an values in the args variable. In argparse usage those are 'positionals' and are required (unless their nargs makes them 'optional').
So while the simple fix is just use args = parser.parse_args(), I suspect there's more under the surface.
A style point:
parser.add_argument('-d', '--directory', dest='directory', action='store', type=str, \
help='complete path of t product folder: mydir/filename/')
can be simplified to
parser.add_argument('-d', '--directory', \
help='complete path of landsat product folder: mydir/filename/')
If not given dest is infered from the first long flag string. store is the default action value, and str is the default type.
In argparse, type is a function, one that converts the input string to something else. int and float are the most common alternatives. str works because it does nothing - returns the same string it was given. argparse actually uses an identify lambda function as the default type (e.g. lambda x:x).

Understanding argument parsing with argparse in Python

I am now starting exploring Python, and was testing how arguments can be passed to a script with "argparse".
The way I wrote a sample script was as following, where arguments passed through flags -i and -o are compulsory and flag -u is optional:
#!/usr/bin/python
import sys
import argparse
## set usage options and define arguments
usage = "usage: %prog [options]"
parser = argparse.ArgumentParser(usage)
parser.add_argument("-i", action="store", dest="input", help="input file")
parser.add_argument("-o", action="store", dest="output", help="output file")
parser.add_argument("-u", action="store_true", dest="isunfolded", default=False, help="optional flag")
args = parser.parse_args()
print len(sys.argv)
if len(sys.argv) < 2:
# parser.print_help()
print 'Incorrect number of params'
exit()
else:
print "Correct number of params: ", len(sys.argv)
Running this script:
> ./test-args.py -i a -o b
prints:
5
Correct number of params: 5
I understand the printing statement in the if conditional (5 is higher than 2), however, after reading the argparse documentation (https://docs.python.org/3/library/argparse.html) I still don't quite understand why -i and -o flags are counted as arguments. This behaviour seems to be quite different from e.g. perl Getopt::Std, which I'm more used to.
So, the question is what is the best way of parsing arguments in Python and to evaluate the presence of mandatory arguments (without using required=True)
It gives you 5 because sys.argv contains the raw input passed to python as arguments (the script name and 4 arguments).
You can see argparse as an abstraction for this, so once you use it, you can forget about sys.argv. In most cases it is better not to mix these two methods.
argparse its a nice way to handle arguments, I don't quite get why you don't want to use the required option when that's exactly the way to go. Another alternative is to parse the sys.argv yourself (regex maybe?) and drop argparse altogether.
There's a Python getopt which probably is similar to the Perl one (assuming both are modelled after the C/Unix version).
https://docs.python.org/2/library/getopt.html
In your code, sys.argv is a list of strings from the command line (as interpreted by the shell and interpreter). It is the raw input for any of the parsers ('getopt', 'optparse', 'argparse'). And it is available for your parsing as well. When learning it is a good idea to include a
print sys.argv
line. parser.parse_args() uses this list. sys.argv[0] is used as prog attribute (in the default usage), while sys.argv[1:] (the rest) is parsed according to the rules you define with add_argument. For testing I often like to use parse_args with a custom list of strings, e.g.
print parser.parse_args(['-i', 'input', '-o', 'output', '-u'])
With your definition I'd expect to see something like:
Namespace(input='input', output='output', isunfolded=True)
The parser returns an object (type argparse.Namespace), which has attributes defined by your arguments. Values are usually accessed with expressions like args.input, args.isunfolded. The docs also show how you easily express this as a dictionary.
By long standing UNIX conventions, arguments flagged by strings like '-i' are options, that is they are optional. argparse generalizes this concept by letting you specify a required=True parameter.
Other arguments are positionals. They are interpreted according to their order. And as such they are normally required. What argparse adds is the ability to define those positionals, such as type, nargs, etc. With nargs='?' they are optional. Many of the nargs values are similar to regular expression characters (e.g. +?*). In fact argparse uses a form a regular expression parsing to allocate strings among arguments.
I'd refine your arguments thus (taking advantage of various defaults)
a1 = parser.add_argument("-i", "--input", help="input file") # 'store' is the default
a2 = parser.add_argument("-o", "--output",help="output file") # use the --output as dest
a3 = parser.add_argument("-u", "--isunfolded", action="store_true", help="optional flag")
If input and output were required, I could change them to:
parser.add_argument("input", help="input file") # 'store' is the default
parser.add_argument("output",help="output file") # use the --output as dest
parser.add_argument("-u", "--isunfolded", action="store_true", help="optional flag")
Now input and output are positional arguments, as in test.py -u inputfile outputfile
By using a1 = parser... I can look at the object produced by this statement.
print a1
produces
_StoreAction(option_strings=['-i', '--input'], dest='input', nargs=None, const=None,
default=None, type=None, choices=None, help='input file', metavar=None)
This tells me that a1 is a _StoreAction object (a subclass of argparse.Action). It also displays a number (not all) of its attributes, ones that define its action. A positional, on the other hand, has values like these:
a2 = p.add_argument("output", help="output file")
_StoreAction(option_strings=[], dest='output', nargs=None, const=None,
default=None, type=None, choices=None, help='output file', metavar=None)
It may also be instructive to look at a1.required and a2.required, which are respectively False and True. required is an Action attribute that is not routinely displayed, but is, never the less accessible.
I've pulled all these test values from a parser defined in an interactive shell (Ipython). It's a great way to explore Python and modules like argparse.
After reading other related posts it seems that the best way to do this is as was suggested by #Rufflewind and inspect the args itself:
if not args.input or not args.output:
print 'Incorrect number of params'
exit()
else:
print "Correct number of params"

Categories

Resources