Skip to content

Commit

Permalink
deprecate markinfo and fix up most marker scoping access while comple…
Browse files Browse the repository at this point in the history
…tely breaking metafunc testing
  • Loading branch information
RonnyPfannschmidt committed Mar 29, 2018
1 parent e8feee0 commit 180ae09
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 32 deletions.
3 changes: 2 additions & 1 deletion _pytest/deprecated.py
Expand Up @@ -32,7 +32,8 @@ class RemovedInPytest4Warning(DeprecationWarning):
)

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(
Expand Down
7 changes: 5 additions & 2 deletions _pytest/fixtures.py
Expand Up @@ -5,6 +5,7 @@
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
from more_itertools import flatten

import attr
import py
Expand Down Expand Up @@ -982,10 +983,10 @@ def getfixtureinfo(self, node, func, cls, funcargs=True):
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)
Expand Down Expand Up @@ -1067,6 +1068,8 @@ def pytest_generate_tests(self, metafunc):
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
Expand Down
12 changes: 6 additions & 6 deletions _pytest/mark/structures.py
Expand Up @@ -4,7 +4,7 @@
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

Expand Down Expand Up @@ -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):
Expand All @@ -274,9 +274,9 @@ def __init__(self, mark):
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)
Expand Down
41 changes: 30 additions & 11 deletions _pytest/python.py
Expand Up @@ -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)


Expand Down Expand Up @@ -212,6 +208,7 @@ class PyobjContext(object):


class PyobjMixin(PyobjContext):
_ALLOW_MARKERS = True

def __init__(self, *k, **kw):
super(PyobjMixin, self).__init__(*k, **kw)
Expand All @@ -221,8 +218,9 @@ def fget(self):
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):
Expand Down Expand Up @@ -370,8 +368,13 @@ def _genfunctions(self, name, funcobj):
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)
Expand Down Expand Up @@ -530,6 +533,8 @@ def setup(self):


class Instance(PyCollector):
_ALLOW_MARKERS = False # hack, destroy later

def _getobj(self):
return self.parent.obj()

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1189,3 +1196,15 @@ def runtest(self):
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
16 changes: 6 additions & 10 deletions _pytest/skipping.py
Expand Up @@ -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

Expand Down Expand Up @@ -60,15 +59,12 @@ def nop(*args, **kwargs):
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())

skip_info = item.keywords.get('skip')
if isinstance(skip_info, (MarkInfo, MarkDecorator)):
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._skipped_by_mark = True
skip(eval_skipif.getexplanation())

for skip_info in item.find_markers('skip'):
item._skipped_by_mark = True
if 'reason' in skip_info.kwargs:
skip(skip_info.kwargs['reason'])
Expand Down
4 changes: 3 additions & 1 deletion testing/python/fixture.py
Expand Up @@ -1781,6 +1781,8 @@ def test_parametrization_setup_teardown_ordering(self, testdir):
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):
Expand All @@ -1798,7 +1800,7 @@ def test_finish():
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):
Expand Down
8 changes: 7 additions & 1 deletion testing/python/metafunc.py
Expand Up @@ -26,11 +26,17 @@ def __init__(self, names):

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)
Expand Down
9 changes: 9 additions & 0 deletions testing/test_mark.py
Expand Up @@ -8,6 +8,8 @@
EMPTY_PARAMETERSET_OPTION,
)

ignore_markinfo = pytest.mark.filterwarnings('ignore:MarkInfo objects:_pytest.deprecated.RemovedInPytest4Warning')


class TestMark(object):
def test_markinfo_repr(self):
Expand Down Expand Up @@ -51,6 +53,7 @@ def f():
mark.hello(f)
assert f.hello

@ignore_markinfo
def test_pytest_mark_keywords(self):
mark = Mark()

Expand All @@ -62,6 +65,7 @@ def f():
assert f.world.kwargs['x'] == 3
assert f.world.kwargs['y'] == 4

@ignore_markinfo
def test_apply_multiple_and_merge(self):
mark = Mark()

Expand All @@ -78,6 +82,7 @@ def f():
assert f.world.kwargs['y'] == 1
assert len(f.world.args) == 0

@ignore_markinfo
def test_pytest_mark_positional(self):
mark = Mark()

Expand All @@ -88,6 +93,7 @@ def f():
assert f.world.args[0] == "hello"
mark.world("world")(f)

@ignore_markinfo
def test_pytest_mark_positional_func_and_keyword(self):
mark = Mark()

Expand All @@ -103,6 +109,7 @@ def g():
assert g.world.args[0] is f
assert g.world.kwargs["omega"] == "hello"

@ignore_markinfo
def test_pytest_mark_reuse(self):
mark = Mark()

Expand Down Expand Up @@ -484,6 +491,7 @@ def test_func(self):
assert 'hello' in keywords
assert 'world' in keywords

@ignore_markinfo
def test_merging_markers(self, testdir):
p = testdir.makepyfile("""
import pytest
Expand Down Expand Up @@ -621,6 +629,7 @@ def test_func(arg):
"keyword: *hello*"
])

@ignore_markinfo
def test_merging_markers_two_functions(self, testdir):
p = testdir.makepyfile("""
import pytest
Expand Down

0 comments on commit 180ae09

Please sign in to comment.