Argparse with action='store_true' not working as expected - python

The idea is to add a flag (--slack, or -s) when running the script, so that I don't have to comment out the rep.post_report_to_slack() method every time I don't want to use it. When I run:
$ python my_script.py --slack
I get the error:
my_script.py: error: unrecognized arguments: --slack
Here's the code:
def main():
gc = Google_Connection()
meetings = gc.meetings
rep = Report(meetings)
if args.slack:
rep.post_report_to_slack()
print('posted to slack')
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--slack', help='post to slack',
action='store_true')
args = parser.parse_args()
main()

Your code works, but it relies on args being available in the module namespace, which isn't great because, for one thing, it means you can't use your function without calling the script from the command line. A more flexible and conventional approach would be to write the function to accept whatever arguments it needs, and then pass everything you get from argparse to the function:
# imports should usually go at the top of the module
import argparse
def get_meeting_report(slack=False):
gc = Google_Connection()
meetings = gc.meetings
rep = Report(meetings)
if slack:
rep.post_report_to_slack()
print('posted to slack')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--slack', help='post to slack',
action='store_true')
args = parser.parse_args()
args = vars(args)
get_meeting_report(**args)
Now you can also more easily use your function outside of argparse by calling it directly.

Related

running python script with argparser

Trying to run my script using argparser, where the program does not run, unless correct argument is in place, however it does not seem to work;
AttributeError: 'Namespace' object has no attribute 'func'
import sys
import argparse
from develop import Autogit as gt
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
# Create argument command
parser_update = subparsers.add_parser('--sync', help='Sync local and remote repos')
parser_update.set_defaults(func=gt.run)
# Adding arguments
parser.add_argument('--sync', type=str, required=True)
if len(sys.argv) <= 1:
sys.argv.append('--help')
options = parser.parse_args()
options.func() # <--- Causes the error
if __name__ == '__main__':
main()
Also when the --sync arg is given it ask for another, then when I add one more argument. SYNC, then it returns attribute error.
Edit
Trying to make the program run the develop.Autogit.run
Working..
Had to also add args as argument in the run funciton i am calling.
I think what you are trying to accomplish is setting a default, typically this is done with ArgumentParser.set_defaults(). You need to do this with the uninitialised function. See this example:
import sys
import argparse
def f(args):
print("In func")
print(args)
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
# Create argument command
parser_update = subparsers.add_parser("sync", help="Sync local and remote repos")
parser_update.set_defaults(func=f) # <-- notice it's `f` not `f()`
options = parser.parse_args()
options.func(options)
if __name__ == "__main__":
main()
As an aside, you will have more problems with your snippet as you are defining the same parameter (--sync) in multiple places. When using subparsers it is customary to make these positional (no leading --) so they act as subcommands.
Here is a typical command line that I would use with subcommands:
import sys
import argparse
def f(args):
print("In func f")
print(args)
def g(args):
print("In func g")
print(args)
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
parser_update = subparsers.add_parser("sync", help="Sync local and remote repos")
parser_update.set_defaults(func=f)
parser_delete = subparsers.add_parser("delete", help="Delete sub-command")
parser_delete.set_defaults(func=g)
options = parser.parse_args()
if options.command is not None:
options.func(options)
else:
parser.print_help()
parser.exit()
if __name__ == "__main__":
main()

Generating and passing args at runtime to a second script that uses argparse

