With this code, I can't seem to add "--height" as argument because Python is confused with the "-h / --help" default option. I've tried to add add_help=False when creating the object but I still get the error main.py: error: the following arguments are required: height
import argparse
parser = argparse.ArgumentParser(description='my description')
parser.add_argument('height', type=int, nargs=1)
args = parser.parse_args()
You created a positional argument. The way argparse works is that when you define an argument without any leading - or -- it will consider it positional, so you have to call the script like python yourscript.py the_height.
If you want to call it like python myscript.py --height 222 then you must do
parser.add_argument("--height", action="store")
args_namespace = parser.parse_args()
print(args_namespace.height)
Related
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='*')
I have a script that consumes command line arguments and I would like to implement two argument-passing schemes, namely:
Typing the arguments out at the command line.
Storing the argument list in a file, and passing the name of this file to the program via the command line.
To that end I am passing the argument fromfile_prefix_chars to the ArgumentParser constructor.
script.py
from argparse import ArgumentParser
parser = ArgumentParser(fromfile_prefix_chars='#')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
args.txt
--foo
2
--bar
2
Sample use cases
$ python script.py --foo 3
Namespace(bar=1, filename=None, foo='3')
$ python script.py #args.txt --foo 3
Namespace(bar='2', filename=None, foo='3')
I was expecting that args.filename would retain the name of the file, but surprinsingly enough it has the value None instead. I am aware that I could get the file name from sys.argv through a bit of processing. Is there a cleaner way (ideally an argparse-based approach) to elicit the name of the arguments file?
Your script.py, plus the file. I have added the file name to the file itself.
args.txt
args.txt
--foo
2
--bar
2
testing:
1803:~/mypy$ python3 stack56811067.py --foo 3
Namespace(bar=1, filename=None, foo='3')
1553:~/mypy$ python3 stack56811067.py #args.txt --foo 3
Namespace(bar='2', filename='args.txt', foo='3')
From my testing, using fromfile_prefix_chars means that argparse will not actually pass the argument to your program. Instead, argparse sees the #args.txt, intercepts it, reads from it, and passes the arguments without #args.txt to your program. This is presumably because most people don't really need the filename, just need the arguments within, so argparse saves you the trouble of creating another argument to store something you don't need.
Unfortunately, all of the arguments are stored as local variables in argparse.py, so we cannot access them. I suppose that you could override some of argparse's functions. Keep in mind that this is a horrible, disgusting, hacky solution and I feel that parsing sys.argv is 100% better.
from argparse import ArgumentParser
# Most of the following is copied from argparse.py
def customReadArgs(self, arg_strings):
# expand arguments referencing files
new_arg_strings = []
for arg_string in arg_strings:
# for regular arguments, just add them back into the list
if not arg_string or arg_string[0] not in self.fromfile_prefix_chars:
new_arg_strings.append(arg_string)
# replace arguments referencing files with the file content
else:
try:
fn = arg_string[1:]
with open(fn) as args_file:
# What was changed: before was []
arg_strings = [fn]
for arg_line in args_file.read().splitlines():
for arg in self.convert_arg_line_to_args(arg_line):
arg_strings.append(arg)
arg_strings = self._read_args_from_files(arg_strings)
new_arg_strings.extend(arg_strings)
except OSError:
err = _sys.exc_info()[1]
self.error(str(err))
# return the modified argument list
return new_arg_strings
ArgumentParser._read_args_from_files = customReadArgs
parser = ArgumentParser(fromfile_prefix_chars='#')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
Just for the record, here's a quick and dirty solution I came up with. It basically consists in creating a copy of parser and set its from_file_prefix_chars attribute to None:
import copy
parser_dupe = copy.copy(parser)
parser_dupe.fromfile_prefix_chars = None
args_raw = parser_dupe.parse_args()
if args_raw.filename:
args.filename = args_raw.filename[1:]
Positional parameters with nargs='+' or nargs=<integer> are required, meaning that argparse gives an error if the parameter is not specified at least once. However, no error is given if the user calls the program with the -h|--help option, regardless of whether the positional parameters were specified.
How can I implement custom options like -h that do not require positional parameters to be set, while still requiring positional parameters for all other options?
For example, if let's say my program has the following (or similar) usage text:
usage: PROG [-h] [-o] [-u] positional
How can I implement the following behaviour:
calling PROG with no options and no positionals is an error
calling PROG with -u and no positionals is an error
calling PROG with only positionals is allowed
calling PROG with -h or -o is allowed regardless of whatever else is specified
My code
This meets all requirements except the last one, namely -o still requires the positional parameter to be specified.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs=1)
parser.add_argument('-o', action='store_true')
parser.add_argument('-u', action='store_true')
args = parser.parse_args()
print(args)
Does argparse have a built-in way to say that positional is not required iff -o is specified, or must this behaviour be implemented manually?
Manual methods
Set nargs='?' or nargs='*' for the positional parameter (i.e. make it optional), and then add the following lines immediately after args = parser.parse_args():
if not args.o and args.positional is None:
parser.error("the following arguments are required: positional")
Or add these lines, courtesy of this answer:
if len(sys.argv) == 1:
# display help message when no args are passed.
parser.print_help()
sys.exit(1)
This simply prints the help message if no options or positional parameters were specified. This behaviour is consistent with many command line programs, e.g. git.
Of course, making positional optional will affect the usage message:
usage: PROG [-h] [-o] [-u] [positional]
If you really don't want this to happen you can override the usage method like this:
parser = argparse.ArgumentParser(usage="%(prog)s [-h] [-o] [-u] positional")
With the below code, I am able to execute in the following way
python example.py create path /root/bin/.
But I don't want to give argument as create and path instead,
python example.py --create --path /root/bin.
When I create subparser with --create, it is taking as optional argument and not like a positional argument. How to make it positional argument with leading double minus sign? and i want path should be a subparser of create
My code should execute in the following way
python example --create --path /root/bin
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='commands')
create_parser = subparsers.add_parser('create', help='Create a directory')
path_subparser = create_parser.add_subparsers()
path_parser = path_subparser.add_parser('path', help='path')
path_parser.add_argument('path', action='store', help='path')
I am trying to make a Python program that uses the argparse module to parse command-line options.
I want to make an optional argument that can either be named or positional. For example, I want myScript --username=batman to do the same thing as myScript batman. I also want myScript without a username to be valid. Is this possible? If so, how can it be done?
I tried various things similar to the code below without any success.
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-u", "--user-name", default="admin")
group.add_argument("user-name", default="admin")
args = parser.parse_args()
EDIT: The above code throws an exception saying ValueError: mutually exclusive arguments must be optional.
I am using Python 2.7.2 on OS X 10.8.4.
EDIT: I tried Gabriel Jacobsohn's suggestion but I couldn't get it working correctly in all cases.
I tried this:
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-u", "--user-name", default="admin", nargs="?")
group.add_argument("user_name", default="admin", nargs="?")
args = parser.parse_args()
print(args)
and running myScript batman would print Namespace(user_name='batman'), but myScript -u batman and myScript --user-name=batman would print Namespace(user_name='admin').
I tried changing the name user-name to user_name in the 1st add_argument line and that sometimes resulted in both batman and admin in the namespace or an error, depending on how I ran the program.
I tried changing the name user_name to user-name in the 2nd add_argument line but that would print either Namespace(user-name='batman', user_name='admin') or Namespace(user-name='admin', user_name='batman'), depending on how I ran the program.
The way the ArgumentParser works, it always checks for any trailing positional arguments after it has parsed the optional arguments. So if you have a positional argument with the same name as an optional argument, and it doesn't appear anywhere on the command line, it's guaranteed to override the optional argument (either with its default value or None).
Frankly this seems like a bug to me, at least when used in a mutually exclusive group, since if you had specified the parameter explicitly it would have been an error.
That said, my suggested solution, is to give the positional argument a different name.
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-u','--username')
group.add_argument('static_username',nargs='?',default='admin')
Then when parsing, you use the optional username if present, otherwise fallback to the positional static_username.
results = parser.parse_args()
username = results.username or results.static_username
I realise this isn't a particularly neat solution, but I don't think any of the answers will be.
Here is a solution that I think does everything you want:
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--user-name", default="admin")
# Gather all extra args into a list named "args.extra"
parser.add_argument("extra", nargs='*')
args = parser.parse_args()
# Set args.user_name to first extra arg if it is not given and len(args.extra) > 0
if args.user_name == parser.get_default("user_name") and args.extra:
args.user_name = args.extra.pop(0)
print args
If you run myScript -u batman or myScript --user-name=batman, args.user_name is set to 'batman'. If you do myScript batman, args.user_name is again set to 'batman'. And finally, if you just do myScript, args.user_name is set to 'admin'.
Also, as an added bonus, you now have all of the extra arguments that were passed to the script stored in args.extra. Hope this helps.
Try to use the "nargs" parameter of the add_argument method.
This way it works for me.
Now you can add the username twice,
so you have to check it and raise an error, if you want.
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--user-name", default="admin")
parser.add_argument("user_name", default="admin", nargs="?")
args = parser.parse_args()
print(args)