1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/bc620a10f932/ Changeset: bc620a10f932 User: hpk42 Date: 2013-10-03 15:43:56 Summary: more fixes regarding marking, in particular plugins should use add_marker/get_marker now. Affected #: 6 files
diff -r ebcecaa7d610a5ec2aa5b3367169bffc1b50415f -r bc620a10f932bc0d961b92f2c0b741e2ae1c8715 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,16 @@ - avoid tmpdir fixture to create too long filenames especially when parametrization is used (issue354) +- fix pytest-pep8 and pytest-flakes / pytest interactions + (collection names in mark plugin was assuming an item always + has a function which is not true for those plugins etc.) + Thanks Andi Zeidler. + +- introduce node.get_marker/node.add_marker API for plugins + like pytest-pep8 and pytest-flakes to avoid the messy + details of the node.keywords pseudo-dicts. Adapated + docs. + Changes between 2.4.0 and 2.4.1 ----------------------------------- diff -r ebcecaa7d610a5ec2aa5b3367169bffc1b50415f -r bc620a10f932bc0d961b92f2c0b741e2ae1c8715 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -321,6 +321,27 @@ chain.reverse() return chain + def add_marker(self, marker): + """ dynamically add a marker object to the node. + + ``marker`` can be a string or pytest.mark.* instance. + """ + from _pytest.mark import MarkDecorator + if isinstance(marker, py.builtin._basestring): + marker = MarkDecorator(marker) + elif not isinstance(marker, MarkDecorator): + raise ValueError("is not a string or pytest.mark.* Marker") + self.keywords[marker.name] = marker + + def get_marker(self, name): + """ get a marker object from this node or None if + the node doesn't have a marker with that name. """ + val = self.keywords.get(name, None) + if val is not None: + from _pytest.mark import MarkInfo, MarkDecorator + if isinstance(val, (MarkDecorator, MarkInfo)): + return val + def listextrakeywords(self): """ Return a set of all extra keywords in self and any parents.""" extra_keywords = set() diff -r ebcecaa7d610a5ec2aa5b3367169bffc1b50415f -r bc620a10f932bc0d961b92f2c0b741e2ae1c8715 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -81,11 +81,8 @@ class MarkMapping: - """Provides a local mapping for markers. - Only the marker names from the given :class:`NodeKeywords` will be mapped, - so the names are taken only from :class:`MarkInfo` or - :class:`MarkDecorator` items. - """ + """Provides a local mapping for markers where item access + resolves to True if the marker is present. """ def __init__(self, keywords): mymarks = set() for key, value in keywords.items(): @@ -93,8 +90,8 @@ mymarks.add(key) self._mymarks = mymarks - def __getitem__(self, markname): - return markname in self._mymarks + def __getitem__(self, name): + return name in self._mymarks class KeywordMapping: @@ -202,13 +199,17 @@ pass """ def __init__(self, name, args=None, kwargs=None): - self.markname = name + self.name = name self.args = args or () self.kwargs = kwargs or {} + @property + def markname(self): + return self.name # for backward-compat (2.4.1 had this attr) + def __repr__(self): d = self.__dict__.copy() - name = d.pop('markname') + name = d.pop('name') return "<MarkDecorator %r %r>" % (name, d) def __call__(self, *args, **kwargs): @@ -228,19 +229,19 @@ else: func.pytestmark = [self] else: - holder = getattr(func, self.markname, None) + holder = getattr(func, self.name, None) if holder is None: holder = MarkInfo( - self.markname, self.args, self.kwargs + self.name, self.args, self.kwargs ) - setattr(func, self.markname, holder) + setattr(func, self.name, holder) else: holder.add(self.args, self.kwargs) return func kw = self.kwargs.copy() kw.update(kwargs) args = self.args + args - return self.__class__(self.markname, args=args, kwargs=kw) + return self.__class__(self.name, args=args, kwargs=kw) class MarkInfo: diff -r ebcecaa7d610a5ec2aa5b3367169bffc1b50415f -r bc620a10f932bc0d961b92f2c0b741e2ae1c8715 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -235,7 +235,7 @@ "env(name): mark test to run only on named environment") def pytest_runtest_setup(item): - envmarker = item.keywords.get("env", None) + envmarker = item.get_marker("env") if envmarker is not None: envname = envmarker.args[0] if envname != item.config.getoption("-E"): @@ -318,7 +318,7 @@ import sys def pytest_runtest_setup(item): - g = item.keywords.get("glob", None) + g = item.get_marker("glob") if g is not None: for info in g: print ("glob args=%s kwargs=%s" %(info.args, info.kwargs)) @@ -353,7 +353,7 @@ def pytest_runtest_setup(item): if isinstance(item, item.Function): plat = sys.platform - if plat not in item.keywords: + if not item.get_marker(plat): if ALL.intersection(item.keywords): pytest.skip("cannot run on platform %s" %(plat)) @@ -439,9 +439,9 @@ def pytest_collection_modifyitems(items): for item in items: if "interface" in item.nodeid: - item.keywords["interface"] = pytest.mark.interface + item.add_marker(pytest.mark.interface) elif "event" in item.nodeid: - item.keywords["event"] = pytest.mark.event + item.add_marker(pytest.mark.event) We can now use the ``-m option`` to select one set:: diff -r ebcecaa7d610a5ec2aa5b3367169bffc1b50415f -r bc620a10f932bc0d961b92f2c0b741e2ae1c8715 plugin-test.sh --- /dev/null +++ b/plugin-test.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# this assumes plugins are installed as sister directories + +set -e +cd ../pytest-pep8 +py.test +cd ../pytest-instafail +py.test +cd ../pytest-cache +py.test +#cd ../pytest-cov +#py.test +cd ../pytest-xdist +py.test + diff -r ebcecaa7d610a5ec2aa5b3367169bffc1b50415f -r bc620a10f932bc0d961b92f2c0b741e2ae1c8715 testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -180,6 +180,7 @@ assert len(passed) == len(passed_result) assert list(passed) == list(passed_result) + class TestFunctional: def test_mark_per_function(self, testdir): @@ -362,7 +363,6 @@ deselected_tests = dlist[0].items assert len(deselected_tests) == 2 - def test_keywords_at_node_level(self, testdir): p = testdir.makepyfile(""" import pytest @@ -383,6 +383,30 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_keyword_added_for_session(self, testdir): + testdir.makeconftest(""" + import pytest + def pytest_collection_modifyitems(session): + session.add_marker("mark1") + session.add_marker(pytest.mark.mark2) + session.add_marker(pytest.mark.mark3) + pytest.raises(ValueError, lambda: + session.add_marker(10)) + """) + testdir.makepyfile(""" + def test_some(request): + assert "mark1" in request.keywords + assert "mark2" in request.keywords + assert "mark3" in request.keywords + assert 10 not in request.keywords + marker = request.node.get_marker("mark1") + assert marker.name == "mark1" + assert marker.args == () + assert marker.kwargs == {} + """) + reprec = testdir.inline_run("-m", "mark1") + reprec.assertoutcome(passed=1) + class TestKeywordSelection: def test_select_simple(self, testdir): file_test = testdir.makepyfile(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. _______________________________________________ pytest-commit mailing list pytest-commit@python.org https://mail.python.org/mailman/listinfo/pytest-commit