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:
holger krekel 2010-10-31 17:41:58 +01:00
parent 1280041f0c
commit b6ec5a575d
12 changed files with 145 additions and 159 deletions

View File

@ -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`:

View File

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

View File

@ -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:

View File

@ -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 = []

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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):

View File

@ -51,3 +51,4 @@ commands=
[pytest]
minversion=2.0
plugins=pytester