Python argparse default string value - python

using argparse:
parser.add_argument("-o", "--output", help="Log to file")
I want to achieve the following behavior:
The user doesn't specify the -o flag - no logging should be done.
User specifies the -o with nothing - I should log to a default location,
defined within my program.
User specifies -o and a string(path) - I should log there.
Does anyone know the best way to use add_argument to achieve that? I saw a similar example with int values, but in my case, it doesn't get my default value.

You can use nargs='?' for this:
parser.add_argument('-o', '--output',
nargs='?', default=None, const='my_default_location')
If not present, it will produce the default value, if present but without a value it'll use const, otherwise it'll use the supplied value.
Also read through the other examples in the docs, there's a sample for an optional output file which could be useful.

Related

argparse action menu with variable options

I'm trying to build a program that takes the first argument as an action (like program list, program create, program delete, etc.), and then uses the rest of the provided options in context to the action (like -c, --all, etc.).
How can I define the same optional argument several times, for each action I define in the first argument?
#hpaulj's comment helped me solve this. I've used subparsers and created separated contexts to store a config for each main action that I have in my script.
An example can be found here: https://stackoverflow.com/a/53324772/901925
You can use something like this:
parser = argparse.ArgumentParser()
parser.add_argument("action", type=str, help="action like create or delete")
parser.add_argument("-c", action="count", default=0)
in this case, the first argument given without -c will be stored in action variable.

How to create Argument Parser

