Raise an error if pytest_plugins is defined in a non-top-level conftest.py file

Fix #4548
This commit is contained in:
Bruno Oliveira 2018-12-19 18:09:47 -02:00
parent 9138419379
commit a93f41233a
5 changed files with 34 additions and 32 deletions

View File

@ -0,0 +1,3 @@
An error is now raised if the ``pytest_plugins`` variable is defined in a non-top-level ``conftest.py`` file (i.e., not residing in the ``rootdir``).
See our `docs <https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files>`__ for more information.

View File

@ -81,16 +81,6 @@ As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` i
:ref:`the documentation <update marker code>` on tips on how to update your code. :ref:`the documentation <update marker code>` on tips on how to update your code.
pytest_plugins in non-top-level conftest files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.5
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
features ``conftest.py`` files are only *active* for tests at or below it.
marks in ``pytest.mark.parametrize`` marks in ``pytest.mark.parametrize``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -242,6 +232,16 @@ You can consult `funcarg comparison section in the docs <https://docs.pytest.org
more information. more information.
pytest_plugins in non-top-level conftest files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
features ``conftest.py`` files are only *active* for tests at or below it.
``Config.warn`` and ``Node.warn`` ``Config.warn`` and ``Node.warn``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -31,6 +31,7 @@ from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback from _pytest._code import filter_traceback
from _pytest.compat import lru_cache from _pytest.compat import lru_cache
from _pytest.compat import safe_str from _pytest.compat import safe_str
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning
@ -429,11 +430,11 @@ class PytestPluginManager(PluginManager):
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
) )
warnings.warn_explicit( fail(
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST, PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST.format(
category=None, conftestpath, self._confcutdir
filename=str(conftestpath), ),
lineno=0, pytrace=False,
) )
except Exception: except Exception:
raise ConftestImportFailure(conftestpath, sys.exc_info()) raise ConftestImportFailure(conftestpath, sys.exc_info())

View File

@ -69,10 +69,14 @@ WARNS_EXEC = PytestDeprecationWarning(
"See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec" "See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec"
) )
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = RemovedInPytest4Warning( PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = (
"Defining pytest_plugins in a non-top-level conftest is deprecated, " "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported "
"because it affects the entire directory tree in a non-explicit way.\n" "because it affects the entire directory tree in a non-explicit way.\n"
"Please move it to the top level conftest file instead." " {}\n"
"Please move it to a top level conftest file at the rootdir:\n"
" {}\n"
"For more information, visit:\n"
" https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files"
) )
PYTEST_CONFIG_GLOBAL = PytestDeprecationWarning( PYTEST_CONFIG_GLOBAL = PytestDeprecationWarning(

View File

@ -113,17 +113,15 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir):
""" """
) )
res = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) res = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG)
assert res.ret == 0 assert res.ret == 2
msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0]
res.stdout.fnmatch_lines( res.stdout.fnmatch_lines(
"*subdirectory{sep}conftest.py:0: RemovedInPytest4Warning: {msg}*".format( ["*{msg}*".format(msg=msg), "*subdirectory{sep}conftest.py*".format(sep=os.sep)]
sep=os.sep, msg=msg
)
) )
@pytest.mark.parametrize("use_pyargs", [True, False]) @pytest.mark.parametrize("use_pyargs", [True, False])
def test_pytest_plugins_in_non_top_level_conftest_deprecated_pyargs( def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs(
testdir, use_pyargs testdir, use_pyargs
): ):
"""When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)""" """When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)"""
@ -143,7 +141,7 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_pyargs(
args = ("--pyargs", "pkg") if use_pyargs else () args = ("--pyargs", "pkg") if use_pyargs else ()
args += (SHOW_PYTEST_WARNINGS_ARG,) args += (SHOW_PYTEST_WARNINGS_ARG,)
res = testdir.runpytest(*args) res = testdir.runpytest(*args)
assert res.ret == 0 assert res.ret == (0 if use_pyargs else 2)
msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0]
if use_pyargs: if use_pyargs:
assert msg not in res.stdout.str() assert msg not in res.stdout.str()
@ -151,7 +149,7 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_pyargs(
res.stdout.fnmatch_lines("*{msg}*".format(msg=msg)) res.stdout.fnmatch_lines("*{msg}*".format(msg=msg))
def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_top_level_conftest( def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest(
testdir testdir
): ):
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
@ -160,8 +158,6 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_top_level_confte
subdirectory.mkdir() subdirectory.mkdir()
testdir.makeconftest( testdir.makeconftest(
""" """
import warnings
warnings.filterwarnings('always', category=DeprecationWarning)
pytest_plugins=['capture'] pytest_plugins=['capture']
""" """
) )
@ -175,16 +171,14 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_top_level_confte
) )
res = testdir.runpytest_subprocess() res = testdir.runpytest_subprocess()
assert res.ret == 0 assert res.ret == 2
msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0]
res.stdout.fnmatch_lines( res.stdout.fnmatch_lines(
"*subdirectory{sep}conftest.py:0: RemovedInPytest4Warning: {msg}*".format( ["*{msg}*".format(msg=msg), "*subdirectory{sep}conftest.py*".format(sep=os.sep)]
sep=os.sep, msg=msg
)
) )
def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_false_positives( def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives(
testdir testdir
): ):
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST