Skip to content

Commit

Permalink
Support [tool:pytest] in setup.cfg files
Browse files Browse the repository at this point in the history
Also deprecate [pytest] usage in setup.cfg files

Fix pytest-dev#567
  • Loading branch information
nicoddemus committed Aug 17, 2016
1 parent d3b8551 commit b792384
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 33 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ time or change existing behaviors in order to make them less surprising/more use
* ``yield``-based tests are considered deprecated and will be removed in pytest-4.0.
Thanks `@nicoddemus`_ for the PR.

* ``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
to avoid conflicts with other distutils commands (see `#567`_). ``[pytest]`` in
``pytest.ini`` or ``tox.ini`` files are supported and unchanged.
Thanks `@nicoddemus`_ for the PR.

* Using ``pytest_funcarg__`` prefix to declare fixtures is considered deprecated and will be
removed in pytest-4.0 (`#1684`_).
Thanks `@nicoddemus`_ for the PR.
Expand Down Expand Up @@ -374,6 +379,7 @@ time or change existing behaviors in order to make them less surprising/more use
.. _#372: https://github.com/pytest-dev/pytest/issues/372
.. _#457: https://github.com/pytest-dev/pytest/issues/457
.. _#460: https://github.com/pytest-dev/pytest/pull/460
.. _#567: https://github.com/pytest-dev/pytest/pull/567
.. _#607: https://github.com/pytest-dev/pytest/issues/607
.. _#634: https://github.com/pytest-dev/pytest/issues/634
.. _#717: https://github.com/pytest-dev/pytest/issues/717
Expand Down
27 changes: 20 additions & 7 deletions _pytest/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ def pytest_load_initial_conftests(self, early_config):

def _initini(self, args):
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args)
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info['rootdir'] = self.rootdir
self._parser.extra_info['inifile'] = self.inifile
Expand Down Expand Up @@ -1154,7 +1154,18 @@ def exists(path, ignore=EnvironmentError):
except ignore:
return False

def getcfg(args, inibasenames):
def getcfg(args, warnfunc=None):
"""
Search the list of arguments for a valid ini-file for pytest,
and return a tuple of (rootdir, inifile, cfg-dict).
note: warnfunc is an optional function used to warn
about ini-files that use deprecated features.
This parameter should be removed when pytest
adopts standard deprecation warnings (#1804).
"""
from _pytest.deprecated import SETUP_CFG_PYTEST
inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
args = [x for x in args if not str(x).startswith("-")]
if not args:
args = [py.path.local()]
Expand All @@ -1166,7 +1177,11 @@ def getcfg(args, inibasenames):
if exists(p):
iniconfig = py.iniconfig.IniConfig(p)
if 'pytest' in iniconfig.sections:
if inibasename == 'setup.cfg' and warnfunc:
warnfunc('C1', SETUP_CFG_PYTEST)
return base, p, iniconfig['pytest']
if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
return base, p, iniconfig['tool:pytest']
elif inibasename == "pytest.ini":
# allowed to be empty
return base, p, {}
Expand Down Expand Up @@ -1207,7 +1222,7 @@ def get_dirs_from_args(args):
if d.exists()]


