more fixes regarding marking, in particular plugins should use add_marker/get_marker now.
This commit is contained in:
parent
9fdfa155fb
commit
2248a31a44
10
CHANGELOG
10
CHANGELOG
|
@ -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
|
||||
-----------------------------------
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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::
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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("""
|
||||
|
|
Loading…
Reference in New Issue