7 new commits in pytest:

https://bitbucket.org/hpk42/pytest/commits/0b9d82e69dca/
Changeset:   0b9d82e69dca
User:        pfctdayelise
Date:        2013-05-17 10:46:36
Summary:     issue #308
first attempt, mark individual parametrize test instances with other marks 
(like xfail)
Affected #:  2 files

diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 
0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -4,6 +4,7 @@
 import sys
 import pytest
 from _pytest.main import getfslineno
+from _pytest.mark import MarkDecorator, MarkInfo
 from _pytest.monkeypatch import monkeypatch
 from py._code.code import TerminalRepr
 
@@ -565,11 +566,13 @@
         self._globalid_args = set()
         self._globalparam = _notexists
         self._arg2scopenum = {}  # used for sorting parametrized resources
+        self.keywords = {}
 
     def copy(self, metafunc):
         cs = CallSpec2(self.metafunc)
         cs.funcargs.update(self.funcargs)
         cs.params.update(self.params)
+        cs.keywords.update(self.keywords)
         cs._arg2scopenum.update(self._arg2scopenum)
         cs._idlist = list(self._idlist)
         cs._globalid = self._globalid
@@ -593,7 +596,7 @@
     def id(self):
         return "-".join(map(str, filter(None, self._idlist)))
 
-    def setmulti(self, valtype, argnames, valset, id, scopenum=0):
+    def setmulti(self, valtype, argnames, valset, id, keywords, scopenum=0):
         for arg,val in zip(argnames, valset):
             self._checkargnotcontained(arg)
             getattr(self, valtype)[arg] = val
@@ -605,6 +608,7 @@
             if val is _notexists:
                 self._emptyparamspecified = True
         self._idlist.append(id)
+        self.keywords.update(keywords)
 
     def setall(self, funcargs, id, param):
         for x in funcargs:
@@ -673,6 +677,18 @@
         if not argvalues:
             argvalues = [(_notexists,) * len(argnames)]
 
+        # these marks/keywords will be applied in Function init
+        newkeywords = {}
+        for i, argval in enumerate(argvalues):
+            newkeywords[i] = {}
+            if isinstance(argval, MarkDecorator):
+                # convert into a mark without the test content mixed in
+                newmark = MarkDecorator(argval.markname, argval.args[:-1], 
argval.kwargs)
+                newkeywords[i] = {newmark.markname: newmark}
+
+        argvalues = [av.args[-1] if isinstance(av, MarkDecorator) else av
+                     for av in argvalues]
+
         if scope is None:
             scope = "subfunction"
         scopenum = scopes.index(scope)
@@ -691,7 +707,7 @@
                 assert len(valset) == len(argnames)
                 newcallspec = callspec.copy(self)
                 newcallspec.setmulti(valtype, argnames, valset, ids[i],
-                                     scopenum)
+                                     newkeywords[i], scopenum)
                 newcalls.append(newcallspec)
         self._calls = newcalls
 
@@ -908,6 +924,9 @@
 
         for name, val in (py.builtin._getfuncdict(self.obj) or {}).items():
             self.keywords[name] = val
+        if callspec:
+            for name, val in callspec.keywords.items():
+                self.keywords[name] = val
         if keywords:
             for name, val in keywords.items():
                 self.keywords[name] = val

diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 
0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 testing/python/metafunc.py
--- a/testing/python/metafunc.py
+++ b/testing/python/metafunc.py
@@ -577,4 +577,175 @@
             "*3 passed*"
         ])
 