I have a script (A) that uses argparse to parse comand line args, I am writing script (B) that will generate various combinations of arguments for script A at runtime and call the main method of A.
I cannot modify script A.
Script A
import argparse
def main():
args = code_to_add_args().parse_args()
# does stuff with args
Script B
import argparse
from A import main
if __name__ == "__main__":
my_cli_args = code_to_add_args().parse_args()
new_args_generated_at_runtime = generate_args()
main() # pass new_args_generated_at_runtime into A to be parsed by argparse
How do I use script B to supply CLI args to script A without modifying script A?
The closest I have found is here, but its answer requires modifying the script A.
If possible I would prefer not to use subprocess.call()
The comment from hpaulj led me to a solution that worked.
I cleared sys.argv and then appended my args to it:
Script A
import argparse
def main():
args = code_to_add_args().parse_args()
# does stuff with args
Script B
import argparse
from A import main
if __name__ == "__main__":
my_cli_args = code_to_add_args().parse_args()
new_args_list = generate_list_of_args()
# Keep the first element of sys.argv to retain the name of the script
sys.argv = [sys.argv[0]]
for param in new_args_list:
sys.argv.append(param)
main() # Now new args have been added to sys.argv, argparse in script A will use them.

argparse not getting triggered

I'm trying to use the Python argparse module to check argument parameters:
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Example with non-optional arguments')
parser.add_argument('count', action="store", type=int)
parser.add_argument('units', action="store")
print 'test'
When I run the script (python test.py some inches), it just prints the output 'test' but the argparse module is not being triggered.
You need to actually call it!
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Example with non-optional arguments')
parser.add_argument('count', action="store", type=int)
parser.add_argument('units', action="store")
args = parser.parse_args()
print args
You have to parse the args for this to work. You either need to call parser.parse_args() or parser.parse_known_args(). More info can be found here:
https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.parse_args

Python optparse from method call?

I have two python scripts with the following structure:
# Script1.py
from optparse import OptionParser
def main():
parser = OptionParser()
parser.add_option("-a", "--add-foobar", action="store_true", help="set foobar true",
dest="foobar", default=False)
options, args = parser.parse_args()
print options.foobar
if __name__ == "__main__":
main()
# Script2.py
from Script1 import main as script1Main
def main():
script1Main()
Is there a way to pass command line arguments from script 2 to script 1? Script 1 in this example is immutable, therefore this must be done only thorough optparse.
If you don't pass any arguments to parse_args, it just uses the value of sys.argv[1:], which is going to be whatever arguments were passed when you called Script2.py. The fact that Script2.py calls Script1.main doesn't change that.
Firstly, maybe use argparse instead. You can process all arguments in script 2, then pass the argument handle to script 1.
# Script1.py
def main(args):
print args
# Script2.py
import argparse
from Script1 import main as script1Main
def main():
parser = argparse.ArgumentParser(
parser.add_option("-a", "--add-foobar", action="store_true", help="set foobar true", default=False)
args = parser.parse_args()
script1Main(args)
if __name__ == "__main__":
main()

The python's argparse errors

where report this error : TypeError: 'Namespace' object is not iterable
import argparse
def parse_args():
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-a', '--aa', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)
return parser.parse_args()
def main():
(options, args) = parse_args()
if __name__ == '__main__':
main()
Your issue has to do with this line:
(options, args) = parse_args()
Which seems to be an idiom from the deprecated "optparse".
Use the argparse idiom without "options":
import argparse
parser = argparse.ArgumentParser(description='Do Stuff')
parser.add_argument('--verbosity')
args = parser.parse_args()
Try:
args = parse_args()
print args
Results:
$ python x.py -b B -aa
Namespace(aa=True, b='B', c=None)
It's exactly like the error message says: parser.parse_args() returns a Namespace object, which is not iterable. Only iterable things can be 'unpacked' like options, args = ....
Though I have no idea what you were expecting options and args, respectively, to end up as in your example.
The error is in that parse_argv option is not required or used, only argv is passed.
Insted of:
(options, args) = parse_args()
You need to pass
args = parse_args()
And the rest remains same.
For calling any method just make sure of using argv instead of option.
For example:
a = argv.b
The best way (for me) to operate on args, where args = parser.parse_args()
is using args.__dict__. It's good, for example, when you want to edit arguments.
Moreover, it's appropriate to use long notation in your arguments, for example '--second' in '-a' and '--third' in '-b', as in first argument.
If you want to run 'main' you can should miss 'options', but it was said earlier.

Categories