get option settings from ini-file. make getting configuration options from conftest.py only an internal feature.
--HG-- branch : trunk
This commit is contained in:
parent
1280041f0c
commit
b6ec5a575d
|
@ -5,22 +5,39 @@ Customizing and Extending py.test
|
|||
basic test configuration
|
||||
===================================
|
||||
|
||||
Command line options
|
||||
---------------------------------
|
||||
Command line options and configuration file settings
|
||||
-----------------------------------------------------------------
|
||||
|
||||
You can get help on options and configuration by running::
|
||||
You can get help on options and configuration options by running::
|
||||
|
||||
py.test -h # or --help
|
||||
py.test -h # prints options _and_ config file settings
|
||||
|
||||
This will display command line options, ini-settings and conftest.py
|
||||
settings in your specific environment.
|
||||
This will display command line and configuration file settings
|
||||
which were registered by installed plugins.
|
||||
|
||||
reading test configuration from ini-files
|
||||
how test configuration is read from setup/tox ini-files
|
||||
--------------------------------------------------------
|
||||
|
||||
py.test tries to find a configuration INI format file, trying
|
||||
to find a section ``[pytest]`` in a ``tox.ini`` (or XXX ``pytest.ini`` file).
|
||||
Possible entries in a ``[pytest]`` section are:
|
||||
py.test looks for the first ``[pytest]`` section in either the first ``setup.cfg`` or the first ``tox.ini`` file found upwards from the arguments. Example::
|
||||
|
||||
py.test path/to/testdir
|
||||
|
||||
will look in the following dirs for a config file::
|
||||
|
||||
path/to/testdir/setup.cfg
|
||||
path/to/setup.cfg
|
||||
path/setup.cfg
|
||||
setup.cfg
|
||||
... # up until root of filesystem
|
||||
path/to/testdir/tox.ini
|
||||
path/to/tox.ini
|
||||
path/tox.ini
|
||||
... # up until root of filesystem
|
||||
|
||||
If no path was provided at all the current working directory is used for the lookup.
|
||||
|
||||
builtin configuration file options
|
||||
----------------------------------------------
|
||||
|
||||
.. confval:: minversion = VERSTRING
|
||||
|
||||
|
@ -31,28 +48,14 @@ Possible entries in a ``[pytest]`` section are:
|
|||
.. confval:: addargs = OPTS
|
||||
|
||||
add the specified ``OPTS`` to the set of command line arguments as if they
|
||||
had been specified by the user. Example::
|
||||
had been specified by the user. Example: if you have this ini file content::
|
||||
|
||||
addargs = --maxfail=2 -rf # exit after 2 failures, report fail info
|
||||
[pytest]
|
||||
addargs = --maxfail=2 -rf # exit after 2 failures, report fail info
|
||||
|
||||
issuing ``py.test test_hello.py`` actually means::
|
||||
|
||||
setting persistent option defaults
|
||||
------------------------------------
|
||||
|
||||
py.test will lookup option values in this order:
|
||||
|
||||
* command line
|
||||
* ``[pytest]`` section in upwards ``setup.cfg`` or ``tox.ini`` files.
|
||||
* conftest.py files
|
||||
* environment variables
|
||||
|
||||
To get an overview on existing names and settings type::
|
||||
|
||||
py.test --help-config
|
||||
|
||||
This will print information about all available options
|
||||
in your environment, including your local plugins and
|
||||
command line options.
|
||||
py.test --maxfail=2 -rf test_hello.py
|
||||
|
||||
.. _`function arguments`: funcargs.html
|
||||
.. _`extensions`:
|
||||
|
@ -70,10 +73,8 @@ extensions and customizations close to test code.
|
|||
local conftest.py plugins
|
||||
--------------------------------------------------------------
|
||||
|
||||
local `conftest.py` plugins are usually automatically loaded and
|
||||
registered but its contained hooks are only called when collecting or
|
||||
running tests in files or directories next to or below the ``conftest.py``
|
||||
file. Assume the following layout and content of files::
|
||||
local ``conftest.py`` plugins contain directory-specific hook implemenations. Its contained runtest- and collection- related hooks are called when collecting or running tests in files or directories next to or below the ``conftest.py``
|
||||
file. Example: Assume the following layout and content of files::
|
||||
|
||||
a/conftest.py:
|
||||
def pytest_runtest_setup(item):
|
||||
|
@ -93,17 +94,18 @@ Here is how you might run it::
|
|||
py.test a/test_sub.py # will show "setting up"
|
||||
|
||||
``py.test`` loads all ``conftest.py`` files upwards from the command
|
||||
line file arguments. It usually looks up configuration values or hooks
|
||||
right-to-left, i.e. the closer conftest files are checked before
|
||||
the further away ones. This means you can have a ``conftest.py``
|
||||
in your home directory to provide global configuration values.
|
||||
line file arguments. It usually performs look up right-to-left, i.e.
|
||||
the hooks in "closer" conftest files will be called earlier than further
|
||||
away ones. This means you can even have a ``conftest.py`` file in your home
|
||||
directory to customize test functionality globally for all of your projects.
|
||||
|
||||
.. Note::
|
||||
if you have ``conftest.py`` files which do not reside in a
|
||||
If you have ``conftest.py`` files which do not reside in a
|
||||
python package directory (i.e. one containing an ``__init__.py``) then
|
||||
"import conftest" will be ambigous and should be avoided. If you
|
||||
ever want to import anything from a ``conftest.py`` file
|
||||
put it inside a package. You avoid trouble this way.
|
||||
"import conftest" can be ambigous because there might be other
|
||||
``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
|
||||
It is good practise for projects to put ``conftest.py`` within a package
|
||||
scope or to never import anything from the conftest.py file.
|
||||
|
||||
.. _`named plugins`: plugin/index.html
|
||||
|
||||
|
@ -115,9 +117,6 @@ py.test loads plugin modules at tool startup in the following way:
|
|||
|
||||
* by loading all plugins registered through `setuptools entry points`_.
|
||||
|
||||
* by reading the ``PYTEST_PLUGINS`` environment variable
|
||||
and importing the comma-separated list of named plugins.
|
||||
|
||||
* by pre-scanning the command line for the ``-p name`` option
|
||||
and loading the specified plugin before actual command line parsing.
|
||||
|
||||
|
@ -127,39 +126,17 @@ py.test loads plugin modules at tool startup in the following way:
|
|||
not loaded at tool startup.
|
||||
|
||||
* by recursively loading all plugins specified by the
|
||||
``pytest_plugins`` variable in a ``conftest.py`` file
|
||||
``pytest_plugins`` variable in ``conftest.py`` files
|
||||
|
||||
Requiring/Loading plugins in a test module or plugin
|
||||
-------------------------------------------------------------
|
||||
|
||||
You can specify plugins in a test module or a plugin like this::
|
||||
You can require plugins in a test module or a plugin like this::
|
||||
|
||||
pytest_plugins = "name1", "name2",
|
||||
|
||||
When the test module or plugin is loaded the specified plugins
|
||||
will be loaded. If you specify plugins without the ``pytest_``
|
||||
prefix it will be automatically added. All plugin names
|
||||
must be lowercase.
|
||||
|
||||
.. _`conftest.py plugin`:
|
||||
.. _`conftestplugin`:
|
||||
|
||||
Writing per-project plugins (conftest.py)
|
||||
------------------------------------------------------
|
||||
|
||||
The purpose of :file:`conftest.py` files is to allow project-specific
|
||||
test customization. They thus make for a good place to implement
|
||||
project-specific test related features through hooks. For example you may
|
||||
set the ``collect_ignore`` variable depending on a command line option
|
||||
by defining the following hook in a ``conftest.py`` file::
|
||||
|
||||
# ./conftest.py in your root or package dir
|
||||
collect_ignore = ['hello', 'test_world.py']
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--runall", action="store_true", default=False)
|
||||
def pytest_configure(config):
|
||||
if config.getvalue("runall"):
|
||||
collect_ignore[:] = []
|
||||
will be loaded.
|
||||
|
||||
.. _`setuptools entry points`:
|
||||
.. _registered:
|
||||
|
@ -357,10 +334,13 @@ Reference of important objects involved in hooks
|
|||
.. autoclass:: pytest.plugin.config.Parser
|
||||
:members:
|
||||
|
||||
.. autoclass:: pytest.plugin.session.File
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: pytest.plugin.session.Item
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: pytest.plugin.session.Node
|
||||
.. autoclass:: pytest.plugin.python.Function
|
||||
:members:
|
||||
|
||||
.. autoclass:: pytest.plugin.runner.CallInfo
|
||||
|
@ -370,30 +350,3 @@ Reference of important objects involved in hooks
|
|||
:members:
|
||||
|
||||
|
||||
|
||||
conftest.py configuration files
|
||||
=================================================
|
||||
|
||||
conftest.py reference docs
|
||||
|
||||
A unique feature of py.test are its ``conftest.py`` files which allow
|
||||
project and directory specific customizations to testing.
|
||||
|
||||
* `set option defaults`_
|
||||
|
||||
or set particular variables to influence the testing process:
|
||||
|
||||
* ``pytest_plugins``: list of named plugins to load
|
||||
|
||||
* ``collect_ignore``: list of paths to ignore during test collection, relative to the containing ``conftest.py`` file
|
||||
|
||||
* ``rsyncdirs``: list of to-be-rsynced directories for distributed
|
||||
testing, relative to the containing ``conftest.py`` file.
|
||||
|
||||
You may put a conftest.py files in your project root directory or into
|
||||
your package directory if you want to add project-specific test options.
|
||||
|
||||
|
||||
.. _`specify funcargs`: funcargs.html#application-setup-tutorial-example
|
||||
|
||||
.. _`set option defaults`:
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
import py
|
||||
failure_demo = py.path.local(__file__).dirpath('failure_demo.py')
|
||||
|
||||
pytest_plugins = "pytest_pytester"
|
||||
|
||||
def test_failure_demo_fails_properly(testdir):
|
||||
target = testdir.tmpdir.join(failure_demo.basename)
|
||||
failure_demo.copy(target)
|
||||
|
|
|
@ -11,7 +11,7 @@ def pytest_cmdline_parse(pluginmanager, args):
|
|||
return config
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addini('addargs', 'extra command line arguments')
|
||||
parser.addini('addargs', 'default command line arguments')
|
||||
parser.addini('minversion', 'minimally required pytest version')
|
||||
|
||||
class Parser:
|
||||
|
@ -72,14 +72,9 @@ class Parser:
|
|||
setattr(option, name, value)
|
||||
return args
|
||||
|
||||
def addini(self, name, description):
|
||||
def addini(self, name, description, type=None):
|
||||
""" add an ini-file option with the given name and description. """
|
||||
self._inidict[name] = description
|
||||
|
||||
def setfromini(self, inisection, option):
|
||||
for name, value in inisection.items():
|
||||
assert name in self._inidict
|
||||
return setattr(option, name, value)
|
||||
self._inidict[name] = (description, type)
|
||||
|
||||
class OptionGroup:
|
||||
def __init__(self, name, description="", parser=None):
|
||||
|
@ -330,7 +325,6 @@ class Config(object):
|
|||
self._preparse(args)
|
||||
self._parser.hints.extend(self.pluginmanager._hints)
|
||||
args = self._parser.parse_setoption(args, self.option)
|
||||
self._parser.setfromini(self.inicfg, self.option)
|
||||
if not args:
|
||||
args.append(py.std.os.getcwd())
|
||||
self.args = args
|
||||
|
@ -358,12 +352,26 @@ class Config(object):
|
|||
return py.path.local.make_numbered_dir(prefix=basename,
|
||||
keep=0, rootdir=basetemp, lock_timeout=None)
|
||||
|
||||
def getconftest_pathlist(self, name, path=None):
|
||||
""" return a matching value, which needs to be sequence
|
||||
of filenames that will be returned as a list of Path
|
||||
objects (they can be relative to the location
|
||||
where they were found).
|
||||
"""
|
||||
def getini(self, name):
|
||||
""" return configuration value from an ini file. """
|
||||
try:
|
||||
description, type = self._parser._inidict[name]
|
||||
except KeyError:
|
||||
raise ValueError("unknown configuration value: %r" %(name,))
|
||||
try:
|
||||
value = self.inicfg[name]
|
||||
except KeyError:
|
||||
return # None indicates nothing found
|
||||
if type == "pathlist":
|
||||
dp = py.path.local(self.inicfg.config.path).dirpath()
|
||||
l = []
|
||||
for relpath in py.std.shlex.split(value):
|
||||
l.append(dp.join(relpath, abs=True))
|
||||
return l
|
||||
else:
|
||||
return value
|
||||
|
||||
def _getconftest_pathlist(self, name, path=None):
|
||||
try:
|
||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||
except KeyError:
|
||||
|
@ -377,6 +385,21 @@ class Config(object):
|
|||
l.append(relroot)
|
||||
return l
|
||||
|
||||
def _getconftest(self, name, path=None, check=False):
|
||||
if check:
|
||||
self._checkconftest(name)
|
||||
return self._conftest.rget(name, path)
|
||||
|
||||
def getvalue(self, name, path=None):
|
||||
""" return 'name' value looked up from command line 'options'.
|
||||
(deprecated) if we can't find the option also lookup
|
||||
the name in a matching conftest file.
|
||||
"""
|
||||
try:
|
||||
return getattr(self.option, name)
|
||||
except AttributeError:
|
||||
return self._getconftest(name, path, check=False)
|
||||
|
||||
def getvalueorskip(self, name, path=None):
|
||||
""" return getvalue(name) or call py.test.skip if no value exists. """
|
||||
try:
|
||||
|
@ -387,17 +410,6 @@ class Config(object):
|
|||
except KeyError:
|
||||
py.test.skip("no %r value found" %(name,))
|
||||
|
||||
def getvalue(self, name, path=None):
|
||||
""" return 'name' value looked up from the 'options'
|
||||
and then from the first conftest file found up
|
||||
the path (including the path itself).
|
||||
if path is None, lookup the value in the initial
|
||||
conftest modules found during command line parsing.
|
||||
"""
|
||||
try:
|
||||
return getattr(self.option, name)
|
||||
except AttributeError:
|
||||
return self._conftest.rget(name, path)
|
||||
|
||||
def getcfg(args, inibasenames):
|
||||
if not args:
|
||||
|
|
|
@ -40,9 +40,8 @@ def showhelp(config):
|
|||
tw.write(config._parser.optparser.format_help())
|
||||
tw.line()
|
||||
tw.line()
|
||||
tw.sep( "=", "config file settings")
|
||||
tw.line("the following values can be defined in [pytest] sections of")
|
||||
tw.line("setup.cfg or tox.ini files:")
|
||||
#tw.sep( "=", "config file settings")
|
||||
tw.line("setup.cfg or tox.ini options to be put into [pytest] section:")
|
||||
tw.line()
|
||||
|
||||
for name, help in sorted(config._parser._inidict.items()):
|
||||
|
@ -50,21 +49,21 @@ def showhelp(config):
|
|||
tw.line(line[:tw.fullwidth])
|
||||
|
||||
tw.line() ; tw.line()
|
||||
#tw.sep( "=", "conftest.py settings")
|
||||
tw.line("the following values can be defined in conftest.py files")
|
||||
#tw.sep("=")
|
||||
return
|
||||
|
||||
tw.line("conftest.py options:")
|
||||
tw.line()
|
||||
for name, help in conftest_options:
|
||||
conftestitems = sorted(config._parser._conftestdict.items())
|
||||
for name, help in conftest_options + conftestitems:
|
||||
line = " %-15s %s" %(name, help)
|
||||
tw.line(line[:tw.fullwidth])
|
||||
tw.line()
|
||||
tw.sep( "=")
|
||||
#tw.sep( "=")
|
||||
|
||||
|
||||
conftest_options = (
|
||||
conftest_options = [
|
||||
('pytest_plugins', 'list of plugin names to load'),
|
||||
('collect_ignore', '(relative) paths ignored during collection'),
|
||||
('rsyncdirs', 'to-be-rsynced directories for dist-testing'),
|
||||
)
|
||||
]
|
||||
|
||||
def pytest_report_header(config):
|
||||
lines = []
|
||||
|
|
|
@ -248,6 +248,13 @@ class TmpTestdir:
|
|||
def makeconftest(self, source):
|
||||
return self.makepyfile(conftest=source)
|
||||
|
||||
def makeini(self, source):
|
||||
return self.makefile('.ini', tox=source)
|
||||
|
||||
def getinicfg(self, source):
|
||||
p = self.makeini(source)
|
||||
return py.iniconfig.IniConfig(p)['pytest']
|
||||
|
||||
def makepyfile(self, *args, **kwargs):
|
||||
return self._makefile('.py', args, kwargs)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import pytest
|
|||
import os, sys
|
||||
|
||||
def pytest_addoption(parser):
|
||||
|
||||
group = parser.getgroup("general", "running and selection options")
|
||||
group._addoption('-x', '--exitfirst', action="store_true", default=False,
|
||||
dest="exitfirst",
|
||||
|
@ -32,6 +33,7 @@ def pytest_addoption(parser):
|
|||
group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
|
||||
help="base temporary directory for this test run.")
|
||||
|
||||
|
||||
def pytest_namespace():
|
||||
return dict(collect=dict(Item=Item, Collector=Collector,
|
||||
File=File, Directory=Directory))
|
||||
|
@ -64,7 +66,7 @@ def pytest_runtest_mainloop(session):
|
|||
|
||||
def pytest_ignore_collect(path, config):
|
||||
p = path.dirpath()
|
||||
ignore_paths = config.getconftest_pathlist("collect_ignore", path=p)
|
||||
ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
|
||||
ignore_paths = ignore_paths or []
|
||||
excludeopt = config.getvalue("ignore")
|
||||
if excludeopt:
|
||||
|
@ -445,7 +447,6 @@ class Collector(Node):
|
|||
raise NotImplementedError("abstract")
|
||||
|
||||
def collect_by_name(self, name):
|
||||
""" return a child matching the given name, else None. """
|
||||
for colitem in self._memocollect():
|
||||
if colitem.name == name:
|
||||
return colitem
|
||||
|
|
|
@ -2,9 +2,6 @@ import py
|
|||
import sys
|
||||
|
||||
pytest_plugins = "pytester",
|
||||
collect_ignore = ['../build', '../doc/_build']
|
||||
|
||||
rsyncdirs = ['conftest.py', '../pytest', '../doc', '.']
|
||||
|
||||
import os, py
|
||||
pid = os.getpid()
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import py
|
||||
|
||||
pytest_plugins = "pytester"
|
||||
import pytest.plugin
|
||||
plugindir = py.path.local(pytest.plugin.__file__).dirpath()
|
||||
from pytest._core import default_plugins
|
||||
|
|
|
@ -15,8 +15,8 @@ def test_help(testdir):
|
|||
assert result.ret == 0
|
||||
result.stdout.fnmatch_lines([
|
||||
"*-v*verbose*",
|
||||
"*settings*",
|
||||
"*conftest.py*",
|
||||
"*setup.cfg*",
|
||||
"*minversion*",
|
||||
])
|
||||
|
||||
def test_collectattr():
|
||||
|
|
|
@ -37,7 +37,6 @@ class TestParseIni:
|
|||
])
|
||||
|
||||
class TestConfigCmdlineParsing:
|
||||
|
||||
def test_parsing_again_fails(self, testdir):
|
||||
config = testdir.reparseconfig([testdir.tmpdir])
|
||||
py.test.raises(AssertionError, "config.parse([])")
|
||||
|
@ -108,13 +107,43 @@ class TestConfigAPI:
|
|||
p = tmpdir.join("conftest.py")
|
||||
p.write("pathlist = ['.', %r]" % str(somepath))
|
||||
config = testdir.reparseconfig([p])
|
||||
assert config.getconftest_pathlist('notexist') is None
|
||||
pl = config.getconftest_pathlist('pathlist')
|
||||
assert config._getconftest_pathlist('notexist') is None
|
||||
pl = config._getconftest_pathlist('pathlist')
|
||||
print(pl)
|
||||
assert len(pl) == 2
|
||||
assert pl[0] == tmpdir
|
||||
assert pl[1] == somepath
|
||||
|
||||
def test_addini(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
def pytest_addoption(parser):
|
||||
parser.addini("myname", "my new ini value")
|
||||
""")
|
||||
testdir.makeini("""
|
||||
[pytest]
|
||||
myname=hello
|
||||
""")
|
||||
config = testdir.parseconfig()
|
||||
val = config.getini("myname")
|
||||
assert val == "hello"
|
||||
py.test.raises(ValueError, config.getini, 'other')
|
||||
|
||||
def test_addini_pathlist(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
def pytest_addoption(parser):
|
||||
parser.addini("paths", "my new ini value", type="pathlist")
|
||||
parser.addini("abc", "abc value")
|
||||
""")
|
||||
p = testdir.makeini("""
|
||||
[pytest]
|
||||
paths=hello world/sub.py
|
||||
""")
|
||||
config = testdir.parseconfig()
|
||||
l = config.getini("paths")
|
||||
assert len(l) == 2
|
||||
assert l[0] == p.dirpath('hello')
|
||||
assert l[1] == p.dirpath('world/sub.py')
|
||||
py.test.raises(ValueError, config.getini, 'other')
|
||||
|
||||
def test_options_on_small_file_do_not_blow_up(testdir):
|
||||
def runfiletest(opts):
|
||||
|
@ -151,3 +180,4 @@ def test_preparse_ordering(testdir, monkeypatch):
|
|||
config = testdir.parseconfig()
|
||||
plugin = config.pluginmanager.getplugin("mytestplugin")
|
||||
assert plugin.x == 42
|
||||
|
||||
|
|
|
@ -101,17 +101,6 @@ class TestParser:
|
|||
assert option.hello == "world"
|
||||
assert option.this == 42
|
||||
|
||||
def test_parser_addini(self, tmpdir):
|
||||
parser = parseopt.Parser()
|
||||
parser.addini("myname", "my new ini value")
|
||||
cfg = py.iniconfig.IniConfig("tox.ini", dedent("""
|
||||
[pytest]
|
||||
myname=hello
|
||||
"""))['pytest']
|
||||
class option:
|
||||
pass
|
||||
parser.setfromini(cfg, option)
|
||||
assert option.myname == "hello"
|
||||
|
||||
@py.test.mark.skipif("sys.version_info < (2,5)")
|
||||
def test_addoption_parser_epilog(testdir):
|
||||
|
|
Loading…
Reference in New Issue