Wanted to customize the python argparse module generated errors - python-2.7

import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-f", "--filepath", help="Input the file path")
group.add_argument("-d", "--dirpath", help="Input the directory path")
For Example if I use both options -f and -d,I'm getting the following error,which is generated by python:
error: argument -d/--dirpath: expected one argument
But I want to customise the error and it should be defined by me.

2140:~/mypy$ python3 stack50552334.py
Namespace(dirpath=None, filepath=None)
These are all produced when there's a mismatch between the Action nargs and available strings. While message format is the same, it is customized to the argument's name:
2140:~/mypy$ python3 stack50552334.py -f
usage: stack50552334.py [-h] [-f FILEPATH | -d DIRPATH]
stack50552334.py: error: argument -f/--filepath: expected one argument
2140:~/mypy$ python3 stack50552334.py -d
usage: stack50552334.py [-h] [-f FILEPATH | -d DIRPATH]
stack50552334.py: error: argument -d/--dirpath: expected one argument
2140:~/mypy$ python3 stack50552334.py -f -d
usage: stack50552334.py [-h] [-f FILEPATH | -d DIRPATH]
stack50552334.py: error: argument -f/--filepath: expected one argument
2141:~/mypy$ python3 stack50552334.py -f foo -d
usage: stack50552334.py [-h] [-f FILEPATH | -d DIRPATH]
stack50552334.py: error: argument -d/--dirpath: expected one argument
But when both flags are provided with the expected arguments, then the mutually_exclusive_group test comes into play, and raises its own error.
2141:~/mypy$ python3 stack50552334.py -f foo -d dir
usage: stack50552334.py [-h] [-f FILEPATH | -d DIRPATH]
stack50552334.py: error: argument -d/--dirpath: not allowed with argument -f/--filepath
The first class of message is produced in:
def _match_argument(self, action, arg_strings_pattern):
# match the pattern for this action to the arg strings
nargs_pattern = self._get_nargs_pattern(action)
match = _re.match(nargs_pattern, arg_strings_pattern)
# raise an exception if we weren't able to find a match
if match is None:
nargs_errors = {
None: _('expected one argument'),
OPTIONAL: _('expected at most one argument'),
ONE_OR_MORE: _('expected at least one argument'),
}
default = ngettext('expected %s argument',
'expected %s arguments',
action.nargs) % action.nargs
msg = nargs_errors.get(action.nargs, default)
raise ArgumentError(action, msg)
# return the number of arguments matched
return len(match.group(1))
The _ is alias for gettext.gettext. So there is a possibility of customizing the messages with this mechanism. https://docs.python.org/3/library/gettext.html
from gettext import gettext as _, ngettext
I don't have any experience with this multlingual localization.
The mutually_exclusive error is raised in:
def take_action(action, argument_strings, option_string=None):
...
# error if this argument is not allowed with other previously
# seen arguments, assuming that actions that use the default
# value don't really count as "present"
if argument_values is not action.default:
seen_non_default_actions.add(action)
for conflict_action in action_conflicts.get(action, []):
if conflict_action in seen_non_default_actions:
msg = _('not allowed with argument %s')
action_name = _get_action_name(conflict_action)
raise ArgumentError(action, msg % action_name)
Again, there is the possibility of gettext customization.
custom argparse.py
Of course there's nothing preventing you from modifying a copy of argparse.py file, and placing in your own directory so it is loaded instead of the default one.
post parsing testing
If the wording of the mutually_exclusive error is bothering you (as opposed to the expected one argument errors, you could omit that group from the parser, and do your own tests after parsing. If the user provides non-default values for both args.filepath and args.dirpath call parser.error('your own message'), or raise your own error.

Related

Python 2.7 argsparse with a path as args

I want to give my program a path to a file but it doesnot like it if there is a / included. How can I convince python that my path is only 1 argument
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser(description='Change the option prefix characters',prefix_chars='-+/',)
parser.add_argument('-f', action='store', dest='PathtoFile',help='PathtoFile')
print parser.parse_args()
it works without /
argsparse.py -f banana -> Namespace(PathtoFile='banana')
Any of those result in expect one argument
argsparse.py -f /home/user/banana
argsparse.py -f '/home/user/banana'
argsparse.py -f '//home//user//banana'
argsparse.py -f "/home/user/banana"
argsparse.py -f "//home//user//banana"
->
usage: argsparse.py [-h] [-f PATHTOFILE]
argsparse.py: error: argument -f: expected one argument
UPDATE: Thanks match I forgot I added / as a prefix
Change
parser = argparse.ArgumentParser(description='Change the option prefix characters',prefix_chars='-+/',)
to
parser = argparse.ArgumentParser(description='Change the option prefix characters',prefix_chars='-+',)
The problem is that you're using prefix_chars='-+/' - this tells argparse that those characters are what mark out the command flags.
So your code is seeing / as equivalent to - and reading it as if you'd typed:
argsparse.py -f -home/user/banana
This means that -f has not been given an argument. Remove the prefix_chars and it should work properly.

Required argument for compiled program?

Is there any way to do required argument for crystal program?
For example
./myprog ~/Music -r
Instead of
./myprog -d ~/Music -r
So my program wont run if there's no [directory] argument. Right now using "option_parser" and can only do -arguments.
There is no way to create required arguments using option_parser, but you can parse arguments and throw an error or exit if there is no argument passed you expect:
require "option_parser"
directory = nil
parser = OptionParser.new
parser.on("-d DIR", "Directory [required]") do |d|
directory = d
end
parser.parse ARGV
if directory.nil?
# directory argument was not set
# print help and exit
puts parser
exit 1
else
# ...
end

Python argparse - adding argument depending on existence of other argument [duplicate]

I'd like to have a program that takes a --action= flag, where the valid choices are dump and upload, with upload being the default. If (and only if) dump is selected, I'd like there to also be a --dump-format= option. Is there a way to express this using argparse, or do I need to just accept all the arguments and do the logic myself.
The argparse module offers a way to do this without implementing your own
requiredness checks. The example below uses "subparsers" or "sub commands".
I've implemented a subparser for "dump" and one for "format".
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', help='The file you want to act on.')
subparsers = parser.add_subparsers(dest='subcommand')
subparsers.required = True # required since 3.7
# subparser for dump
parser_dump = subparsers.add_parser('dump')
# add a required argument
parser_dump.add_argument(
'format',
choices=['csv', 'json'],
help='Dump the file in this format.')
# subparser for upload
parser_upload = subparsers.add_parser('upload')
# add a required argument
parser_upload.add_argument(
'server',
choices=['amazon', 'imgur'],
help='Upload the file to this service.')
args = parser.parse_args()
print args
if args.subcommand == 'dump':
print 'I will now dump "%s" in the %s format' % (args.file, args.format)
if args.subcommand == 'upload':
print 'I will now upload "%s" to %s' % (args.file, args.server)
That looks like this on the command line:
$ python ap.py
usage: ap.py [-h] file {upload,dump} ...
ap.py: error: too few arguments
$ python ap.py tmp.txt
usage: ap.py [-h] file {upload,dump} ...
ap.py: error: too few arguments
Upload:
$ python ap.py tmp.txt upload
usage: ap.py file upload [-h] {amazon,imgur}
ap.py file upload: error: too few arguments
$ python ap.py tmp.txt upload amazo
usage: ap.py file upload [-h] {amazon,imgur}
ap.py file upload: error: argument server: invalid choice: 'amazo' (choose from 'amazon', 'imgur')
$ python ap.py tmp.txt upload amazon
Namespace(file='tmp.txt', server='amazon', subcommand='upload')
I will now upload "tmp.txt" to amazon
$ python ap.py tmp.txt upload imgur
Namespace(file='tmp.txt', server='imgur', subcommand='upload')
I will now upload "tmp.txt" to imgur
Dump:
$ python ap.py tmp.txt dump
usage: ap.py file dump [-h] {csv,json}
ap.py file dump: error: too few arguments
$ python ap.py tmp.txt dump csv
Namespace(file='tmp.txt', format='csv', subcommand='dump')
I will now dump "tmp.txt" in the csv format
$ python ap.py tmp.txt dump json
Namespace(file='tmp.txt', format='json', subcommand='dump')
I will now dump "tmp.txt" in the json format
More info: ArgumentParser.add_subparsers()
Another way to approach the problem is by using subcommands (a'la git) with "action" as the first argument:
script dump --dump-format="foo"
script upload
You could use parser.error:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--action', choices=['upload', 'dump'], default='dump')
parser.add_argument('--dump-format')
args = parser.parse_args()
if args.action != 'dump' and args.dump_format:
parser.error('--dump-format can only be set when --action=dump.')
It depends what you consider "doing all the logic yourself". You can still use argparse and add the dump option as follows with minimal effort without resorting to sub-commands:
from argparse import ArgumentParser
from sys import argv
parser = ArgumentParser()
action_choices = ['upload', 'dump']
parser.add_argument('--action', choices=action_choices, default=action_choices[1])
parser.add_argument('--dump-format', required=(action_choices[1] in argv))
This way argparse won't care about the dump format if the dump action wasn't selected
Try this.
Suppose you have a tool that lists, adds and deletes records in a table with the following structure:
id
sitekey
response
status
1
123456
valid
a
2
234567
invalid
3
345678
invalid
c
4
456789
valid
d
And you want to have the following operations and arguments:
list
from: optional
to: optional
short-response: optional
add
sitekey: required
response: required
status: optional
remove
id: required
Then, you can have a code similar to the following:
import argparse
import sys
operation_choices = ['list', 'add', 'remove']
parser = argparse.ArgumentParser()
parser.add_argument("--operation",
choices = operation_choices,
default = operation_choices[0],
help = "Your help!",
required = True)
# list operation subarguments
if True in list(map(lambda x: operation_choices[0] in x, sys.argv)):
parser.add_argument("--from",
type = int,
default = 1,
help = "Your help!",
required = False)
parser.add_argument("--to",
type = int,
help = "Your help!",
required = False)
parser.add_argument("--short-response",
type = bool,
default = True,
help = "Your help!",
required = False)
# add operation subarguments
if True in list(map(lambda x: operation_choices[1] in x, sys.argv)):
parser.add_argument("--sitekey",
type = str,
help = "Your help!",
required = True)
parser.add_argument("--response",
type = str,
help = "Your help!",
required = True)
parser.add_argument("--status",
type = str,
help = "Your help!",
required = False)
# remove operation subarguments
if True in list(map(lambda x: operation_choices[2] in x, sys.argv)):
parser.add_argument("--id",
type = int,
help = "Your help!",
required = True)
args = parser.parse_args()
# Your operations...
So when you run:
$ python tool.py --operation=list
This run, no required arguments
$ python tool.py --operation=add
usage: tool.py [-h] --operation {list,add,remove} --sitekey SITEKEY --response RESPONSE [--status STATUS]
tool.py: error: the following arguments are required: --sitekey, --response
$ python tool.py --operation=remove
usage: tool.py [-h] --operation {list,add,remove} --id ID
tool.py: error: the following arguments are required: --id
$ python tool.py --help
usage: tool.py [-h] --operation {list,add,remove}
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
$ python tool.py --operation=list --help
usage: tool.py [-h] --operation {list,add,remove} [--from FROM] [--to TO] [--short-response SHORT_RESPONSE]
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
--from FROM Your help!
--to TO Your help!
--short-response SHORT_RESPONSE
Your help!
$ python tool.py --operation=add --help
usage: tool.py [-h] --operation {list,add,remove} --sitekey SITEKEY --response RESPONSE [--status STATUS]
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
--sitekey SITEKEY Your help!
--response RESPONSE Your help!
--status STATUS Your help!
$ python tool.py --operation=remove --help
usage: tool.py [-h] --operation {list,add,remove} --id ID
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
--id ID Your help!

How do I specify another procmail script other than .procmailrc?

I am trying to set up two different environments (development and test) on a single machine where I am using fetchmail/procmail. How would I specify a procmail script other than .procmailrc?
You can optionally specify an alternate rcfile on the command line (or even multiple files).
$ procmail -h 2>&1 | head -n 4
Usage: procmail [-vptoY] [-f fromwhom] [parameter=value | rcfile] ...
Or: procmail [-toY] [-f fromwhom] [-a argument] ... -d recipient ...
Or: procmail [-ptY] [-f fromwhom] -m [parameter=value] ... rcfile [arg] ...
Or: procmail [-toY] [-a argument] ... -z
See the ARGUMENTS section of the manual page for precise details.
With the -m option, the rcfile argument is mandatory. It might be a good idea to use in your scenario, as it disables some defaults which make more sense when running as your default LDA.

How do I make command line arguments with a hyphen (-) show up as non-optional in python v2.7?

I have the following python script
parser = argparse.ArgumentParser(description='Process controller.py arguments')
parser.add_argument("-b", help='Build number, e.g., 1234')
args = vars(parser.parse_args())
When I run it I get...
$ python CommandLineParser.py -h
usage: CommandLineParser.py [-h] [-b B]
Process controller.py arguments
optional arguments:
-h, --help show this help message and exit
-b B Build number, e.g., 1234
How do I make the "-b" show up as a "non-optional" argument (because it's NOT!). As an added bonus, how do I rid of the uppercase "B" after it? Thanks!
You need to set required to True and metavar (it's responsible for B) to '':
parser.add_argument("-b", help='Build number, e.g., 1234', required=True, metavar='')
Actually, you will still see your required argument as optional if run your script in a help mode. This is because of a bug: argparse required arguments displayed under "optional arguments":
The argparse module lists required args as optional in the default
help message.
There are also some workarounds suggested, but I like this one more: add your own required arguments group:
required_group = parser.add_argument_group('required arguments')
required_group.add_argument("-b", help='Build number, e.g., 1234', required=True, metavar='')
Then, you will see this on a command-line:
$ python test.py -h
usage: test.py [-h] -b
Process controller.py arguments
optional arguments:
-h, --help show this help message and exit
required arguments:
-b Build number, e.g., 1234
Please use the required keyword when adding it to the argparse: http://docs.python.org/2/library/argparse.html#sub-commands
parser.add_argument("-b", help='Build number, e.g., 1234', required=True)