how to add multiple argument options in python using argparse? - python

My Requirement:
For now when I run my python application with this command
python main.py -d listhere/users.txt
The program will run and save the result file as predefined name say reports.txt
Now I want to add this functionality to allow users to choose what to put the filename and where to save as so
python main.py -d -o output/newfilname -i listhere/users.txt
Everything is same but I want another argument -o to be passed which will determine the filpath and name to be saved. How do I do it. What is the best way to handle or combine multiple options.
I tried this
parser = argparse.ArgumentParser(description = "CHECK-ACCESS REPORTING.")
parser.add_argument('--user','-d', nargs='?')
parser.add_argument('--output','-d -o', nargs='?')
parser.add_argument('--input','-i', nargs='?')
args = parser.parse_args(sys.argv[1:])
if args.output and args.input:
#operation that involves output filename too
elif args.user and not args.input:
#default operation only
else:
#notset
I am getting this error when trying to solve the issue this way
Error:
report.py: error: unrecognized arguments: -o listhere/users.txt

A nargs='?' flagged option works in 3 ways
parser.add_argument('-d', nargs='?', default='DEF', const='CONST')
commandline:
foo.py -d value # => args.d == 'value'
foo.py -d # => args.d == 'CONST'
foo.py # => args.d == 'DEF'
https://docs.python.org/3/library/argparse.html#const
Taking advantage of that, you shouldn't need anything like this erroneous -d -o flag.
If you don't use the const parameter, don't use '?'
parser.add_argument('--user','-u', nargs='?', const='CONST', default='default_user')
parser.add_argument('--output','-o', default='default_outfile')
parser.add_argument('--input','-i', default='default_infile')

Do you want to have something like this:
import argparse
def main():
parser = argparse.ArgumentParser(
description='Check-Access Reporting.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
'-d',
dest='discrepancy',
action='store_true',
help='Generate discrepancy report.',
)
parser.add_argument(
'--input',
'-i',
default='users.txt',
help='Input file for the report.',
)
parser.add_argument(
'--output',
'-o',
default='reports.txt',
help='Output file for the report.',
)
args = parser.parse_args()
if args.discrepancy:
print('Report type: {}'.format(args.report_type))
print('Input file: {}'.format(args.input))
print('Output file: {}'.format(args.output))
else:
print('Report type is not specified.')
if __name__ == '__main__':
main()
Result of option --help:
usage: ptest_047.py [-h] [-d] [--input INPUT] [--output OUTPUT]
Check-Access Reporting.
optional arguments:
-h, --help show this help message and exit
-d generate discrepancy report (default: False)
--input INPUT, -i INPUT
input file for the report (default: users.txt)
--output OUTPUT, -o OUTPUT
output file for the report (default: reports.txt)
Without any option (or missing option -d):
Report type is not specified.
With option -d:
Report type: discrepancy
Input file: users.txt
Output file: reports.txt
With -d --input input.txt --output output.txt:
Report type: discrepancy
Input file: input.txt
Output file: output.txt

Related

Python: help menu doesn't show all options when using parse_known_args()

I went through a "little" issue using python argument parser, I've created a parser like shown below:
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('-l', '--log-level', help="log level")
parser.add_argument('-o', '--out-dir', help="output directory")
args, remaining = parser.parse_known_args()
outpu_dir = args.out_dir
parser.add_argument('-f', '--log-file', help = "log file directory")
args = parser.parse_args()
and the problem is that when calling the program with --help option, arguments added after parse_known_args() are not listed in the menu, I checked other topics related to this, but my case is a bit different! could any one give a help here please ?
Any code which is below the call to parse_known_args will not execute at all.
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-a')
parser.parse_known_args()
1/0
When running with --help this generates
usage: main.py [-h] [-a A]
optional arguments:
-h, --help show this help message and exit
-a A
Process finished with exit code 0
and not a ZeroDivisionError exception.
You can work around it by adding the help option after all the other args have been added & you've parsed the known args.
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter, SUPPRESS
# don't add help right now
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter, add_help=False)
parser.add_argument('-l', '--log-level', help="log level")
parser.add_argument('-o', '--out-dir', help="output directory")
args, remaining = parser.parse_known_args()
outpu_dir = args.out_dir
parser.add_argument('-f', '--log-file', help="log file directory")
# add help later, when all other args have been added
parser.add_argument('-h', '--help', action='help', default=SUPPRESS,
help='Show this help message and exit.')
args = parser.parse_args()
Which results in:
(venv) ➜ python main.py --help
usage: main.py [-l LOG_LEVEL] [-o OUT_DIR] [-f LOG_FILE] [-h]
optional arguments:
-l LOG_LEVEL, --log-level LOG_LEVEL
log level (default: None)
-o OUT_DIR, --out-dir OUT_DIR
output directory (default: None)
-f LOG_FILE, --log-file LOG_FILE
log file directory (default: None)
-h, --help Show this help message and exit.
But it's much better if you can restructure your code to add all the argusments first before doing any parsing (i.e. avoid the call to parse_known_aergs before any other add_argument calls)

