Python 2.7 optparse not reading 2nd flag - python

Can someone help me out here? I'm not sure what I'm doing wrong but I can't seem to get my second option to be read from the command line.
from optparse import OptionParser
parser=OptionParser()
parser.add_option("-s", action="store", type="string", dest="scenario")
parser.add_option("-l", action="store", type="string", dest="logger")
(options, args)=parser.parse_args()
print options.scenario
print options.logger
print options
Print Results
>>python test.py -sfoo -lbar
foo
None
{'logger': None, 'scenario': 'foo'}
Additionally, I cannot put a space in between the flag and argument -sfoo is ok but -s foo is not. It's pretty annoying. Can anyone see what I am doing wrong? Thanks in advance.

As #user3757614 suggests in his comment, use argparse instead.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--scenario', required=True, dest="scenario")
parser.add_argument('-l', '--logger', required=True, dest="logger")
args = parser.parse_args()
print args
print args.scenario
print args.logger
And in the command line:
$ python test.py -s test1 -l test2
Namespace(logger='test2', scenario='test1')
test1
test2

Related

argparse: unable to get subparser_name after declaring a global argument

I've got a parser with some sub-parsers. I setup a global argument to be used on all subparser. Here's the relevant snippet
parser = argparse.ArgumentParser(prog="my_prog", add_help=False)
parser.add_argument('-d', '--debug', action='store_true', help='debug flag')
subparsers = parser.add_subparsers(dest="subparser_name", help='some help notes')
parser_cmd1 = subparsers.add_parser('cmd1', parents=[parser])
parser_cmd1.add_argument('-f', '-foo', type=str, action=foo, required=False, help='foo command')
parser_cmd2 = subparsers.add_parser('cmd2', parents=[parser])
parser_cmd2.add_argument('-b', '-bar', type=str, action=bar, required=False, help='bar command')
args = parser.parse_args()
parser = args.subparser_name
print(args)
if args.debug:
logging.basicConfig(level=logging.INFO)
if parser == 'cmd1':
if args.foo:
//do foo stuff
if parser == 'cmd2':
if args.bar:
//do bar stuff
So you can a command like such my_prog.py cmd1 -d -f inp_str. Here's the problem: subparser_name is None. The output of print(args) looks kind of like this
Namespace(debug=True, foo="inp_str", subparser_name=None)
Before I added the global debug argument, subparser_name would be the name of the command I ran, i.e. 'cmd1' or 'cmd2'. Now, it's 'None'. Even with the parents=[parser] addition in the subparser creation. How can I fix this? How do I know which command was called?
Split out the common args to a separate ArgumentParser, which is then used as parent for the sub parsers. Also your foo and bar options were specified using -foo and -bar whi should be --foo and --bar. Also you didn't have default values for these so e.g. when -f/--foo wasn't specified args.foo correctly didn't exist.
This works better:
import argparse
common_args = argparse.ArgumentParser(prog="my_prog", add_help=False)
common_args.add_argument('-d', '--debug', action='store_true', help='debug flag')
parser = argparse.ArgumentParser(prog="my_prog", add_help=True)
subparsers = parser.add_subparsers(dest="subparser_name", help='some help notes')
parser_cmd1 = subparsers.add_parser('cmd1', parents=[common_args])
parser_cmd1.add_argument('-f', '--foo', type=str, default='', required=False, help='foo command')
parser_cmd2 = subparsers.add_parser('cmd2', parents=[common_args])
parser_cmd2.add_argument('-b', '--bar', type=str, default='', required=False, help='bar command')
args = parser.parse_args()
parser = args.subparser_name
print(args)
if args.debug:
logging.basicConfig(level=logging.INFO)
if parser == 'cmd1':
if args.foo:
#//do foo stuff
print( f"foo {args.foo}" )
if parser == 'cmd2':
if args.bar:
#//do bar stuff
print( f"bar {args.bar}" )
run with:
args.py cmd1 -f asd
output:
Namespace(subparser_name='cmd1', debug=False, foo='asd')
foo asd
Update:
If you want to be able to use e.g. args.py -d cmd1 then on the creation of parser, specify parents=[common_args]
parser = argparse.ArgumentParser(prog="my_prog", add_help=True, parents=[common_args])
Next time you ask a question ensure you only post code as a minimal reproducible example - i.e. that can be run without adding anything
The subparser's defaults have priority of any values set by the main parser - default or user input. The main does set the subparser_name to 'cmd1', but the subparser changes it back to the default None.
While not evident in your test case, defining debug at both levels has the same problem. The subparser's default overwrites anything set in the main.
In general, it is not a good idea to use the same dest in the main and subparsers. Flags can be the same, but the dest should be different - at least if you want to see anything set by the main.
And using the main parser as a parent to the sub, is just asking for confusion.

argparse: Subparser with global argument where position does not matter

I'll explain the problem with an example. Suppose we have the following code for a random python program:
import argparse
parser = argparse.ArgumentParser(prog="webduino-generator",
description="Webduino source builder")
# Global arguments
parser.add_argument("-v", "--verbose",
action="store_true", dest='verbose',
help="Enable verbose output")
subparsers = parser.add_subparsers(dest="command")
parser_build = subparsers.add_parser("build", help="Build it")
parser_open = subparsers.add_parser("open", help="Open it")
# Check arguments
args = parser.parse_args()
print(args)
Now with this parser, I can do
program.py -v open
which is great! However, I cannot do
program.py open -v
Also the parent/global argument -v will not be listed on the help page of the sub parser.
Is there a way to make this work and add it to the help page of the subparser?

Using stdin in a mutually exclusive argument group

