diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index 27c2b2c5c..6190dea01 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -983,7 +983,7 @@ class FixtureManager(object): argnames = getfuncargnames(func, cls=cls) else: argnames = () - usefixtures = flatten(uf_mark.args for uf_mark in node.find_markers("usefixtures")) + usefixtures = flatten(mark.args for mark in node.iter_markers() if mark.name == "usefixtures") initialnames = argnames initialnames = tuple(usefixtures) + initialnames fm = node.session._fixturemanager diff --git a/_pytest/mark/evaluate.py b/_pytest/mark/evaluate.py index 19eff8e00..c89b4933a 100644 --- a/_pytest/mark/evaluate.py +++ b/_pytest/mark/evaluate.py @@ -35,7 +35,7 @@ class MarkEvaluator(object): return not hasattr(self, 'exc') def _get_marks(self): - return list(self.item.find_markers(self._mark_name)) + return [x for x in self.item.iter_markers() if x.name == self._mark_name] def invalidraise(self, exc): raises = self.get('raises') diff --git a/_pytest/nodes.py b/_pytest/nodes.py index 215da0825..799ee078a 100644 --- a/_pytest/nodes.py +++ b/_pytest/nodes.py @@ -1,8 +1,6 @@ from __future__ import absolute_import, division, print_function import os -from itertools import chain -from operator import itemgetter import six import py import attr @@ -10,7 +8,7 @@ import attr import _pytest import _pytest._code -from _pytest.mark.structures import NodeKeywords, NodeMarkers, MarkInfo +from _pytest.mark.structures import NodeKeywords, MarkInfo SEP = "/" @@ -91,7 +89,9 @@ class Node(object): #: keywords/markers collected from all scopes self.keywords = NodeKeywords(self) - self._markers = NodeMarkers() + + #: the marker objects belonging to this node + self.own_markers = [] #: allow adding of extra keywords to use for matching self.extra_keyword_matches = set() @@ -181,30 +181,13 @@ class Node(object): elif not isinstance(marker, MarkDecorator): raise ValueError("is not a string or pytest.mark.* Marker") self.keywords[marker.name] = marker - self._markers.update([marker]) - - def find_markers(self, name): - """find all marks with the given name on the node and its parents - - :param str name: name of the marker - :returns: iterator over marks matching the name""" - return map(itemgetter(1), self.find_markers_with_node(name)) - - def find_markers_with_node(self, name): - """find all marks with the given name on the node and its parents - - :param str name: name of the marker - :returns: iterator over (node, mark) matching the name - """ - for node in reversed(self.listchain()): - for mark in node._markers.find(name): - yield node, mark + self.own_markers.append(marker) def iter_markers(self): """ iterate over all markers of the node """ - return chain.from_iterable(x._markers for x in reversed(self.listchain())) + return (x[1] for x in self.iter_markers_with_node()) def iter_markers_with_node(self): """ @@ -212,7 +195,7 @@ class Node(object): returns sequence of tuples (node, mark) """ for node in reversed(self.listchain()): - for mark in node._markers: + for mark in node.own_markers: yield node, mark def get_marker(self, name): @@ -223,7 +206,7 @@ class Node(object): deprecated """ - markers = list(self.find_markers(name)) + markers = [x for x in self.iter_markers() if x.name == name] if markers: return MarkInfo(markers) diff --git a/_pytest/python.py b/_pytest/python.py index d85f4a89e..5b3e0138f 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -117,8 +117,9 @@ 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)) - for marker in metafunc.definition.find_markers('parametrize'): - metafunc.parametrize(*marker.args, **marker.kwargs) + for marker in metafunc.definition.iter_markers(): + if marker.name == 'parametrize': + metafunc.parametrize(*marker.args, **marker.kwargs) def pytest_configure(config): @@ -221,7 +222,7 @@ class PyobjMixin(PyobjContext): # XXX evil hack # used to avoid Instance collector marker duplication if self._ALLOW_MARKERS: - self._markers.update(get_unpacked_marks(self.obj)) + self.own_markers.extend(get_unpacked_marks(self.obj)) return obj def fset(self, value): @@ -1132,7 +1133,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr): self.obj = callobj self.keywords.update(self.obj.__dict__) - self._markers.update(get_unpacked_marks(self.obj)) + self.own_markers.extend(get_unpacked_marks(self.obj)) if callspec: self.callspec = callspec # this is total hostile and a mess @@ -1142,7 +1143,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr): # feel free to cry, this was broken for years before # and keywords cant fix it per design self.keywords[mark.name] = mark - self._markers.update(callspec.marks) + self.own_markers.extend(callspec.marks) if keywords: self.keywords.update(keywords) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index 318c8795e..f62edcf9a 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -64,7 +64,9 @@ def pytest_runtest_setup(item): item._skipped_by_mark = True skip(eval_skipif.getexplanation()) - for skip_info in item.find_markers('skip'): + for skip_info in item.iter_markers(): + if skip_info.name != 'skip': + continue item._skipped_by_mark = True if 'reason' in skip_info.kwargs: skip(skip_info.kwargs['reason']) diff --git a/_pytest/warnings.py b/_pytest/warnings.py index 8183d6d66..d8b9fc460 100644 --- a/_pytest/warnings.py +++ b/_pytest/warnings.py @@ -60,9 +60,10 @@ def catch_warnings_for_item(item): for arg in inifilters: _setoption(warnings, arg) - for mark in item.find_markers('filterwarnings'): - for arg in mark.args: - warnings._setoption(arg) + for mark in item.iter_markers(): + if mark.name == 'filterwarnings': + for arg in mark.args: + warnings._setoption(arg) yield diff --git a/testing/test_mark.py b/testing/test_mark.py index 64b201577..9ec1ce75a 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -533,7 +533,7 @@ class TestFunctional(object): items, rec = testdir.inline_genitems(p) for item in items: print(item, item.keywords) - assert list(item.find_markers('a')) + assert [x for x in item.iter_markers() if x.name == 'a'] def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir): p = testdir.makepyfile("""