Use an ArgumentParser argument as a default value of another argument [duplicate] - python

This question already has an answer here:
Python Argparse - Set default value of a parameter to another parameter
(1 answer)
Closed last year.
I would like to set an ArgumentParser argument value as the default value of another argument in the same parser.
Desired behavior:
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormater)
parser.add_argument('-p', '--pin_file_path', help = "the directory of the given pin file", default=os.getcwd())
parser.add_argument('-o', '--other', help="", default=**pin_file_path!**)

parse_known_args parse the argument and send it to another argument.
pin_file_path= None
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormater)
parser.add_argument('-p', '--pin_file_path', help = "the directory of the given pin file", default = os.getcwd())
args, remaining = parser.parse_known_args()
parser.add_argument('--option2', default=args.pin_file_path)
args = parser.parse_args()

Related

Argument's name shadows keyword in module argparse python [duplicate]

This question already has answers here:
Using a python keyword as an option in argparse
(2 answers)
Closed 14 days ago.
When I try to get argument with flag "--from" from argparse.ArgumentParser.parse_args() an error occurs. IDE says that "from" is import statement and the code is unreachable:
parser = argparse.ArgumentParser(prog='cache_wiki.py',
description='Find shortest path between links')
parser.add_argument('--from', required=True, help='page to start search with')
args = parser.parse_args()
print(args.from)
It is ok with another name:
parser = argparse.ArgumentParser(prog='cache_wiki.py',
description='Find shortest path between links')
parser.add_argument('--f', required=True, help='page to start search with')
args = parser.parse_args()
print(args.f)
but I really need to use flag "--from".
I would ignore the IDE here. True, you cannot use args.from, but that's just a syntactic limitation. You can still access the attribute using, for example, getattr(args, 'from').
You can also override the default destination name so that you can use the option --from, but set a different attribute:
...
parser.add_argument('--from',
required=True,
dest='from_',
help='page to start search with')
args = p.parse_args(['--from', 'foo'])
assert args.from_ == 'foo'

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"

Forcing an argument to be a valid path

I'm writing a simple script that will be used parse the contents of a JSON file stored on AWS CloudWatch. I've added an argument parser to the script that will accept user input, and allow the user to either print the output of the file to the screen (in a predetermined fashion), or allow them to output the contents to a local JSON file. Here's a snippet of what's stumping me:
import argparse
parser = argparse.ArgumentParser(description="Process a log file")
parser.add_argument('-o', '--output', choices=[???, 'print'],
default='print', help='Specify logfile output path or print to screen')
args = parser.parse_args()
My question stems from the parser.add_argument line, specifically the choices argument. I'd like to allow two inputs for this flag, those being either print or some valid path on their local machine. I'd like the choice that's currently marked by question marks to be a PATH that Python can recognize.
Is there a way, using argparse, to specify that one of of the arguments to a flag must be a PATH? Search results have been, so far, inconclusive.
Thanks in advance!
Use the type keyword argument to add_argument, instead of choices. As the documentation says:
The type keyword argument of add_argument() allows any necessary
type-checking and type conversions to be performed.
def myfiletype(arg):
if arg == 'print' or os.path.isdir(arg):
return arg
else:
raise ValueError('Invalid path specification')
parser.add_argument('-o', '--output', type=myfiletype,
default='print',
help='Specify logfile output path or print to screen')
Define a custom type (as in https://stackoverflow.com/a/14117511/1093967), where the value can be a valid path or 'print'. choices isn't the right choice here.

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