diff --git a/AUTHORS b/AUTHORS index cce84a3c6..d8c431dc0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -127,6 +127,7 @@ Ted Xiao Thomas Grainger Tom Viner Trevor Bekolay +Tyler Goodlet Vasily Kuznetsov Wouter van Ackooy Xuecong Liao diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 61fb9b5b3..a67ec4eee 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,18 +16,21 @@ in Python 3 (`#1944`_). Thanks `@axil`_ for the PR. -* +* Explain a bad scope value passed to ``@fixture`` declarations or + a ``MetaFunc.parametrize()`` call. Thanks `@tgoodlet`_ for the PR. .. _@philpep: https://github.com/philpep .. _@raquel-ucl: https://github.com/raquel-ucl .. _@axil: https://github.com/axil +.. _@tgoodlet: https://github.com/tgoodlet .. _#1905: https://github.com/pytest-dev/pytest/issues/1905 .. _#1934: https://github.com/pytest-dev/pytest/issues/1934 .. _#1944: https://github.com/pytest-dev/pytest/issues/1944 + 3.0.2 ===== diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index cf3e9dd93..3f08b7c6d 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -599,12 +599,29 @@ class ScopeMismatchError(Exception): which has a lower scope (e.g. a Session one calls a function one) """ + scopes = "session module class function".split() scopenum_function = scopes.index("function") + + def scopemismatch(currentscope, newscope): return scopes.index(newscope) > scopes.index(currentscope) +def scope2index(scope, descr, where=None): + """Look up the index of ``scope`` and raise a descriptive value error + if not defined. + """ + try: + return scopes.index(scope) + except ValueError: + raise ValueError( + "{0} {1}has an unsupported scope value '{2}'".format( + descr, 'from {0} '.format(where) if where else '', + scope) + ) + + class FixtureLookupError(LookupError): """ could not return a requested Fixture (missing or invalid). """ def __init__(self, argname, request, msg=None): @@ -703,6 +720,7 @@ def call_fixture_func(fixturefunc, request, kwargs): res = fixturefunc(**kwargs) return res + class FixtureDef: """ A container for a factory definition. """ def __init__(self, fixturemanager, baseid, argname, func, scope, params, @@ -713,7 +731,11 @@ class FixtureDef: self.func = func self.argname = argname self.scope = scope - self.scopenum = scopes.index(scope or "function") + self.scopenum = scope2index( + scope or "function", + descr='fixture {0}'.format(func.__name__), + where=baseid + ) self.params = params startindex = unittest and 1 or None self.argnames = getfuncargnames(func, startindex=startindex) diff --git a/_pytest/python.py b/_pytest/python.py index f26c128ca..a361af874 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -770,7 +770,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. """ - from _pytest.fixtures import scopes + from _pytest.fixtures import scope2index from _pytest.mark import extract_argvalue from py.io import saferepr @@ -799,7 +799,8 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): if scope is None: scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) - scopenum = scopes.index(scope) + scopenum = scope2index( + scope, descr='call to {0}'.format(self.parametrize)) valtypes = {} for arg in argnames: if arg not in self.fixturenames: diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 5ff459cbc..d6b7840c6 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1063,6 +1063,22 @@ class TestFixtureUsages: "*1 error*" ]) + def test_invalid_scope(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="functions") + def badscope(): + pass + + def test_nothing(badscope): + pass + """) + result = testdir.runpytest_inprocess() + result.stdout.fnmatch_lines( + ("*ValueError: fixture badscope from test_invalid_scope.py has an unsupported" + " scope value 'functions'") + ) + def test_funcarg_parametrized_and_used_twice(self, testdir): testdir.makepyfile(""" import pytest diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index b9bf589c3..9d71df20a 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -96,6 +96,14 @@ class TestMetafunc: pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6])) pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6])) + def test_parametrize_bad_scope(self, testdir): + def func(x): pass + metafunc = self.Metafunc(func) + try: + metafunc.parametrize("x", [1], scope='doggy') + except ValueError as ve: + assert "has an unsupported scope value 'doggy'" in str(ve) + def test_parametrize_and_id(self): def func(x, y): pass metafunc = self.Metafunc(func)