+    @pytest.mark.issue308
+    def test_mark_on_individual_parametrize_instance(self, testdir):
+        s = """
+            import pytest
 
+            @pytest.mark.foo
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.bar((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        items = testdir.getitems(s)
+        assert len(items) == 3
+        for item in items:
+            assert 'foo' in item.keywords
+        assert 'bar' not in items[0].keywords
+        assert 'bar' in items[1].keywords
+        assert 'bar' not in items[2].keywords
+
+    @pytest.mark.issue308
+    def test_select_individual_parametrize_instance_based_on_mark(self, 
testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.foo((2, 3)),
+                (3, 4),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        testdir.makepyfile(s)
+        rec = testdir.inline_run("-m", 'foo')
+        passed, skipped, fail = rec.listoutcomes()
+        assert len(passed) == 1
+        assert len(skipped) == 0
+        assert len(fail) == 0
+
+    @pytest.mark.xfail("is this important to support??")
+    @pytest.mark.issue308
+    def test_nested_marks_on_individual_parametrize_instance(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.foo(pytest.mark.bar((1, 3))),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        items = testdir.getitems(s)
+        assert len(items) == 3
+        for mark in ['foo', 'bar']:
+            assert mark not in items[0].keywords
+            assert mark in items[1].keywords
+            assert mark not in items[2].keywords
+
+    @pytest.mark.xfail(reason="is this important to support??")
+    @pytest.mark.issue308
+    def test_nested_marks_on_individual_parametrize_instance(self, testdir):
+        s = """
+            import pytest
+            mastermark = pytest.mark.foo(pytest.mark.bar)
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                mastermark((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        items = testdir.getitems(s)
+        assert len(items) == 3
+        for mark in ['foo', 'bar']:
+            assert mark not in items[0].keywords
+            assert mark in items[1].keywords
+            assert mark not in items[2].keywords
+
+    @pytest.mark.issue308
+    def test_simple_xfail_on_individual_parametrize_instance(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.xfail((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        # xfail is skip??
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    @pytest.mark.issue308
+    def test_xfail_with_arg_on_individual_parametrize_instance(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0")((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    @pytest.mark.issue308
+    def test_xfail_with_kwarg_on_individual_parametrize_instance(self, 
testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.xfail(reason="some bug")((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    @pytest.mark.issue308
+    def test_xfail_with_arg_and_kwarg_on_individual_parametrize_instance(self, 
testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 
3)),
+                (2, 3),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    @pytest.mark.issue308
+    def test_xfail_is_xpass_on_individual_parametrize_instance(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("input", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 
3)),
+                (3, 4),
+            ])
+            def test_increment(input, expected):
+                assert input + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        # xpass is fail, obviously :)
+        reprec.assertoutcome(passed=2, failed=1)
+


https://bitbucket.org/hpk42/pytest/commits/3c892457f19e/
Changeset:   3c892457f19e
User:        hpk42
Date:        2013-05-17 11:32:52
Summary:     Merged hpk42/pytest into default
Affected #:  2 files

diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r 
3c892457f19ed9c676d7ddf26b7b49e151eb579f CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -20,6 +20,8 @@
 
 - honor --tb style for setup/teardown errors as well.  Thanks Maho.
 
+- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
+
 Changes between 2.3.4 and 2.3.5
 -----------------------------------
 

diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r 
3c892457f19ed9c676d7ddf26b7b49e151eb579f doc/en/example/nonpython/conftest.py
--- a/doc/en/example/nonpython/conftest.py
+++ b/doc/en/example/nonpython/conftest.py
@@ -9,7 +9,7 @@
 class YamlFile(pytest.File):
     def collect(self):
         import yaml # we need a yaml parser, e.g. PyYAML
-        raw = yaml.load(self.fspath.open())
+        raw = yaml.safe_load(self.fspath.open())
         for name, spec in raw.items():
             yield YamlItem(name, self, spec)
 


https://bitbucket.org/hpk42/pytest/commits/c38344905336/
Changeset:   c38344905336
User:        pfctdayelise
Date:        2013-05-20 04:52:20
Summary:     issue #308

address some comments by @hpk42 on 0b9d82e :

- move tests into their own class, rename
- add test showing metafunc.parametrize called in pytest_generate_tests rather 
than as decorator
- add test and fix single-argname case
- convert two loops into one in parametrize()

also
- renamed 'input' to 'n', since 'input' is a built-in
Affected #:  2 files

diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r 
c38344905336154f7dd36cc05f84f1fb26cde9c4 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -671,24 +671,27 @@
             It will also override any fixture-function defined scope, allowing
             to set a dynamic scope using test context or configuration.
         """
+        # remove any marks applied to individual tests instances
+        # these marks will be applied in Function init
+        newkeywords = {}
+        strippedargvalues = []
+        for i, argval in enumerate(argvalues):
+            if isinstance(argval, MarkDecorator):
+                # convert into a mark without the test content mixed in
+                newmark = MarkDecorator(argval.markname, argval.args[:-1], 
argval.kwargs)
+                newkeywords[i] = {newmark.markname: newmark}
+                strippedargvalues.append(argval.args[-1])
+            else:
+                newkeywords[i] = {}
+                strippedargvalues.append(argval)
+        argvalues = strippedargvalues
+
         if not isinstance(argnames, (tuple, list)):
             argnames = (argnames,)
             argvalues = [(val,) for val in argvalues]
         if not argvalues:
             argvalues = [(_notexists,) * len(argnames)]
 
-        # these marks/keywords will be applied in Function init
-        newkeywords = {}
-        for i, argval in enumerate(argvalues):
-            newkeywords[i] = {}
-            if isinstance(argval, MarkDecorator):
-                # convert into a mark without the test content mixed in
-                newmark = MarkDecorator(argval.markname, argval.args[:-1], 
argval.kwargs)
-                newkeywords[i] = {newmark.markname: newmark}
-
-        argvalues = [av.args[-1] if isinstance(av, MarkDecorator) else av
-                     for av in argvalues]
-
         if scope is None:
             scope = "subfunction"
         scopenum = scopes.index(scope)

diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r 
c38344905336154f7dd36cc05f84f1fb26cde9c4 testing/python/metafunc.py
--- a/testing/python/metafunc.py
+++ b/testing/python/metafunc.py
@@ -577,19 +577,21 @@
             "*3 passed*"
         ])
 
-    @pytest.mark.issue308
-    def test_mark_on_individual_parametrize_instance(self, testdir):
+
+@pytest.mark.issue308
+class TestMarkersWithParametrization:
+    def test_simple_mark(self, testdir):
         s = """
             import pytest
 
             @pytest.mark.foo
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
                 pytest.mark.bar((1, 3)),
                 (2, 3),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         items = testdir.getitems(s)
         assert len(items) == 3
@@ -599,18 +601,17 @@
         assert 'bar' in items[1].keywords
         assert 'bar' not in items[2].keywords
 
-    @pytest.mark.issue308
-    def test_select_individual_parametrize_instance_based_on_mark(self, 
testdir):
+    def test_select_based_on_mark(self, testdir):
         s = """
             import pytest
 
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
                 pytest.mark.foo((2, 3)),
                 (3, 4),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         testdir.makepyfile(s)
         rec = testdir.inline_run("-m", 'foo')
@@ -619,19 +620,19 @@
         assert len(skipped) == 0
         assert len(fail) == 0
 
-    @pytest.mark.xfail("is this important to support??")
-    @pytest.mark.issue308
-    def test_nested_marks_on_individual_parametrize_instance(self, testdir):
+    @pytest.mark.xfail(reason="is this important to support??")
+    def test_nested_marks(self, testdir):
         s = """
             import pytest
+            mastermark = pytest.mark.foo(pytest.mark.bar)
 
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
-                pytest.mark.foo(pytest.mark.bar((1, 3))),
+                mastermark((1, 3)),
                 (2, 3),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         items = testdir.getitems(s)
         assert len(items) == 3
@@ -640,112 +641,123 @@
             assert mark in items[1].keywords
             assert mark not in items[2].keywords
 
-    @pytest.mark.xfail(reason="is this important to support??")
-    @pytest.mark.issue308
-    def test_nested_marks_on_individual_parametrize_instance(self, testdir):
-        s = """
-            import pytest
-            mastermark = pytest.mark.foo(pytest.mark.bar)
-
-            @pytest.mark.parametrize(("input", "expected"), [
-                (1, 2),
-                mastermark((1, 3)),
-                (2, 3),
-            ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
-        """
-        items = testdir.getitems(s)
-        assert len(items) == 3
-        for mark in ['foo', 'bar']:
-            assert mark not in items[0].keywords
-            assert mark in items[1].keywords
-            assert mark not in items[2].keywords
-
-    @pytest.mark.issue308
-    def test_simple_xfail_on_individual_parametrize_instance(self, testdir):
+    def test_simple_xfail(self, testdir):
         s = """
             import pytest
 
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
                 pytest.mark.xfail((1, 3)),
                 (2, 3),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         testdir.makepyfile(s)
         reprec = testdir.inline_run()
         # xfail is skip??
         reprec.assertoutcome(passed=2, skipped=1)
 
-    @pytest.mark.issue308
-    def test_xfail_with_arg_on_individual_parametrize_instance(self, testdir):
+    def test_simple_xfail_single_argname(self, testdir):
         s = """
             import pytest
 
-            @pytest.mark.parametrize(("input", "expected"), [
-                (1, 2),
-                pytest.mark.xfail("sys.version > 0")((1, 3)),
-                (2, 3),
+            @pytest.mark.parametrize("n", [
+                2,
+                pytest.mark.xfail(3),
+                4,
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_isEven(n):
+                assert n % 2 == 0
         """
         testdir.makepyfile(s)
         reprec = testdir.inline_run()
         reprec.assertoutcome(passed=2, skipped=1)
 
-    @pytest.mark.issue308
-    def test_xfail_with_kwarg_on_individual_parametrize_instance(self, 
testdir):
+    def test_xfail_with_arg(self, testdir):
         s = """
             import pytest
 
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
-                pytest.mark.xfail(reason="some bug")((1, 3)),
+                pytest.mark.xfail("sys.version > 0")((1, 3)),
                 (2, 3),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         testdir.makepyfile(s)
         reprec = testdir.inline_run()
         reprec.assertoutcome(passed=2, skipped=1)
 
-    @pytest.mark.issue308
-    def test_xfail_with_arg_and_kwarg_on_individual_parametrize_instance(self, 
testdir):
+    def test_xfail_with_kwarg(self, testdir):
         s = """
             import pytest
 
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
-                pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 
3)),
+                pytest.mark.xfail(reason="some bug")((1, 3)),
                 (2, 3),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         testdir.makepyfile(s)
         reprec = testdir.inline_run()
         reprec.assertoutcome(passed=2, skipped=1)
 
-    @pytest.mark.issue308
-    def test_xfail_is_xpass_on_individual_parametrize_instance(self, testdir):
+    def test_xfail_with_arg_and_kwarg(self, testdir):
         s = """
             import pytest
 
-            @pytest.mark.parametrize(("input", "expected"), [
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 
3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    def test_xfail_passing_is_xpass(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
                 (1, 2),
                 pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 
3)),
                 (3, 4),
             ])
-            def test_increment(input, expected):
-                assert input + 1 == expected
+            def test_increment(n, expected):
+                assert n + 1 == expected
         """
         testdir.makepyfile(s)
         reprec = testdir.inline_run()
         # xpass is fail, obviously :)
         reprec.assertoutcome(passed=2, failed=1)
 
+    def test_parametrize_called_in_generate_tests(self, testdir):
+        s = """
+            import pytest
+
+
+            def pytest_generate_tests(metafunc):
+                passingTestData = [(1, 2),
+                                   (2, 3)]
+                failingTestData = [(1, 3),
+                                   (2, 2)]
+
+                testData = passingTestData + [pytest.mark.xfail(d)
+                                  for d in failingTestData]
+                metafunc.parametrize(("n", "expected"), testData)
+
+
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=2)


https://bitbucket.org/hpk42/pytest/commits/598409cef71a/
Changeset:   598409cef71a
User:        pfctdayelise
Date:        2013-05-20 04:56:30
Summary:     ? pull/merge
Affected #:  2 files

diff -r c38344905336154f7dd36cc05f84f1fb26cde9c4 -r 
598409cef71aeb988c97bdb83fbf01782ac42b75 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -20,6 +20,8 @@
 
 - honor --tb style for setup/teardown errors as well.  Thanks Maho.
 
+- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
+
 Changes between 2.3.4 and 2.3.5
 -----------------------------------
 

diff -r c38344905336154f7dd36cc05f84f1fb26cde9c4 -r 
598409cef71aeb988c97bdb83fbf01782ac42b75 doc/en/example/nonpython/conftest.py
--- a/doc/en/example/nonpython/conftest.py
+++ b/doc/en/example/nonpython/conftest.py
@@ -9,7 +9,7 @@
 class YamlFile(pytest.File):
     def collect(self):
         import yaml # we need a yaml parser, e.g. PyYAML
-        raw = yaml.load(self.fspath.open())
+        raw = yaml.safe_load(self.fspath.open())
         for name, spec in raw.items():
             yield YamlItem(name, self, spec)
 


https://bitbucket.org/hpk42/pytest/commits/9210cf8ca829/
Changeset:   9210cf8ca829
User:        pfctdayelise
Date:        2013-05-21 03:12:45
Summary:     issue #308

+ docs
Affected #:  3 files

diff -r 598409cef71aeb988c97bdb83fbf01782ac42b75 -r 
9210cf8ca82927da6f835cbebddbfe2367ddc817 doc/en/example/markers.txt
--- a/doc/en/example/markers.txt
+++ b/doc/en/example/markers.txt
@@ -185,6 +185,29 @@
 in which case it will be applied to all functions and
 methods defined in the module.
 
+.. _`marking individual tests when using parametrize`:
+
+Marking individual tests when using parametrize
+-----------------------------------------------
+
+When using parametrize, applying a mark will make it apply
+to each individual test. However it is also possible to
+apply a marker to an individual test instance::
+
+    import pytest
+
+    @pytest.mark.foo
+    @pytest.mark.parametrize(("n", "expected"), [
+        (1, 2),
+        pytest.mark.bar((1, 3)),
+        (2, 3),
+    ])
+    def test_increment(n, expected):
+         assert n + 1 == expected
+
+In this example the mark "foo" will apply to each of the three
+tests, whereas the "bar" mark is only applied to the second test.
+Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail 
with parametrize`.
 
 
 .. _`adding a custom marker from a plugin`:

diff -r 598409cef71aeb988c97bdb83fbf01782ac42b75 -r 
9210cf8ca82927da6f835cbebddbfe2367ddc817 doc/en/parametrize.txt
--- a/doc/en/parametrize.txt
+++ b/doc/en/parametrize.txt
@@ -82,6 +82,18 @@
 Note that there ways how you can mark a class or a module,
 see :ref:`mark`.
 
+It is also possible to mark individual test instances within parametrize::
+
+    # content of test_expectation.py
+    import pytest
+    @pytest.mark.parametrize(("input", "expected"), [
+        ("3+5", 8),
+        ("2+4", 6),
+        pytest.mark.xfail(("6*9", 42)),
+    ])
+    def test_eval(input, expected):
+        assert eval(input) == expected
+
 
 .. _`pytest_generate_tests`:
 

diff -r 598409cef71aeb988c97bdb83fbf01782ac42b75 -r 
9210cf8ca82927da6f835cbebddbfe2367ddc817 doc/en/skipping.txt
--- a/doc/en/skipping.txt
+++ b/doc/en/skipping.txt
@@ -176,6 +176,28 @@
     
     ======================== 6 xfailed in 0.05 seconds 
=========================
 
+.. _`skip/xfail with parametrize`:
+
+Skip/xfail with parametrize
+---------------------------
+
+It is possible to apply markers like skip and xfail to individual
+test instances when using parametrize:
+
+     import pytest
+
+     @pytest.mark.parametrize(("n", "expected"), [
+         (1, 2),
+        pytest.mark.xfail((1, 0)),
+        pytest.mark.xfail(reason="some bug")((1, 3)),
+        (2, 3),
+         (3, 4),
+         (4, 5),
+         pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)),
+     ])
+     def test_increment(n, expected):
+        assert n + 1 == expected
+
 
 Imperative xfail from within a test or setup function
 ------------------------------------------------------


https://bitbucket.org/hpk42/pytest/commits/2917dfdb26d3/
Changeset:   2917dfdb26d3
User:        pfctdayelise
Date:        2013-05-21 03:18:37
Summary:     Merged hpk42/pytest into default
Affected #:  3 files

diff -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 -r 
2917dfdb26d3152ec0ac40590cbcbe64bb764ec0 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,6 +11,11 @@
   when importing markers between modules.  Specifying conditions
   as strings will remain fully supported.
 
+- improved doctest counting for doctests in python modules -- 
+  files without any doctest items will not show up anymore
+  and doctest examples are counted as separate test items.
+  thanks Danilo Bellini.
+
 - fix issue245 by depending on the released py-1.4.14
   which fixes py.io.dupfile to work with files with no
   mode. Thanks Jason R. Coombs.

diff -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 -r 
2917dfdb26d3152ec0ac40590cbcbe64bb764ec0 _pytest/doctest.py
--- a/_pytest/doctest.py
+++ b/_pytest/doctest.py
@@ -34,6 +34,14 @@
         self.reprlocation.toterminal(tw)
 
 class DoctestItem(pytest.Item):
+    def __init__(self, name, parent, runner=None, dtest=None):
+        super(DoctestItem, self).__init__(name, parent)
+        self.runner = runner
+        self.dtest = dtest
+
+    def runtest(self):
+        self.runner.run(self.dtest)
+
     def repr_failure(self, excinfo):
         doctest = py.std.doctest
         if excinfo.errisinstance((doctest.DocTestFailure,
@@ -76,7 +84,7 @@
             return super(DoctestItem, self).repr_failure(excinfo)
 
     def reportinfo(self):
-        return self.fspath, None, "[doctest]"
+        return self.fspath, None, "[doctest] %s" % self.name
 
 class DoctestTextfile(DoctestItem, pytest.File):
     def runtest(self):
@@ -91,8 +99,8 @@
             extraglobs=dict(getfixture=fixture_request.getfuncargvalue),
             raise_on_error=True, verbose=0)
 
-class DoctestModule(DoctestItem, pytest.File):
-    def runtest(self):
+class DoctestModule(pytest.File):
+    def collect(self):
         doctest = py.std.doctest
         if self.fspath.basename == "conftest.py":
             module = self.config._conftest.importconftest(self.fspath)
@@ -102,7 +110,11 @@
         self.funcargs = {}
         self._fixtureinfo = FuncFixtureInfo((), [], {})
         fixture_request = FixtureRequest(self)
-        failed, tot = doctest.testmod(
-            module, raise_on_error=True, verbose=0,
-            extraglobs=dict(getfixture=fixture_request.getfuncargvalue),
-            optionflags=doctest.ELLIPSIS)
+        doctest_globals = dict(getfixture=fixture_request.getfuncargvalue)
+        # uses internal doctest module parsing mechanism
+        finder = doctest.DocTestFinder()
+        runner = doctest.DebugRunner(verbose=0, optionflags=doctest.ELLIPSIS)
+        for test in finder.find(module, module.__name__,
+                                extraglobs=doctest_globals):
+            if test.examples: # skip empty doctests
+                yield DoctestItem(test.name, self, runner, test)

diff -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 -r 
2917dfdb26d3152ec0ac40590cbcbe64bb764ec0 testing/test_doctest.py
--- a/testing/test_doctest.py
+++ b/testing/test_doctest.py
@@ -1,4 +1,4 @@
-from _pytest.doctest import DoctestModule, DoctestTextfile
+from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
 import py, pytest
 
 class TestDoctests:
@@ -19,13 +19,61 @@
         items, reprec = testdir.inline_genitems(w)
         assert len(items) == 1
 
-    def test_collect_module(self, testdir):
+    def test_collect_module_empty(self, testdir):
         path = testdir.makepyfile(whatever="#")
         for p in (path, testdir.tmpdir):
             items, reprec = testdir.inline_genitems(p,
                 '--doctest-modules')
+            assert len(items) == 0
+
+    def test_collect_module_single_modulelevel_doctest(self, testdir):
+        path = testdir.makepyfile(whatever='""">>> pass"""')
+        for p in (path, testdir.tmpdir):
+            items, reprec = testdir.inline_genitems(p,
+                '--doctest-modules')
             assert len(items) == 1
-            assert isinstance(items[0], DoctestModule)
+            assert isinstance(items[0], DoctestItem)
+            assert isinstance(items[0].parent, DoctestModule)
+
+    def test_collect_module_two_doctest_one_modulelevel(self, testdir):
+        path = testdir.makepyfile(whatever="""
+            '>>> x = None'
+            def my_func():
+                ">>> magic = 42 "
+        """)
+        for p in (path, testdir.tmpdir):
+            items, reprec = testdir.inline_genitems(p,
+                '--doctest-modules')
+            assert len(items) == 2
+            assert isinstance(items[0], DoctestItem)
+            assert isinstance(items[1], DoctestItem)
+            assert isinstance(items[0].parent, DoctestModule)
+            assert items[0].parent is items[1].parent
+
+    def test_collect_module_two_doctest_no_modulelevel(self, testdir):
+        path = testdir.makepyfile(whatever="""
+            '# Empty'
+            def my_func():
+                ">>> magic = 42 "
+            def unuseful():
+                '''
+                # This is a function
+                # >>> # it doesn't have any doctest
+                '''
+            def another():
+                '''
+                # This is another function
+                >>> import os # this one does have a doctest
+                '''
+        """)
+        for p in (path, testdir.tmpdir):
+            items, reprec = testdir.inline_genitems(p,
+                '--doctest-modules')
+            assert len(items) == 2
+            assert isinstance(items[0], DoctestItem)
+            assert isinstance(items[1], DoctestItem)
+            assert isinstance(items[0].parent, DoctestModule)
+            assert items[0].parent is items[1].parent
 
     def test_simple_doctestfile(self, testdir):
         p = testdir.maketxtfile(test_doc="""
@@ -164,3 +212,47 @@
         """)
         reprec = testdir.inline_run(p, "--doctest-modules")
         reprec.assertoutcome(passed=1)
+
+    def test_doctestmodule_three_tests(self, testdir):
+        p = testdir.makepyfile("""
+            '''
+            >>> dir = getfixture('tmpdir')
+            >>> type(dir).__name__
+            'LocalPath'
+            '''
+            def my_func():
+                '''
+                >>> magic = 42
+                >>> magic - 42
+                0
+                '''
+            def unuseful():
+                pass
+            def another():
+                '''
+                >>> import os
+                >>> os is os
+                True
+                '''
+        """)
+        reprec = testdir.inline_run(p, "--doctest-modules")
+        reprec.assertoutcome(passed=3)
+
+    def test_doctestmodule_two_tests_one_fail(self, testdir):
+        p = testdir.makepyfile("""
+            class MyClass:
+                def bad_meth(self):
+                    '''
+                    >>> magic = 42
+                    >>> magic
+                    0
+                    '''
+                def nice_meth(self):
+                    '''
+                    >>> magic = 42
+                    >>> magic - 42
+                    0
+                    '''
+        """)
+        reprec = testdir.inline_run(p, "--doctest-modules")
+        reprec.assertoutcome(failed=1, passed=1)


https://bitbucket.org/hpk42/pytest/commits/fff62647d862/
Changeset:   fff62647d862
User:        hpk42
Date:        2013-05-22 13:36:39
Summary:     Merged in pfctdayelise/pytest (pull request #36)

issue 308
Affected #:  5 files

diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r 
fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -4,6 +4,7 @@
 import sys
 import pytest
 from _pytest.main import getfslineno
+from _pytest.mark import MarkDecorator, MarkInfo
 from _pytest.monkeypatch import monkeypatch
 from py._code.code import TerminalRepr
 
@@ -565,11 +566,13 @@
         self._globalid_args = set()
         self._globalparam = _notexists
         self._arg2scopenum = {}  # used for sorting parametrized resources
+        self.keywords = {}
 
     def copy(self, metafunc):
         cs = CallSpec2(self.metafunc)
         cs.funcargs.update(self.funcargs)
         cs.params.update(self.params)
+        cs.keywords.update(self.keywords)
         cs._arg2scopenum.update(self._arg2scopenum)
         cs._idlist = list(self._idlist)
         cs._globalid = self._globalid
@@ -593,7 +596,7 @@
     def id(self):
         return "-".join(map(str, filter(None, self._idlist)))
 
-    def setmulti(self, valtype, argnames, valset, id, scopenum=0):
+    def setmulti(self, valtype, argnames, valset, id, keywords, scopenum=0):
         for arg,val in zip(argnames, valset):
             self._checkargnotcontained(arg)
             getattr(self, valtype)[arg] = val
@@ -605,6 +608,7 @@
             if val is _notexists:
                 self._emptyparamspecified = True
         self._idlist.append(id)
+        self.keywords.update(keywords)
 
     def setall(self, funcargs, id, param):
         for x in funcargs:
@@ -667,6 +671,21 @@
             It will also override any fixture-function defined scope, allowing
             to set a dynamic scope using test context or configuration.
         """
+        # remove any marks applied to individual tests instances
+        # these marks will be applied in Function init
+        newkeywords = {}
+        strippedargvalues = []
+        for i, argval in enumerate(argvalues):
+            if isinstance(argval, MarkDecorator):
+                # convert into a mark without the test content mixed in
+                newmark = MarkDecorator(argval.markname, argval.args[:-1], 
argval.kwargs)
+                newkeywords[i] = {newmark.markname: newmark}
+                strippedargvalues.append(argval.args[-1])
+            else:
+                newkeywords[i] = {}
+                strippedargvalues.append(argval)
+        argvalues = strippedargvalues
+
         if not isinstance(argnames, (tuple, list)):
             argnames = (argnames,)
             argvalues = [(val,) for val in argvalues]
@@ -691,7 +710,7 @@
                 assert len(valset) == len(argnames)
                 newcallspec = callspec.copy(self)
                 newcallspec.setmulti(valtype, argnames, valset, ids[i],
-                                     scopenum)
+                                     newkeywords[i], scopenum)
                 newcalls.append(newcallspec)
         self._calls = newcalls
 
@@ -908,6 +927,9 @@
 
         for name, val in (py.builtin._getfuncdict(self.obj) or {}).items():
             self.keywords[name] = val
+        if callspec:
+            for name, val in callspec.keywords.items():
+                self.keywords[name] = val
         if keywords:
             for name, val in keywords.items():
                 self.keywords[name] = val

diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r 
fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f doc/en/example/markers.txt
--- a/doc/en/example/markers.txt
+++ b/doc/en/example/markers.txt
@@ -185,6 +185,29 @@
 in which case it will be applied to all functions and
 methods defined in the module.
 
+.. _`marking individual tests when using parametrize`:
+
+Marking individual tests when using parametrize
+-----------------------------------------------
+
+When using parametrize, applying a mark will make it apply
+to each individual test. However it is also possible to
+apply a marker to an individual test instance::
+
+    import pytest
+
+    @pytest.mark.foo
+    @pytest.mark.parametrize(("n", "expected"), [
+        (1, 2),
+        pytest.mark.bar((1, 3)),
+        (2, 3),
+    ])
+    def test_increment(n, expected):
+         assert n + 1 == expected
+
+In this example the mark "foo" will apply to each of the three
+tests, whereas the "bar" mark is only applied to the second test.
+Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail 
with parametrize`.
 
 
 .. _`adding a custom marker from a plugin`:

diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r 
fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f doc/en/parametrize.txt
--- a/doc/en/parametrize.txt
+++ b/doc/en/parametrize.txt
@@ -82,6 +82,18 @@
 Note that there ways how you can mark a class or a module,
 see :ref:`mark`.
 
+It is also possible to mark individual test instances within parametrize::
+
+    # content of test_expectation.py
+    import pytest
+    @pytest.mark.parametrize(("input", "expected"), [
+        ("3+5", 8),
+        ("2+4", 6),
+        pytest.mark.xfail(("6*9", 42)),
+    ])
+    def test_eval(input, expected):
+        assert eval(input) == expected
+
 
 .. _`pytest_generate_tests`:
 

diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r 
fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f doc/en/skipping.txt
--- a/doc/en/skipping.txt
+++ b/doc/en/skipping.txt
@@ -176,6 +176,28 @@
     
     ======================== 6 xfailed in 0.05 seconds 
=========================
 
+.. _`skip/xfail with parametrize`:
+
+Skip/xfail with parametrize
+---------------------------
+
+It is possible to apply markers like skip and xfail to individual
+test instances when using parametrize:
+
+     import pytest
+
+     @pytest.mark.parametrize(("n", "expected"), [
+         (1, 2),
+        pytest.mark.xfail((1, 0)),
+        pytest.mark.xfail(reason="some bug")((1, 3)),
+        (2, 3),
+         (3, 4),
+         (4, 5),
+         pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)),
+     ])
+     def test_increment(n, expected):
+        assert n + 1 == expected
+
 
 Imperative xfail from within a test or setup function
 ------------------------------------------------------

diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r 
fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f testing/python/metafunc.py
--- a/testing/python/metafunc.py
+++ b/testing/python/metafunc.py
@@ -578,3 +578,186 @@
         ])
 
 
+@pytest.mark.issue308
+class TestMarkersWithParametrization:
+    def test_simple_mark(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.foo
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.bar((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        items = testdir.getitems(s)
+        assert len(items) == 3
+        for item in items:
+            assert 'foo' in item.keywords
+        assert 'bar' not in items[0].keywords
+        assert 'bar' in items[1].keywords
+        assert 'bar' not in items[2].keywords
+
+    def test_select_based_on_mark(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.foo((2, 3)),
+                (3, 4),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        rec = testdir.inline_run("-m", 'foo')
+        passed, skipped, fail = rec.listoutcomes()
+        assert len(passed) == 1
+        assert len(skipped) == 0
+        assert len(fail) == 0
+
+    @pytest.mark.xfail(reason="is this important to support??")
+    def test_nested_marks(self, testdir):
+        s = """
+            import pytest
+            mastermark = pytest.mark.foo(pytest.mark.bar)
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                mastermark((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        items = testdir.getitems(s)
+        assert len(items) == 3
+        for mark in ['foo', 'bar']:
+            assert mark not in items[0].keywords
+            assert mark in items[1].keywords
+            assert mark not in items[2].keywords
+
+    def test_simple_xfail(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.xfail((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        # xfail is skip??
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    def test_simple_xfail_single_argname(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize("n", [
+                2,
+                pytest.mark.xfail(3),
+                4,
+            ])
+            def test_isEven(n):
+                assert n % 2 == 0
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    def test_xfail_with_arg(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0")((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    def test_xfail_with_kwarg(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.xfail(reason="some bug")((1, 3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    def test_xfail_with_arg_and_kwarg(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 
3)),
+                (2, 3),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=1)
+
+    def test_xfail_passing_is_xpass(self, testdir):
+        s = """
+            import pytest
+
+            @pytest.mark.parametrize(("n", "expected"), [
+                (1, 2),
+                pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 
3)),
+                (3, 4),
+            ])
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        # xpass is fail, obviously :)
+        reprec.assertoutcome(passed=2, failed=1)
+
+    def test_parametrize_called_in_generate_tests(self, testdir):
+        s = """
+            import pytest
+
+
+            def pytest_generate_tests(metafunc):
+                passingTestData = [(1, 2),
+                                   (2, 3)]
+                failingTestData = [(1, 3),
+                                   (2, 2)]
+
+                testData = passingTestData + [pytest.mark.xfail(d)
+                                  for d in failingTestData]
+                metafunc.parametrize(("n", "expected"), testData)
+
+
+            def test_increment(n, expected):
+                assert n + 1 == expected
+        """
+        testdir.makepyfile(s)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2, skipped=2)

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.
_______________________________________________
pytest-commit mailing list
pytest-commit@python.org
http://mail.python.org/mailman/listinfo/pytest-commit

Reply via email to