I am new to argparser in python . I am trying to create argparser for file which contains two functions download and upload file on/from box. It will do only one functionality at once according according to that i am trying to create parser for that file as follows but it's not working for me:
parser = argparse.ArgumentParser(description='Download or Upload file on box.')
parser.add_argument('-df', '--download', required=True,
help='download file box')
parser.add_argument('-uf', '--upload', nargs='+', required=True,
help='upload file of box')
parser.add_argument('-fp', '--filepath', required=True,
help='file path to upload(which file to upload) or download(where to download file)')
parser.add_argument('-fn', '--filename', required=True,
help='what should be the name of file on box')
parser.add_argument('-fi', '--fileid', required=True,
help='file id of file to download from box')
args = vars(parser.parse_args())
NOTE :- every time only -df or -uf options will be there, -fp is mandatory for both and if it is -df then -fi is only option and it is mandatory and if -uf then -fn is only option and it's mandatory.
How do I achieve this, following are example how will i pass argument to file
pyhton abc.py -df -fp 'Download/boxfile/xyz.txt' -fi 123
python abc.py -uf -fp 'Download/boxfile/xyz.txt' -fn 'qwe.txt'
As written all 5 of the arguments are required - you made that explicit. If that's what you really want, all the rest of the question is irrelevant. You'll have to provide all 5 regardless.
But the comments indicate that you want to use either -df or -uf, but probably not both (though that bit's unclear).
While there is a mutually_exclusive_mechanism in argparse, there isn't a inclusive equivalent - something that says if -f is present, then -g must also be given.
But subparsers mechanism can be used that way. You could define an download subparser, with a required -fi argument. And an upload with its own argument.
Another option is to set -df to take 2 arguments (nargs=2), both its box and its file.
If -df and -uf are mutually exclusive, why not use the same argument for the name of the file? That is, replace -fn and -fi with one file argument.
Another option is to make all (or most) of the arguments not-required, and check for the correct combinations after parsing. It's easier to implement complicated logic in your own code than to force argparse to do it for you.
e.g.
if args.download is not None: # not default
<check for `args.filename`> etc
It can also be a good idea to provide defaults for optional arguments. That way the code can run even if the user doesn't provide all items.
On a matter of style. Short option flags, with on - are usually a single character. -d, -u, etc. The ability to combine several into one string only works in that case, e.g. -df foo. In this case it probably doesn't matter since none of your arguments are store_true.
I'm not incredibly familiar with argparse, however from reading the documentation I don't believe there's a way to use mutually_exclusive_group and required to force this. It seems conditional statements or equivalent are necessary to confirm that valid parameter combinations were passed.
See: Python Argparse conditionally required arguments
I'd suggest that you get rid of most of these arguments, maybe all, and think about standard Unix ways of doing things.
Consider scp, which has a syntax of: scp source destination
For instance: scp Download/boxfile/xyz.txt qwe.txt
If you don't supply a destination, it infers that you want the file to be called the same thing, and land right here, so these two things are equivalent:
scp Download/boxfile/xyz.txt xyz.txt
scp Download/boxfile/xyz.txt
Of course, scp can talk to machines across the internet, so there is a format for that:
scp hostname:Download/boxfile/xyz.txt xyz.txt
And if you are uploading, you simple switch the order:
scp xyz.txt hostname:Download/boxfile/xyz.txt

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.

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"

optparse(): Input validation

My apology in advance if it's already answered somewhere; I've been in the python site since last hr. but didn't quite figure out how I can I do this. My script should take the options like this:
myScript.py -f <file-name> -e [/ -d]
myScript.py -s <string> -e [/ -d]
myScript.py -f <file-name> [/ -s <string>] -e -w [<file_name>]
i.e. -f/-s,-e/-d are mandatory options but -f&-s cannot be used together and the same as with -e&-d options - cannot be used together. How can I put the check in place?
Another question, if I may ask at the same time: How can I use -w option (when used) with or w/o a value? When no value is supplied, it should take the default value otherwise the supplied one.
Any help greatly appreciated. Cheers!!
It's been a while since I did anything with optparse, but I took a brief look through the docs and an old program.
"-f/-s,-e/-d are mandatory options but -f&-s cannot be used together and the same as with -e&-d options - cannot be used together. How can I put the check in place?"
For mutual exclusivity, you have to do the check yourself, for example:
parser.add_option("-e", help="e desc", dest="e_opt", action="store_true")
parser.add_option("-d", help="d desc", dest="d_opt", action="store_true")
(opts, args) = parser.parse_args()
if (parser.has_option("-e") and parser.has_option("-d")):
print "Error! Found both d and e options. You can't do that!"
sys.exit(1)
Since the example options here are boolean, you could replace the if line above with:
if (opts.e_opt and opts.d_opt):
See the section How optparse handles errors for more.
"How can I use -w option (when used) with or w/o a value?"
I've never figured out a way to have an optparse option for which a value is, well, optional. AFAIK, you have to set the option up to have values or to not have values. The closest I've come is to specify a default value for an option which must have a value. Then that entry doesn't have to be specified on the command line. Sample code :
parser.add_option("-w", help="warning", dest="warn", default=0)
An aside with a (hopefully helpful) suggestion:
If you saw the docs, you did see the part about how "mandatory options" is an oxymoron, right? ;-p Humor aside, you may want to consider re-designing the interface, so that:
Required information isn't entered using an "option".
Only one argument (or group of arguments) enters data which could be mutually exclusive. In other words, instead of "-e" or "-d", have "-e on" or "-e off". If you want something like "-v" for verbose and "-q" for quiet/verbose off, you can store the values into one variable:
parser.add_option("-v", help="verbose on", dest="verbose", action="store_true")
parser.add_option("-q", help="verbose off", dest="verbose", action="store_false")
This particular example is borrowed (with slight expansion) from the section Handling boolean (flag) options. For something like this you might also want to check out the Grouping Options section; I've not used this feature, so won't say more about it.
You should try with argparse if you are using 2.7+.
This section should be what you want.
Tl;dr:
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('--foo', action='store_true')
group.add_argument('--bar', action='store_false')
makes --foo and --bar mutually exclusive. See detailed argparse usage for more informations on using ArgumentParsers
Remember that optparse is deprecated, so using argparse is a good idea anyway.

Categories

Resources