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
|
- avoid tmpdir fixture to create too long filenames especially
|
||||||
when parametrization is used (issue354)
|
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
|
Changes between 2.4.0 and 2.4.1
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
|
|
@ -321,6 +321,27 @@ class Node(object):
|
||||||
chain.reverse()
|
chain.reverse()
|
||||||
return chain
|
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):
|
def listextrakeywords(self):
|
||||||
""" Return a set of all extra keywords in self and any parents."""
|
""" Return a set of all extra keywords in self and any parents."""
|
||||||
extra_keywords = set()
|
extra_keywords = set()
|
||||||
|
|
|
@ -81,11 +81,8 @@ def pytest_collection_modifyitems(items, config):
|
||||||
|
|
||||||
|
|
||||||
class MarkMapping:
|
class MarkMapping:
|
||||||
"""Provides a local mapping for markers.
|
"""Provides a local mapping for markers where item access
|
||||||
Only the marker names from the given :class:`NodeKeywords` will be mapped,
|
resolves to True if the marker is present. """
|
||||||
so the names are taken only from :class:`MarkInfo` or
|
|
||||||
:class:`MarkDecorator` items.
|
|
||||||
"""
|
|
||||||
def __init__(self, keywords):
|
def __init__(self, keywords):
|
||||||
mymarks = set()
|
mymarks = set()
|
||||||
for key, value in keywords.items():
|
for key, value in keywords.items():
|
||||||
|
@ -93,8 +90,8 @@ class MarkMapping:
|
||||||
mymarks.add(key)
|
mymarks.add(key)
|
||||||
self._mymarks = mymarks
|
self._mymarks = mymarks
|
||||||
|
|
||||||
def __getitem__(self, markname):
|
def __getitem__(self, name):
|
||||||
return markname in self._mymarks
|
return name in self._mymarks
|
||||||
|
|
||||||
|
|
||||||
class KeywordMapping:
|
class KeywordMapping:
|
||||||
|
@ -202,13 +199,17 @@ class MarkDecorator:
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, args=None, kwargs=None):
|
def __init__(self, name, args=None, kwargs=None):
|
||||||
self.markname = name
|
self.name = name
|
||||||
self.args = args or ()
|
self.args = args or ()
|
||||||
self.kwargs = kwargs or {}
|
self.kwargs = kwargs or {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def markname(self):
|
||||||
|
return self.name # for backward-compat (2.4.1 had this attr)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
d = self.__dict__.copy()
|
d = self.__dict__.copy()
|
||||||
name = d.pop('markname')
|
name = d.pop('name')
|
||||||
return "<MarkDecorator %r %r>" % (name, d)
|
return "<MarkDecorator %r %r>" % (name, d)
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
|
@ -228,19 +229,19 @@ class MarkDecorator:
|
||||||
else:
|
else:
|
||||||
func.pytestmark = [self]
|
func.pytestmark = [self]
|
||||||
else:
|
else:
|
||||||
holder = getattr(func, self.markname, None)
|
holder = getattr(func, self.name, None)
|
||||||
if holder is None:
|
if holder is None:
|
||||||
holder = MarkInfo(
|
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:
|
else:
|
||||||
holder.add(self.args, self.kwargs)
|
holder.add(self.args, self.kwargs)
|
||||||
return func
|
return func
|
||||||
kw = self.kwargs.copy()
|
kw = self.kwargs.copy()
|
||||||
kw.update(kwargs)
|
kw.update(kwargs)
|
||||||
args = self.args + args
|
args = self.args + args
|
||||||
return self.__class__(self.markname, args=args, kwargs=kw)
|
return self.__class__(self.name, args=args, kwargs=kw)
|
||||||
|
|
||||||
|
|
||||||
class MarkInfo:
|
class MarkInfo:
|
||||||
|
|
|
@ -235,7 +235,7 @@ specifies via named environments::
|
||||||
"env(name): mark test to run only on named environment")
|
"env(name): mark test to run only on named environment")
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
envmarker = item.keywords.get("env", None)
|
envmarker = item.get_marker("env")
|
||||||
if envmarker is not None:
|
if envmarker is not None:
|
||||||
envname = envmarker.args[0]
|
envname = envmarker.args[0]
|
||||||
if envname != item.config.getoption("-E"):
|
if envname != item.config.getoption("-E"):
|
||||||
|
@ -318,7 +318,7 @@ test function. From a conftest file we can read it like this::
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
g = item.keywords.get("glob", None)
|
g = item.get_marker("glob")
|
||||||
if g is not None:
|
if g is not None:
|
||||||
for info in g:
|
for info in g:
|
||||||
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
|
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):
|
def pytest_runtest_setup(item):
|
||||||
if isinstance(item, item.Function):
|
if isinstance(item, item.Function):
|
||||||
plat = sys.platform
|
plat = sys.platform
|
||||||
if plat not in item.keywords:
|
if not item.get_marker(plat):
|
||||||
if ALL.intersection(item.keywords):
|
if ALL.intersection(item.keywords):
|
||||||
pytest.skip("cannot run on platform %s" %(plat))
|
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):
|
def pytest_collection_modifyitems(items):
|
||||||
for item in items:
|
for item in items:
|
||||||
if "interface" in item.nodeid:
|
if "interface" in item.nodeid:
|
||||||
item.keywords["interface"] = pytest.mark.interface
|
item.add_marker(pytest.mark.interface)
|
||||||
elif "event" in item.nodeid:
|
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::
|
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 len(passed) == len(passed_result)
|
||||||
assert list(passed) == list(passed_result)
|
assert list(passed) == list(passed_result)
|
||||||
|
|
||||||
|
|
||||||
class TestFunctional:
|
class TestFunctional:
|
||||||
|
|
||||||
def test_mark_per_function(self, testdir):
|
def test_mark_per_function(self, testdir):
|
||||||
|
@ -362,7 +363,6 @@ class TestFunctional:
|
||||||
deselected_tests = dlist[0].items
|
deselected_tests = dlist[0].items
|
||||||
assert len(deselected_tests) == 2
|
assert len(deselected_tests) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_keywords_at_node_level(self, testdir):
|
def test_keywords_at_node_level(self, testdir):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -383,6 +383,30 @@ class TestFunctional:
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run()
|
||||||
reprec.assertoutcome(passed=1)
|
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:
|
class TestKeywordSelection:
|
||||||
def test_select_simple(self, testdir):
|
def test_select_simple(self, testdir):
|
||||||
file_test = testdir.makepyfile("""
|
file_test = testdir.makepyfile("""
|
||||||
|
|
Loading…
Reference in New Issue