more fixes regarding marking, in particular plugins should use add_marker/get_marker now.

This commit is contained in:
holger krekel 2013-10-03 15:43:56 +02:00
parent 9fdfa155fb
commit 2248a31a44
6 changed files with 91 additions and 19 deletions

View File

@ -11,6 +11,16 @@ Changes between 2.4.1 and 2.4.2
- 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
-----------------------------------

View File

@ -321,6 +321,27 @@ class Node(object):
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()

View File

@ -81,11 +81,8 @@ def pytest_collection_modifyitems(items, config):
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 @@ class MarkMapping:
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 @@ class MarkDecorator:
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 @@ class MarkDecorator:
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:

View File

@ -235,7 +235,7 @@ specifies via named environments::
"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 @@ test function. From a conftest file we can read it like this::
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 @@ for your particular platform, you could use the following plugin::
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 @@ We want to dynamically define two markers and can do it in a
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::

16
plugin-test.sh Normal file
View File

@ -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

View File

@ -180,6 +180,7 @@ def test_keyword_option_custom(spec, testdir):
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 @@ class TestFunctional:
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 @@ class TestFunctional:
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("""