deprecate markinfo and fix up most marker scoping access while completely breaking metafunc testing

This commit is contained in:
Ronny Pfannschmidt 2018-03-17 21:42:43 +01:00
parent e8feee0612
commit 180ae09202
8 changed files with 67 additions and 31 deletions

View File

@ -32,7 +32,8 @@ RESULT_LOG = (
)
MARK_INFO_ATTRIBUTE = RemovedInPytest4Warning(
"MarkInfo objects are deprecated as they contain the merged marks"
"MarkInfo objects are deprecated as they contain the merged marks.\n"
"Please use node.find_markers to iterate over markers correctly"
)
MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(

View File

@ -5,6 +5,7 @@ import inspect
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
from more_itertools import flatten
import attr
import py
@ -982,10 +983,10 @@ class FixtureManager(object):
argnames = getfuncargnames(func, cls=cls)
else:
argnames = ()
usefixtures = getattr(func, "usefixtures", None)
usefixtures = flatten(uf_mark.args for uf_mark in node.find_markers("usefixtures"))
initialnames = argnames
if usefixtures is not None:
initialnames = usefixtures.args + initialnames
initialnames = tuple(usefixtures) + initialnames
fm = node.session._fixturemanager
names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames,
node)
@ -1067,6 +1068,8 @@ class FixtureManager(object):
fixturedef = faclist[-1]
if fixturedef.params is not None:
parametrize_func = getattr(metafunc.function, 'parametrize', None)
if parametrize_func is not None:
parametrize_func = parametrize_func.combined
func_params = getattr(parametrize_func, 'args', [[None]])
func_kwargs = getattr(parametrize_func, 'kwargs', {})
# skip directly parametrized arguments

View File

@ -4,7 +4,7 @@ from operator import attrgetter
import inspect
import attr
from ..deprecated import MARK_PARAMETERSET_UNPACKING
from ..deprecated import MARK_PARAMETERSET_UNPACKING, MARK_INFO_ATTRIBUTE
from ..compat import NOTSET, getfslineno
from six.moves import map
@ -260,10 +260,10 @@ def _marked(func, mark):
invoked more than once.
"""
try:
func_mark = getattr(func, mark.name)
func_mark = getattr(func, getattr(mark, 'combined', mark).name)
except AttributeError:
return False
return mark.args == func_mark.args and mark.kwargs == func_mark.kwargs
return any(mark == info.combined for info in func_mark)
class MarkInfo(object):
@ -274,9 +274,9 @@ class MarkInfo(object):
self.combined = mark
self._marks = [mark]
name = alias('combined.name')
args = alias('combined.args')
kwargs = alias('combined.kwargs')
name = alias('combined.name', warning=MARK_INFO_ATTRIBUTE)
args = alias('combined.args', warning=MARK_INFO_ATTRIBUTE)
kwargs = alias('combined.kwargs', warning=MARK_INFO_ATTRIBUTE)
def __repr__(self):
return "<MarkInfo {0!r}>".format(self.combined)

View File

@ -117,11 +117,7 @@ def pytest_generate_tests(metafunc):
if hasattr(metafunc.function, attr):
msg = "{0} has '{1}', spelling should be 'parametrize'"
raise MarkerError(msg.format(metafunc.function.__name__, attr))
try:
markers = metafunc.function.parametrize
except AttributeError:
return
for marker in markers:
for marker in metafunc.definition.find_markers('parametrize'):
metafunc.parametrize(*marker.args, **marker.kwargs)
@ -212,6 +208,7 @@ class PyobjContext(object):
class PyobjMixin(PyobjContext):
_ALLOW_MARKERS = True
def __init__(self, *k, **kw):
super(PyobjMixin, self).__init__(*k, **kw)
@ -221,8 +218,9 @@ class PyobjMixin(PyobjContext):
obj = getattr(self, '_obj', None)
if obj is None:
self._obj = obj = self._getobj()
# XXX evil hacn
self._markers.update(get_unpacked_marks(self.obj))
# XXX evil hack
if self._ALLOW_MARKERS:
self._markers.update(get_unpacked_marks(self.obj))
return obj
def fset(self, value):
@ -370,8 +368,13 @@ class PyCollector(PyobjMixin, nodes.Collector):
transfer_markers(funcobj, cls, module)
fm = self.session._fixturemanager
fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
metafunc = Metafunc(funcobj, fixtureinfo, self.config,
cls=cls, module=module)
definition = FunctionDefinition(
name=name,
parent=self,
callobj=funcobj,
)
metafunc = Metafunc(definition, fixtureinfo, self.config, cls=cls, module=module)
methods = []
if hasattr(module, "pytest_generate_tests"):
methods.append(module.pytest_generate_tests)
@ -530,6 +533,8 @@ class Class(PyCollector):
class Instance(PyCollector):
_ALLOW_MARKERS = False # hack, destroy later
def _getobj(self):
return self.parent.obj()
@ -729,15 +734,17 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
test function is defined.
"""
def __init__(self, function, fixtureinfo, config, cls=None, module=None):
def __init__(self, definition, fixtureinfo, config, cls=None, module=None):
#: access to the :class:`_pytest.config.Config` object for the test session
assert isinstance(definition, FunctionDefinition)
self.definition = definition
self.config = config
#: the module object where the test function is defined in.
self.module = module
#: underlying python test function
self.function = function
self.function = definition.obj
#: set of fixture names required by the test function
self.fixturenames = fixtureinfo.names_closure
@ -1189,3 +1196,15 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
def setup(self):
super(Function, self).setup()
fixtures.fillfixtures(self)
class FunctionDefinition(Function):
"""
internal hack until we get actual definition nodes instead of the
crappy metafunc hack
"""
def runtest(self):
raise RuntimeError("function definitions are not supposed to be used")
setup = runtest

