Running web.py app with custom cmd options - python

I would like to use web.py to build an http interface for some larger library, which also provides a command line script that takes optional parameters.
When I tried the simple web.py tutorial example in combination with optparse, I have the problem that web.py always takes the first cmd argument as port, which is not what I want. Is there a way to tell web-py not to check the command line args. Here is an example:
#!/usr/bin/env python
# encoding: utf-8
"""
web_interface.py: A simple Web interface
"""
import optparse
import web
urls = ("/.*", "hello")
app = web.application(urls, globals())
class hello:
def GET(self):
return 'Hello, world!\n'
if __name__ == "__main__":
p = optparse.OptionParser()
p.add_option('--test', '-t', help="the number of seed resources")
options, arguments = p.parse_args()
print options.test
app.run()
...which I want to run as follows:
python web_interface.py -t 10

It's a bit of a hack, but I guess you could do:
import sys
...
if __name__ == "__main__":
p = optparse.OptionParser()
p.add_option('--test', '-t', help="the number of seed resources")
options, arguments = p.parse_args()
print options.test
# set sys.argv to the remaining arguments after
# everything consumed by optparse
sys.argv = arguments
app.run()

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 with action='store_true' not working as expected

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.

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()

Python - unittest trying to call imported custom argparser

I have a unittest that wants to call an imported module to do both parse_os based on the unittest's command-line option but it seems unittest does not recognize the option, any ideas:
./python testParser.py --mac
option --mac not recognized
Usage: testParser.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
testParser.py - run default set of tests
testParser.py MyTestSuite - run suite 'MyTestSuite'
testParser.py MyTestCase.testSomething - run MyTestCase.testSomething
testParser.py MyTestCase - run all 'test*' test methods
in MyTestCase
I want to run my unittest program like this: python testParser.py --mac
EDITTED: Works now by changing 'unittest.main()' to:
runner = unittest.TextTestRunner(stream=stderr_file)
itersuite = unittest.TestLoader().loadTestsFromTestCase(TT28046_ForensicSearchSmokeTest)
runner.run(itersuite)
Unittest program:
import logging
import unittest
from myargparse import *
class MyTest(unittest.TestCase):
def test_parse_os(self):
## Parse the args:
self.install = install_sw(parse_os(arg=""))
print 'Which os? %s' % self.install
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
# get the default logger
logger = logging.getLogger()
# add a file handler
logger.addHandler(logging.FileHandler('stdout.txt', mode='w'))
# set up a stream for all stderr output
stderr_file = open('stderr.txt', 'w')
# attach that stream to the testRunner
unittest.main(testRunner=unittest.TextTestRunner(stream=stderr_file))
My imported module:
import argparse
import os
import sys
def parse_os(arg):
my_os = ''
parser = argparse.ArgumentParser()
parser.add_argument("-m", "--mac",
action="store_true")
parser.add_argument("-w", "--win",
action="store_true")
args = parser.parse_args()
if args.mac:
print 'Mac'
my_os = "Mac"
if args.win:
print 'Windows'
my_os = "Windows"
return my_os
def install_sw(my_os):
installed_os = None
if my_os == 'Mac':
print 'Installing Mac...'
installed_os = 'Mac'
if my_os == 'Windows':
print 'Installing Windows...'
installed_os = 'Windows'
return installed_os
The sys.argv variable is a simple list so you can modify/replace it at your wish.
I'd consider using a context manager in this case, on the lines of:
class SysArgv(object):
def __init__(self, argv):
self._old_argv = sys.argv
sys.argv = argv
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
sys.argv = self._old_argv
return False
And used as:
In [4]: with SysArgv(['a', 'b', 'c']):
...: print(sys.argv)
...:
['a', 'b', 'c']
In your case simple wrap the test code like:
with SysArgv(['the_module_name.py', '--mac']):
# test code goes here
and the argparse module will see the arguments you wants.
As for passing the arguments to the unittest module when running the tests, it's possible passing the argv argument to unittest.main. From the documentation:
The `argv` argument can be a list of options passed to the program, with the first element being the program name. If not specified or `None`, the values of `sys.argv` are used.
However in this case you should modify the sys.argv variable before calling unittest.main:
if __name__ == '__main__':
options = ['name_of_module.py'] + sys.argv[-1:] # last argument as option for the test
with SysArgv(sys.argv[:-1]): # or modify how you want
unittest.main(argv=options)
Have you tried using just '-m' instead of '--mac'?
You may also try:
import optparse
parser = optparse.OptionParser()
parser.add_option("-m", "--mac",
dest="mac",
action="store_true",
help="Run as Mac")
parser.add_option("-w", "--win",
dest="win",
action="store_true",
help="Run as Win")
(options, args) = parser.parse_args()
Thank you all for your suggestions but I decided to go with this to limit the changes to my program.
Instead of calling 'unittest.main()', I just changed to call the following:
runner = unittest.TextTestRunner(stream=stderr_file)
itersuite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
runner.run(itersuite)
Based on all the answers here, I originally did this simple hack, and it worked:
# Change sys.argv before unittest tries to parse our args
sys.argv = [sys.argv[0]] # Replace with only the first arg
unittest.main()
The I realized I could still use all the unitest command line args, and in my case I was just passing a bunch of paths, so anything starting with a "-" could just be passed on and there is no need to hack anything since unittest.main() has an argv argument
# Pass on options, and more importantly, don't pass on ALL args
options = [sys.argv[0]] + [a for a in sys.argv if a.startswith("-")]
unittest.main(argv=options)
Just filter out all argparse and not arparse parameters and send then ones not being argparse arguments to sys.argv which is the one unittest uses:
args, notknownargs = parser.parse_known_args()
sys.argv[1:] = notknownargs

Categories