def determine_setup(inifile, args):
def determine_setup(inifile, args, warnfunc=None):
dirs = get_dirs_from_args(args)
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
Expand All @@ -1218,15 +1233,13 @@ def determine_setup(inifile, args):
rootdir = get_common_ancestor(dirs)
else:
ancestor = get_common_ancestor(dirs)
rootdir, inifile, inicfg = getcfg(
[ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
if rootdir is None:
for rootdir in ancestor.parts(reverse=True):
if rootdir.join("setup.py").exists():
break
else:
rootdir, inifile, inicfg = getcfg(
dirs, ["pytest.ini", "tox.ini", "setup.cfg"])
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
if rootdir is None:
rootdir = get_common_ancestor([py.path.local(), ancestor])
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
Expand Down
2 changes: 2 additions & 0 deletions _pytest/deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@
'and scheduled to be removed in pytest 4.0. '
'Please remove the prefix and use the @pytest.fixture decorator instead.')

SETUP_CFG_PYTEST = '[pytest] section in setup.cfg files is deprecated, use [tool:pytest] instead.'

GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue"

1 change: 0 additions & 1 deletion _pytest/helpconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def showhelp(config):
tw.write(config._parser.optparser.format_help())
tw.line()
tw.line()
#tw.sep( "=", "config file settings")
tw.line("[pytest] ini-options in the next "
"pytest.ini|tox.ini|setup.cfg file:")
tw.line()
Expand Down
6 changes: 3 additions & 3 deletions doc/en/customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Here is the algorithm which finds the rootdir from ``args``:

Note that an existing ``pytest.ini`` file will always be considered a match,
whereas ``tox.ini`` and ``setup.cfg`` will only match if they contain a
``[pytest]`` section. Options from multiple ini-files candidates are never
``[pytest]`` or ``[tool:pytest]`` section, respectively. Options from multiple ini-files candidates are never
merged - the first one wins (``pytest.ini`` always wins, even if it does not
contain a ``[pytest]`` section).

Expand All @@ -73,7 +73,7 @@ check for ini-files as follows::

# first look for pytest.ini files
path/pytest.ini
path/setup.cfg # must also contain [pytest] section to match
path/setup.cfg # must also contain [tool:pytest] section to match
path/tox.ini # must also contain [pytest] section to match
pytest.ini
... # all the way down to the root
Expand Down Expand Up @@ -154,7 +154,7 @@ Builtin configuration file options

.. code-block:: ini
# content of setup.cfg
# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*
Expand Down
9 changes: 5 additions & 4 deletions doc/en/example/pythoncollection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ Example::
Changing directory recursion
-----------------------------------------------------

You can set the :confval:`norecursedirs` option in an ini-file, for example your ``setup.cfg`` in the project root directory::
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory::

# content of setup.cfg
# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*

Expand All @@ -94,8 +94,9 @@ You can configure different naming conventions by setting
the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` configuration options. Example::

# content of setup.cfg
# can also be defined in in tox.ini or pytest.ini file
# content of pytest.ini
# can also be defined in in tox.ini or setup.cfg file, although the section
# name in setup.cfg files should be "tool:pytest"
[pytest]
python_files=check_*.py
python_classes=Check
Expand Down
18 changes: 18 additions & 0 deletions doc/en/goodpractices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,24 @@ required for calling the test command. You can also pass additional
arguments to pytest such as your test directory or other
options using ``--addopts``.

You can also specify other pytest-ini options in your ``setup.cfg`` file
by putting them into a ``[tool:pytest]`` version:

.. code-block:: ini
[tool:pytest]
addopts = --verbose
python_files = testing/*/*.py
.. note::
Prior to 3.0, the supported section name was ``[pytest]``. Due to how
this conflicts may collide with some distutils commands, the recommended
section name for ``setup.cfg`` files is now ``[tool:pytest]``.

Note that for ``pytest.ini`` and ``tox.ini`` files the section
name is ``[pytest]``.


Manual Integration
^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 2 additions & 2 deletions doc/en/xdist.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ and ``pytest`` will run your tests. Assuming you have failures it will then
wait for file changes and re-run the failing test set. File changes are detected by looking at ``looponfailingroots`` root directories and all of their contents (recursively). If the default for this value does not work for you you
can change it in your project by setting a configuration option::

# content of a pytest.ini, setup.cfg or tox.ini file
# content of a pytest.ini or tox.ini file
[pytest]
looponfailroots = mypkg testdir

Expand Down Expand Up @@ -181,7 +181,7 @@ to run tests in each of the environments.
Specifying "rsync" dirs in an ini-file
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

In a ``tox.ini`` or ``setup.cfg`` file in your root project directory
In a ``pytest.ini`` or ``tox.ini`` file in your root project directory
you may specify directories to include or to exclude in synchronisation::

[pytest]
Expand Down
9 changes: 9 additions & 0 deletions testing/deprecated_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ def test_funcarg_prefix(value):
])


def test_pytest_setup_cfg_deprecated(testdir):
testdir.makefile('.cfg', setup='''
[pytest]
addopts = --verbose
''')
result = testdir.runpytest()
result.stdout.fnmatch_lines(['*pytest*section in setup.cfg files is deprecated*use*tool:pytest*instead*'])


def test_str_args_deprecated(tmpdir, testdir):
"""Deprecate passing strings to pytest.main(). Scheduled for removal in pytest-4.0."""
from _pytest.main import EXIT_NOTESTSCOLLECTED
Expand Down
36 changes: 20 additions & 16 deletions testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@
from _pytest.main import EXIT_NOTESTSCOLLECTED

class TestParseIni:
def test_getcfg_and_config(self, testdir, tmpdir):

@pytest.mark.parametrize('section, filename',
[('pytest', 'pytest.ini'), ('tool:pytest', 'setup.cfg')])
def test_getcfg_and_config(self, testdir, tmpdir, section, filename):
sub = tmpdir.mkdir("sub")
sub.chdir()
tmpdir.join("setup.cfg").write(_pytest._code.Source("""
[pytest]
tmpdir.join(filename).write(_pytest._code.Source("""
[{section}]
name = value
"""))
rootdir, inifile, cfg = getcfg([sub], ["setup.cfg"])
""".format(section=section)))
rootdir, inifile, cfg = getcfg([sub])
assert cfg['name'] == "value"
config = testdir.parseconfigure(sub)
assert config.inicfg['name'] == 'value'

def test_getcfg_empty_path(self, tmpdir):
getcfg([''], ['setup.cfg']) #happens on pytest ""
def test_getcfg_empty_path(self):
"""correctly handle zero length arguments (a la pytest '')"""
getcfg([''])

def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
monkeypatch.setenv('PYTEST_ADDOPTS', '--color no -rs --tb="short"')
tmpdir.join("setup.cfg").write(_pytest._code.Source("""
tmpdir.join("pytest.ini").write(_pytest._code.Source("""
[pytest]
addopts = --verbose
"""))
Expand All @@ -31,10 +35,6 @@ def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
assert config.option.reportchars == 's'
assert config.option.tbstyle == 'short'
assert config.option.verbose
#config = testdir.Config()
#args = [tmpdir,]
#config._preparse(args, addopts=False)
#assert len(args) == 1

def test_tox_ini_wrong_version(self, testdir):
testdir.makefile('.ini', tox="""
Expand All @@ -47,12 +47,16 @@ def test_tox_ini_wrong_version(self, testdir):
"*tox.ini:2*requires*9.0*actual*"
])

@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
def test_ini_names(self, testdir, name):
@pytest.mark.parametrize("section, name", [
('tool:pytest', 'setup.cfg'),
('pytest', 'tox.ini'),
('pytest', 'pytest.ini')],
)
def test_ini_names(self, testdir, name, section):
testdir.tmpdir.join(name).write(py.std.textwrap.dedent("""
[pytest]
[{section}]
minversion = 1.0
"""))
""".format(section=section)))
config = testdir.parseconfig()
assert config.getini("minversion") == "1.0"

Expand Down

0 comments on commit b792384

Please sign in to comment.