View File

@ -2,7 +2,6 @@
from __future__ import absolute_import, division, print_function
from _pytest.config import hookimpl
from _pytest.mark import MarkInfo, MarkDecorator
from _pytest.mark.evaluate import MarkEvaluator
from _pytest.outcomes import fail, skip, xfail
@ -60,15 +59,12 @@ def pytest_configure(config):
def pytest_runtest_setup(item):
# Check if skip or skipif are specified as pytest marks
item._skipped_by_mark = False
skipif_info = item.keywords.get('skipif')
if isinstance(skipif_info, (MarkInfo, MarkDecorator)):
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._skipped_by_mark = True
skip(eval_skipif.getexplanation())
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._skipped_by_mark = True
skip(eval_skipif.getexplanation())
skip_info = item.keywords.get('skip')
if isinstance(skip_info, (MarkInfo, MarkDecorator)):
for skip_info in item.find_markers('skip'):
item._skipped_by_mark = True
if 'reason' in skip_info.kwargs:
skip(skip_info.kwargs['reason'])

View File

@ -1781,6 +1781,8 @@ class TestAutouseManagement(object):
import pytest
values = []
def pytest_generate_tests(metafunc):
if metafunc.cls is None:
assert metafunc.function is test_finish
if metafunc.cls is not None:
metafunc.parametrize("item", [1,2], scope="class")
class TestClass(object):
@ -1798,7 +1800,7 @@ class TestAutouseManagement(object):
assert values == ["setup-1", "step1-1", "step2-1", "teardown-1",
"setup-2", "step1-2", "step2-2", "teardown-2",]
""")
reprec = testdir.inline_run()
reprec = testdir.inline_run('-s')
reprec.assertoutcome(passed=5)
def test_ordering_autouse_before_explicit(self, testdir):

View File

@ -26,11 +26,17 @@ class TestMetafunc(object):
names = fixtures.getfuncargnames(func)
fixtureinfo = FixtureInfo(names)
return python.Metafunc(func, fixtureinfo, config)
definition = python.FunctionDefinition(
name=func.__name__,
parent=None,
callobj=func,
)
return python.Metafunc(definition, fixtureinfo, config)
def test_no_funcargs(self, testdir):
def function():
pass
metafunc = self.Metafunc(function)
assert not metafunc.fixturenames
repr(metafunc._calls)

View File

@ -8,6 +8,8 @@ from _pytest.mark import (
EMPTY_PARAMETERSET_OPTION,
)
ignore_markinfo = pytest.mark.filterwarnings('ignore:MarkInfo objects:_pytest.deprecated.RemovedInPytest4Warning')
class TestMark(object):
def test_markinfo_repr(self):
@ -51,6 +53,7 @@ class TestMark(object):
mark.hello(f)
assert f.hello
@ignore_markinfo
def test_pytest_mark_keywords(self):
mark = Mark()
@ -62,6 +65,7 @@ class TestMark(object):
assert f.world.kwargs['x'] == 3
assert f.world.kwargs['y'] == 4
@ignore_markinfo
def test_apply_multiple_and_merge(self):
mark = Mark()
@ -78,6 +82,7 @@ class TestMark(object):
assert f.world.kwargs['y'] == 1
assert len(f.world.args) == 0
@ignore_markinfo
def test_pytest_mark_positional(self):
mark = Mark()
@ -88,6 +93,7 @@ class TestMark(object):
assert f.world.args[0] == "hello"
mark.world("world")(f)
@ignore_markinfo
def test_pytest_mark_positional_func_and_keyword(self):
mark = Mark()
@ -103,6 +109,7 @@ class TestMark(object):
assert g.world.args[0] is f
assert g.world.kwargs["omega"] == "hello"
@ignore_markinfo
def test_pytest_mark_reuse(self):
mark = Mark()
@ -484,6 +491,7 @@ class TestFunctional(object):
assert 'hello' in keywords
assert 'world' in keywords
@ignore_markinfo
def test_merging_markers(self, testdir):
p = testdir.makepyfile("""
import pytest
@ -621,6 +629,7 @@ class TestFunctional(object):
"keyword: *hello*"
])
@ignore_markinfo
def test_merging_markers_two_functions(self, testdir):
p = testdir.makepyfile("""
import pytest