[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