deferring exiting on --help when using parse_known_args()

I have a code that needs to build its command line arguments dynamically, based on configuration file. Schematically, what I end up doing is
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--config", type=str, default="",
help="Path to config file.")
args, unknown = parser.parse_known_args()
#here do stuff with args.config to extend parser list of arguments, and then:
parser.parse_args()
The argument management seems to work perfectly well but the trouble I have is that --help will exit at the first call parse_known_args, instead of the second parser.parse_args() which would have shown all the dynamically added arguments.... Is there a way to solve this?
I had the same problem some time ago. It can be solved by using two parsers.
A first pre-parser, with add_help=False to determine the configuration file;
The second full parser with help.
# test.py
import argparse
# First pre-parser to determine the configuration file
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("-c", "--config", default="", help="Path to config file.")
args = parser.parse_known_args()
# Do something with args.config
# Full and real parser
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--config", default="", help="Path to config file.")
parser.add_argument("-o", "--option", help="Option with default value from config file")
parser.parse_args()
This result in:
$ python3 test.py --help
usage: test.py [-h] [-c CONFIG] [-o OPTION]
optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Path to config file.
-o OPTION, --option OPTION
Option with default value from config file

collecting input files from command line in unix using argparse library

I'm trying to write a script that would take some flags and files as arguments and then execute other scripts, depend on the flag that the user chooses. For example, the command line should look like that:
main_script.py -flag1 -file_for_flag_1 another_file_for_flag_1
and
main_script.py -flag2 -file_for_flag_2
I tried to use the argparse library, but I don't know how to take the input files as arguments for the next steps and manipulate them as I want. I started with:
parser = argparse.ArgumentParser(description="Processing inputs")
parser.add_argument(
"-flat_map",
type=str,
nargs="+",
help="generates a flat addressmap from the given files",
)
parser.add_argument(
"-json_convert",
type=str,
nargs="+",
help="generates a flat addressmap from the given files",
)
args = parser.parse_args(args=["-flat_map"])
print(args)
I printed args in the end to see what I get from it but I got nothing I can work with.
Would like to have some guidance. Thanks.
You can convert the args to a dict (where the key is the arg option and the value is the arg value) if it's more convenient for you:
args_dict = {key: value for key, value in vars(parser.parse_args()).items() if value}
Using argparse you can use sub-commands to select sub-modules:
import argparse
def run_command(parser, args):
if args.command == 'command1':
# add code for command1 here
print(args)
elif args.command == 'command2':
# add code for command2 here
print(args)
parser = argparse.ArgumentParser(
prog='PROG',
epilog="See '<command> --help' to read about a specific sub-command."
)
subparsers = parser.add_subparsers(dest='command', help='Sub-commands')
A_parser = subparsers.add_parser('command1', help='Command 1')
A_parser.add_argument("--foo")
A_parser.add_argument('--bar')
A_parser.set_defaults(func=run_command)
B_parser = subparsers.add_parser('command2', help='Command 2')
B_parser.add_argument('--bar')
B_parser.add_argument('--baz')
B_parser.set_defaults(func=run_command)
args = parser.parse_args()
if args.command is not None:
args.func(parser, args)
else:
parser.print_help()
This generates a help page like so:
~ python args.py -h
usage: PROG [-h] {command1,command2} ...
positional arguments:
{command1,command2} Sub-commands
command1 Command 1
command2 Command 2
optional arguments:
-h, --help show this help message and exit
See '<command> --help' to read about a specific sub-command.
and help text for each sub-command:
~ python args.py B -h
arg.py command2 -h
usage: PROG command2 [-h] [--bar BAR] [--baz BAZ]
optional arguments:
-h, --help show this help message and exit
--bar BAR
--baz BAZ

