I have a file conftest.py in directory A. I have some test files in directory B which is in A. I run all of them using py.test -sv B/. However sometime I want to pass argument -val="Hello" and store in a file.
I am trying to do it as following:
import argparse
parser=argparse.ArgumentParser()
parser.add_argument("val",help="value")
args=parser.parse_args()
print(args.val)
a_file=open("file_val","w")
a_file.write(args.val)
a.file.close()
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
However it doesn't write anything and gives error: unrecognized arguments: -sv —val=hello, when I run py.test -sv B/ —val=Hello
so I tried following :
import argparse
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
parser.add_argument("val",help="value")
args=parser.parse_args()
print(args.val)
a_file=open("file_val","w")
a_file.write(args.val)
a.file.close()
But it gives error : No method add_argument when I run py.test -sv B/ —val=Hello
read command line argument while running pytest -sv B/
It should work for you:
First of all you need to pass the argument using addoption and fixture that will read the option.
For that you should create a separate file and name it conftest.py in your test folder.
The content of the conftest.py file:
import pytest
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
#pytest.fixture
def name(request):
return request.config.getoption("--name")
Then you need to create a test function with an argument which named after the fixture in the separate file, or like in your case, even in a separate folder.
Content of B/test_write_file.py:
def test_write_file(name):
print(name)
a_file = open("file_val.txt", "w")
a_file.write(name)
a_file.close()
assert 0 # or whatever you want
After that you can freely run your test from the test root folder:
pytest -sv B/ --name=Hello
Output:
________________ test_write_file _________________
name = 'Hello'
def test_write_file(name):
print(name)
a_file = open("file_val.txt", "w")
a_file.write(name)
a_file.close()
> assert 0
E assert 0
B\test_write_file.py:8: AssertionError
You don't need to use argparse, pytest does everything for you.
The structure of the test folder:
Just like in this example:
https://docs.pytest.org/en/6.2.x/example/simple.html
Cheers!
Im trying to have interchangeable config files and I'm using py.test to send the testing suite to the cloud.
The following works when I run them locally using python main.py --site site1 in the terminal.
I'm trying to figure out how I can add cli arguments so that it will work with py.test
I have 3 files. main, config, and site1_config
main.py
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='CLI tests for an environment')
parser.add_argument('--site', nargs='?', help='the site to use for the config')
parser.add_argument('unittest_args', nargs='*')
#Get our property
args = parser.parse_args()
if args.site:
config.localizeConfig(args.site)
sys.argv[1:] = args.unittest_args
unittest.main(verbosity = 2)
config.py
def localizeConfig(site):
localizedConfig = __import__(site+'_config');
# localizedConfig = __import__(site);
#print dir(localizedConfig)
props = filter(lambda a: not a.startswith('__'), dir(localizedConfig))
#Iterate over each property and set it on the config
for prop in props:
if prop != 'os' and prop != 'sys' and prop != 'webdriver':
globals()[prop] = getattr(localizedConfig, prop)
host_url = www.google.com
site1_config.py
host_url = www.yahoo.com
Im trying to set a flag so that when py.test -n6 --boxed main.py site1
is run, the site1_config.py will copy over its contents into config.py
I'm not sure how I can get this to work with py.test
usage: py.test [options] [file_or_dir] [file_or_dir] [...]
py.test: error: unrecognized arguments:
I don't really get what you are trying to do, but in order to add CLI arguments to py.test check:
http://pytest.org/latest/example/simple.html:
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--cmdopt", action="store", default="type1",
help="my option: type1 or type2")
The equivalent of your unittest.main(verbosity = 2) is pytest.main(['-v'])
Things to note:
In pytest, I'm not sure verbosity level is selectable
Some of the references on pytest.main(input) don't specify that the input has to be a list, but as of today it must be.
https://docs.pytest.org/en/latest/usage.html
Is there a way to use the argparse module hooked in as the interpreter for every prompt in an interface inheriting from cmd?
I'd like for my cmd interface to interpret the typical line parameter in the same way one would interpret the options and arguments passed in at runtime on the bash shell, using optional arguments with - as well as positional arguments.
Well, one way to do that is to override cmd's default method and use it to parse the line with argparse, because all commands without do_ method in your cmd.Cmd subclass will fall through to use the default method. Note the additional _ before do_test to avoid it being used as cmd's command.
import argparse
import cmd
import shlex
class TestCLI(cmd.Cmd):
def __init__(self, **kwargs):
cmd.Cmd.__init__(self, **kwargs)
self.parser = argparse.ArgumentParser()
subparsers = self.parser.add_subparsers()
test_parser = subparsers.add_parser("test")
test_parser.add_argument("--foo", default="Hello")
test_parser.add_argument("--bar", default="World")
test_parser.set_defaults(func=self._do_test)
def _do_test(self, args):
print args.foo, args.bar
def default(self, line):
args = self.parser.parse_args(shlex.split(line))
if hasattr(args, 'func'):
args.func(args)
else:
cmd.Cmd.default(self, line)
test = TestCLI()
test.cmdloop()
argparse does a sys.exit if it encounters unknown commands, so you would need to override or monkey patch your ArgumentParser's error method to raise an exception instead of exiting and handle that in the default method, in order to stay in cmd's command loop.
I would suggest you look into cliff which allows you to write commands that can automatically be used both as argparse and cmd commands, which is pretty neat. It also supports loading commands from setuptools entry points, which allows you to distribute commands as plugins to your app. Note however, that cliff uses cmd2, which is cmd's more powerful cousin, but you can replace it cmd as cmd2 was developed as a drop-in replacement for cmd.
The straight forward way would be to create an argparse parser, and parse line.split() within your function, expecting SystemExit in case invalid arguments are supplied (parse_args() calls sys.exit() when it finds invalid arguments).
class TestInterface(cmd.Cmd):
__test1_parser = argparse.ArgumentParser(prog="test1")
__test1_parser.add_argument('--bar', help="bar help")
def help_test1(self): self.__test1_parser.print_help()
def do_test1(self, line):
try:
parsed = self.__test1_parser.parse_args(line.split())
except SystemExit:
return
print("Test1...")
print(parsed)
If invalid arguments are passed, parse_args() will print errors, and the program will return to the interface without exiting.
(Cmd) test1 --unk
usage: test1 [-h] [--bar BAR]
test1: error: unrecognized arguments: --unk
(Cmd)
Everything else should work the same as a regular argparse use case, also maintaining all of cmd's functionality (help messages, function listing, etc.)
Source: https://groups.google.com/forum/#!topic/argparse-users/7QRPlG97cak
Another way, which simplifies the setup above, is using the decorator below:
class ArgparseCmdWrapper:
def __init__(self, parser):
"""Init decorator with an argparse parser to be used in parsing cmd-line options"""
self.parser = parser
self.help_msg = ""
def __call__(self, f):
"""Decorate 'f' to parse 'line' and pass options to decorated function"""
if not self.parser: # If no parser was passed to the decorator, get it from 'f'
self.parser = f(None, None, None, True)
def wrapped_f(*args):
line = args[1].split()
try:
parsed = self.parser.parse_args(line)
except SystemExit:
return
f(*args, parsed=parsed)
wrapped_f.__doc__ = self.__get_help(self.parser)
return wrapped_f
#staticmethod
def __get_help(parser):
"""Get and return help message from 'parser.print_help()'"""
f = tempfile.SpooledTemporaryFile(max_size=2048)
parser.print_help(file=f)
f.seek(0)
return f.read().rstrip()
It makes defining additional commands simpler, where they take an extra parsed parameter that contains the result of a successful parse_args(). If there are any invalid arguments the function is never entered, everything being handled by the decorator.
__test2_parser = argparse.ArgumentParser(prog="test2")
__test2_parser.add_argument('--foo', help="foo help")
#WrapperCmdLineArgParser(parser=__test2_parser)
def do_test2(self, line, parsed):
print("Test2...")
print(parsed)
Everything works as the original example, including argparse generated help messages - without the need to define a help_command() function.
Source: https://codereview.stackexchange.com/questions/134333/using-argparse-module-within-cmd-interface
This question already has answers here:
How to read/process command line arguments?
(22 answers)
Closed 8 months ago.
What's the easiest, tersest, and most flexible method or library for parsing Python command line arguments?
argparse is the way to go. Here is a short summary of how to use it:
1) Initialize
import argparse
# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')
2) Add Arguments
# Required positional argument
parser.add_argument('pos_arg', type=int,
help='A required integer positional argument')
# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
help='An optional integer positional argument')
# Optional argument
parser.add_argument('--opt_arg', type=int,
help='An optional integer argument')
# Switch
parser.add_argument('--switch', action='store_true',
help='A boolean switch')
3) Parse
args = parser.parse_args()
4) Access
print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)
5) Check Values
if args.pos_arg > 10:
parser.error("pos_arg cannot be larger than 10")
Usage
Correct use:
$ ./app 1 2 --opt_arg 3 --switch
Argument values:
1
2
3
True
Incorrect arguments:
$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'
$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10
Full help:
$ ./app -h
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
Optional app description
positional arguments:
pos_arg A required integer positional argument
opt_pos_arg An optional integer positional argument
optional arguments:
-h, --help show this help message and exit
--opt_arg OPT_ARG An optional integer argument
--switch A boolean switch
This answer suggests optparse which is appropriate for older Python versions. For Python 2.7 and above, argparse replaces optparse. See this answer for more information.
As other people pointed out, you are better off going with optparse over getopt. getopt is pretty much a one-to-one mapping of the standard getopt(3) C library functions, and not very easy to use.
optparse, while being a bit more verbose, is much better structured and simpler to extend later on.
Here's a typical line to add an option to your parser:
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
It pretty much speaks for itself; at processing time, it will accept -q or --query as options, store the argument in an attribute called query and has a default value if you don't specify it. It is also self-documenting in that you declare the help argument (which will be used when run with -h/--help) right there with the option.
Usually you parse your arguments with:
options, args = parser.parse_args()
This will, by default, parse the standard arguments passed to the script (sys.argv[1:])
options.query will then be set to the value you passed to the script.
You create a parser simply by doing
parser = optparse.OptionParser()
These are all the basics you need. Here's a complete Python script that shows this:
import optparse
parser = optparse.OptionParser()
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
options, args = parser.parse_args()
print 'Query string:', options.query
5 lines of python that show you the basics.
Save it in sample.py, and run it once with
python sample.py
and once with
python sample.py --query myquery
Beyond that, you will find that optparse is very easy to extend.
In one of my projects, I created a Command class which allows you to nest subcommands in a command tree easily. It uses optparse heavily to chain commands together. It's not something I can easily explain in a few lines, but feel free to browse around in my repository for the main class, as well as a class that uses it and the option parser
Using docopt
Since 2012 there is a very easy, powerful and really cool module for argument parsing called docopt. Here is an example taken from its documentation:
"""Naval Fate.
Usage:
naval_fate.py ship new <name>...
naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
naval_fate.py ship shoot <x> <y>
naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.py (-h | --help)
naval_fate.py --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Naval Fate 2.0')
print(arguments)
So this is it: 2 lines of code plus your doc string which is essential and you get your arguments parsed and available in your arguments object.
Using python-fire
Since 2017 there's another cool module called python-fire. It can generate a CLI interface for your code with you doing zero argument parsing. Here's a simple example from the documentation (this small program exposes the function double to the command line):
import fire
class Calculator(object):
def double(self, number):
return 2 * number
if __name__ == '__main__':
fire.Fire(Calculator)
From the command line, you can run:
> calculator.py double 10
20
> calculator.py double --number=15
30
The new hip way is argparse for these reasons. argparse > optparse > getopt
update: As of py2.7 argparse is part of the standard library and optparse is deprecated.
I prefer Click. It abstracts managing options and allows "(...) creating beautiful command line interfaces in a composable way with as little code as necessary".
Here's example usage:
import click
#click.command()
#click.option('--count', default=1, help='Number of greetings.')
#click.option('--name', prompt='Your name',
help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
It also automatically generates nicely formatted help pages:
$ python hello.py --help
Usage: hello.py [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
Pretty much everybody is using getopt
Here is the example code for the doc :
import getopt, sys
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
except getopt.GetoptError:
# print help information and exit:
usage()
sys.exit(2)
output = None
verbose = False
for o, a in opts:
if o == "-v":
verbose = True
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-o", "--output"):
output = a
So in a word, here is how it works.
You've got two types of options. Those who are receiving arguments, and those who are
just like switches.
sys.argv is pretty much your char** argv in C. Like in C you skip the first element which is the name of your program and parse only the arguments : sys.argv[1:]
Getopt.getopt will parse it according to the rule you give in argument.
"ho:v" here describes the short arguments : -ONELETTER. The : means that -o accepts one argument.
Finally ["help", "output="] describes long arguments ( --MORETHANONELETTER ).
The = after output once again means that output accepts one arguments.
The result is a list of couple (option,argument)
If an option doesn't accept any argument (like --help here) the arg part is an empty string.
You then usually want to loop on this list and test the option name as in the example.
I hope this helped you.
Use optparse which comes with the standard library. For example:
#!/usr/bin/env python
import optparse
def main():
p = optparse.OptionParser()
p.add_option('--person', '-p', default="world")
options, arguments = p.parse_args()
print 'Hello %s' % options.person
if __name__ == '__main__':
main()
Source: Using Python to create UNIX command line tools
However as of Python 2.7 optparse is deprecated, see: Why use argparse rather than optparse?
Lightweight command line argument defaults
Although argparse is great and is the right answer for fully documented command line switches and advanced features, you can use function argument defaults to handles straightforward positional arguments very simply.
import sys
def get_args(name='default', first='a', second=2):
return first, int(second)
first, second = get_args(*sys.argv)
print first, second
The 'name' argument captures the script name and is not used. Test output looks like this:
> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20
For simple scripts where I just want some default values, I find this quite sufficient. You might also want to include some type coercion in the return values or command line values will all be strings.
Just in case you might need to, this may help if you need to grab unicode arguments on Win32 (2K, XP etc):
from ctypes import *
def wmain(argc, argv):
print argc
for i in argv:
print i
return 0
def startup():
size = c_int()
ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
ref = c_wchar_p * size.value
raw = ref.from_address(ptr)
args = [arg for arg in raw]
windll.kernel32.LocalFree(ptr)
exit(wmain(len(args), args))
startup()
Argparse code can be longer than actual implementation code!
That's a problem I find with most popular argument parsing options is that if your parameters are only modest, the code to document them becomes disproportionately large to the benefit they provide.
A relative new-comer to the argument parsing scene (I think) is plac.
It makes some acknowledged trade-offs with argparse, but uses inline documentation and wraps simply around main() type function function:
def main(excel_file_path: "Path to input training file.",
excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
existing_model_path: "Path to an existing model to refine."=None,
batch_size_start: "The smallest size of any minibatch."=10.,
batch_size_stop: "The largest size of any minibatch."=250.,
batch_size_step: "The step for increase in minibatch size."=1.002,
batch_test_steps: "Flag. If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."
pass # Implementation code goes here!
if __name__ == '__main__':
import plac; plac.call(main)
I prefer optparse to getopt. It's very declarative: you tell it the names of the options and the effects they should have (e.g., setting a boolean field), and it hands you back a dictionary populated according to your specifications.
http://docs.python.org/lib/module-optparse.html
I think the best way for larger projects is optparse, but if you are looking for an easy way, maybe http://werkzeug.pocoo.org/documentation/script is something for you.
from werkzeug import script
# actions go here
def action_foo(name=""):
"""action foo does foo"""
pass
def action_bar(id=0, title="default title"):
"""action bar does bar"""
pass
if __name__ == '__main__':
script.run()
So basically every function action_* is exposed to the command line and a nice
help message is generated for free.
python foo.py
usage: foo.py <action> [<options>]
foo.py --help
actions:
bar:
action bar does bar
--id integer 0
--title string default title
foo:
action foo does foo
--name string
consoleargs deserves to be mentioned here. It is very easy to use. Check it out:
from consoleargs import command
#command
def main(url, name=None):
"""
:param url: Remote URL
:param name: File name
"""
print """Downloading url '%r' into file '%r'""" % (url, name)
if __name__ == '__main__':
main()
Now in console:
% python demo.py --help
Usage: demo.py URL [OPTIONS]
URL: Remote URL
Options:
--name -n File name
% python demo.py http://www.google.com/
Downloading url ''http://www.google.com/'' into file 'None'
% python demo.py http://www.google.com/ --name=index.html
Downloading url ''http://www.google.com/'' into file ''index.html''
Here's a method, not a library, which seems to work for me.
The goals here are to be terse, each argument parsed by a single line, the args line up for readability, the code is simple and doesn't depend on any special modules (only os + sys), warns about missing or unknown arguments gracefully, use a simple for/range() loop, and works across python 2.x and 3.x
Shown are two toggle flags (-d, -v), and two values controlled by arguments (-i xxx and -o xxx).
import os,sys
def HelpAndExit():
print("<<your help output goes here>>")
sys.exit(1)
def Fatal(msg):
sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
sys.exit(1)
def NextArg(i):
'''Return the next command line argument (if there is one)'''
if ((i+1) >= len(sys.argv)):
Fatal("'%s' expected an argument" % sys.argv[i])
return(1, sys.argv[i+1])
### MAIN
if __name__=='__main__':
verbose = 0
debug = 0
infile = "infile"
outfile = "outfile"
# Parse command line
skip = 0
for i in range(1, len(sys.argv)):
if not skip:
if sys.argv[i][:2] == "-d": debug ^= 1
elif sys.argv[i][:2] == "-v": verbose ^= 1
elif sys.argv[i][:2] == "-i": (skip,infile) = NextArg(i)
elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
elif sys.argv[i][:2] == "-h": HelpAndExit()
elif sys.argv[i][:1] == "-": Fatal("'%s' unknown argument" % sys.argv[i])
else: Fatal("'%s' unexpected" % sys.argv[i])
else: skip = 0
print("%d,%d,%s,%s" % (debug,verbose,infile,outfile))
The goal of NextArg() is to return the next argument while checking for missing data, and 'skip' skips the loop when NextArg() is used, keeping the flag parsing down to one liners.
I extended Erco's approach to allow for required positional arguments and for optional arguments. These should precede the -d, -v etc. arguments.
Positional and optional arguments can be retrieved with PosArg(i) and OptArg(i, default) respectively.
When an optional argument is found the start position of searching for options (e.g. -i) is moved 1 ahead to avoid causing an 'unexpected' fatal.
import os,sys
def HelpAndExit():
print("<<your help output goes here>>")
sys.exit(1)
def Fatal(msg):
sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
sys.exit(1)
def NextArg(i):
'''Return the next command line argument (if there is one)'''
if ((i+1) >= len(sys.argv)):
Fatal("'%s' expected an argument" % sys.argv[i])
return(1, sys.argv[i+1])
def PosArg(i):
'''Return positional argument'''
if i >= len(sys.argv):
Fatal("'%s' expected an argument" % sys.argv[i])
return sys.argv[i]
def OptArg(i, default):
'''Return optional argument (if there is one)'''
if i >= len(sys.argv):
Fatal("'%s' expected an argument" % sys.argv[i])
if sys.argv[i][:1] != '-':
return True, sys.argv[i]
else:
return False, default
### MAIN
if __name__=='__main__':
verbose = 0
debug = 0
infile = "infile"
outfile = "outfile"
options_start = 3
# --- Parse two positional parameters ---
n1 = int(PosArg(1))
n2 = int(PosArg(2))
# --- Parse an optional parameters ---
present, a3 = OptArg(3,50)
n3 = int(a3)
options_start += int(present)
# --- Parse rest of command line ---
skip = 0
for i in range(options_start, len(sys.argv)):
if not skip:
if sys.argv[i][:2] == "-d": debug ^= 1
elif sys.argv[i][:2] == "-v": verbose ^= 1
elif sys.argv[i][:2] == "-i": (skip,infile) = NextArg(i)
elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
elif sys.argv[i][:2] == "-h": HelpAndExit()
elif sys.argv[i][:1] == "-": Fatal("'%s' unknown argument" % sys.argv[i])
else: Fatal("'%s' unexpected" % sys.argv[i])
else: skip = 0
print("Number 1 = %d" % n1)
print("Number 2 = %d" % n2)
print("Number 3 = %d" % n3)
print("Debug = %d" % debug)
print("verbose = %d" % verbose)
print("infile = %s" % infile)
print("outfile = %s" % outfile)