[Pytest-commit] commit/pytest: hpk42: more fixes regarding marking, in particular plugins should use add_marker/get_marker now.
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Thu Oct 3 15:44:38 CEST 2013
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.
More information about the pytest-commit
mailing list