Python argv validation

I need to check if the user is giving the input file and the name of the output, and I'm doing the following:
def main():
if len(argv) > 2:
script, file_in, file_out = argv
execute_code(file_in, file_out)
else:
print "Wrong number of arguments!"
print "Usage: python script.py filename_input filename_output"
if __name__ == '__main__':
main()
Is there other way to check if the argv arguments are correct?
You'd use argparse:
The argparse module makes it easy to write user-friendly command-line
interfaces. The program defines what arguments it requires, and
argparse will figure out how to parse those out of sys.argv.
For example your main function could be rewritten as
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('file_in', help='input file')
parser.add_argument('file_out', help='output file')
args = parser.parse_args()
execute_code(args.file_in, args.file_out)
if __name__ == '__main__':
main()
argparse will perform validation for you and display fairly helpful error messages if some of the required arguments are missing:
vaultah#base:~$ python3 /home/vaultah/untitled.py
usage: untitled.py [-h] file_in file_out
untitled.py: error: the following arguments are required: file_in, file_out
vaultah#base:~$ python3 /home/vaultah/untitled.py in
usage: untitled.py [-h] file_in file_out
untitled.py: error: the following arguments are required: file_out
Additionally, it will generate a help message
vaultah#base:~$ python3 /home/vaultah/untitled.py -h
usage: untitled.py [-h] file_in file_out
positional arguments:
file_in input file
file_out output file
optional arguments:
-h, --help show this help message and exit
While it's a little more work, you might want to consider using argparse.
You code would become:
import argparse
def execute_code(file_in, file_out):
pass
def main():
parser = argparse.ArgumentParser(description='Process some files.')
parser.add_argument('file_in', help='input file')
parser.add_argument('file_out', help='output file')
args = parser.parse_args()
execute_code(args.file_in, args.file_out)
if __name__ == '__main__':
main()
Running the program with no arguments:
python demo.py
Yields:
usage: demo.py [-h] file_in file_out
demo.py: error: the following arguments are required: file_in, file_out
Running the program with the -h flag:
python demo.py -h
Yields:
usage: demo.py [-h] file_in file_out
Process some files.
positional arguments:
file_in input file
file_out output file
optional arguments:
-h, --help show this help message and exit
Use argparse; ideally, use argparse.FileType to open files automatically from command-line arguments.
If the arguments don't make sense as open files, you can catch the exception thrown.
You can also specify the option required=True to make the arguments mandatory. This will throw an error if the required arguments are not specified. Here is an example:
parser.add_argument("--required_arg",
required=True,
help="The argument which is mandatory")
For checking the path of the input and output files, you can use the following code:
if not os.path.exists(file_name):
error_message = file_name + " does not exist \n";
You can also look at my blog Techable for more information on argument parser in python.

Pass a directory with argparse (no type checking needed)

I've written a file crawler and I'm trying to expand it. I want to use argparse to handle settings for the script including passing the starting directory in at the command line.
Example: /var/some/directory/
I have gotten several other arguments to work but I'm unable to pass this directory in correctly. I don't care if it's preceded by a flag or not, (e.g -d /path/to/start/) but I need to make sure that at the very least, this is argument is used as it will be the only mandatory option for the script to run.
Code Sample:
parser = argparse.ArgumentParser(description='py pub crawler...')
parser.add_argument('-v', '--verbose', help='verbose output from crawler', action="store_true")
parser.add_argument('-d', '--dump', help='dumps and replaces existing dictionaries', action="store_true")
parser.add_argument('-f', '--fake', help='crawl only, nothing stored to DB', action="store_true")
args = parser.parse_args()
if args.verbose:
verbose = True
if args.dump:
dump = True
if args.fake:
fake = True
Simply add:
parser.add_argument('directory',help='directory to use',action='store')
before your args = parser.parse_args() line. A simple test from the commandline shows that it does the right thing (printing args at the end of the script):
$ python test.py /foo/bar/baz
Namespace(directory='/foo/bar/baz', dump=False, fake=False, verbose=False)
$ python test.py
usage: test.py [-h] [-v] [-d] [-f] directory
test.py: error: too few arguments

Categories

Resources