I'm trying to use argparse in a script such that I could pass a string either as an argument or via standard input. I figured I could use add_mutually_exclusive_group for that, and set required=True to enforce that just one argument is required. So, I created the following script (my_script.py):
#!/usr/bin/env python
import argparse
from sys import stdin
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('str_arg', nargs='?', type=str)
group.add_argument('str_in', nargs='?', type=argparse.FileType('r'), default=stdin)
args = parser.parse_args()
print args.str_arg or args.str_in.readline()
Passing the string as a parameter works fine. However, when I try to pipe the string from standard input like so:
$ echo Hello | python my_script.py
Python complains that one of the arguments str_arg str_in is required. What am I doing wrong? Is there a better way of achieving this?
It doesn't seem like you should try doing this with fancy argparse features, just some simple logic:
import argparse
from sys import stdin
parser = argparse.ArgumentParser()
parser.add_argument('str_arg', nargs='?', type=str)
args = parser.parse_args()
print(args.str_arg or stdin.readline())
2 positionals in a mutually exclusive group does not work.
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('str_arg', nargs='?')
group.add_argument('str_in', nargs='?')
args = parser.parse_args()
print(args)
sample runs:
0217:~/mypy$ python3 stack49705916.py
usage: stack49705916.py [-h] (str_arg | str_in)
stack49705916.py: error: one of the arguments str_arg str_in is required
0904:~/mypy$ python3 stack49705916.py foo
Namespace(str_arg='foo', str_in=None)
0904:~/mypy$ python3 stack49705916.py foo bar
usage: stack49705916.py [-h] (str_arg | str_in)
stack49705916.py: error: argument str_in: not allowed with argument str_arg
piping is not a substitute for a commandline argument. stdin has to be read separately.
import argparse, sys
print(sys.argv)
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('str_arg', nargs='?')
group.add_argument('str_in', nargs='?')
args = parser.parse_args()
print(args)
print(sys.stdin.read())
0909:~/mypy$ echo 'hello' | python3 stack49705916.py foo
['stack49705916.py', 'foo']
Namespace(str_arg='foo', str_in=None)
hello
argparse.FileType('r') recognizes - as stdin.
print(sys.argv)
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--str_in', type=argparse.FileType('r'))
args = parser.parse_args()
print(args)
print(args.str_in.read())
runs
0945:~/mypy$ python3 stack49705916.py -i test.txt
['stack49705916.py', '-i', 'test.txt']
Namespace(str_in=<_io.TextIOWrapper name='test.txt' mode='r' encoding='UTF-8'>)
2000+0
2001+2
2002+1
0946:~/mypy$ python3 stack49705916.py -i -
['stack49705916.py', '-i', '-']
Namespace(str_in=<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>)
typing hello on input line
typing hello on input line
0947:~/mypy$ echo Hello | python3 stack49705916.py -i -
['stack49705916.py', '-i', '-']
Namespace(str_in=<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>)
Hello
0947:~/mypy$
In a shell you can pass the output of echo Whatever as an argument using back quotes and not a pipe:
$ python my_script.py `echo whatever`

Using argparse with a specified acceptable option

I am trying to use argparse to accept required command line options. I have defined a function like so
def get_args():
parser = argparse.ArgumentParser(description='Help Desk Calendar Tool')
parser.add_argument('-s', '--start', type=str, required=True, metavar='YYYY-MM-DD')
parser.add_argument('-e','--end', type=str, required=True, metavar='YYYY-MM-DD')
parser.add_argument('-m','--mode', type=str, required=True , metavar='add|del')
args = parser.parse_args()
start = args.start
end = args.end
mode = args.mode
return start,end,mode
What I am trying to do is for the option --mode I would like it to ONLY accept an parameter of either add or del. I could do this from an if statement but was wondering if argparse has a built in way of accomplishing this task. I looked at nargs but wasn't too clear if that's the path I need to go down
I think you are asking about choices:
parser.add_argument('-m','--mode', type=str, required=True, choices=['add', 'del'])
Demo:
$ python test.py -s 10 -e 20 -m invalid
usage: test.py [-h] -m {add,del}
test.py: error: argument -m/--mode: invalid choice: 'invalid' (choose from 'add', 'del')

Take string argument from command line?

I need to take an optional argument when running my Python script:
python3 myprogram.py afile.json
or
python3 myprogram.py
This is what I've been trying:
filename = 0
parser = argparse.ArgumentParser(description='Create Configuration')
parser.add_argument('filename', type=str,
help='optional filename')
if filename is not 0:
json_data = open(filename).read()
else:
json_data = open('defaultFile.json').read()
I was hoping to have the filename stored in my variable called "filename" if it was provided. Obviously this isn't working. Any advice?
Please read the tutorial carefully. http://docs.python.org/howto/argparse.html
i believe you need to actually parse the arguments:
parser = argparse.ArgumentParser()
args = parser.parse_args()
then filename will be come available args.filename
Check sys.argv. It gives a list with the name of the script and any command line arguments.
Example script:
import sys
print sys.argv
Calling it:
> python script.py foobar baz
['script.py', 'foobar', 'baz']
If you are looking for the first parameter sys.argv[1] does the trick. More info here.
Try argparse's default parameter, its well documented.
import argparse
parser = argparse.ArgumentParser(description='Create Configuration')
parser.add_argument('--file-name', type=str, help='optional filename',
default="defaultFile.json")
args = parser.parse_args()
print(args.file_name)
Output:
$ python open.py --file-name option1
option1
$ python open.py
defaultFile.json
Alternative library:
click library for arg parsing.

Categories