[py-svn] commit/pytest: 4 new changesets

Bitbucket commits-noreply at bitbucket.org
Wed Dec 28 16:55:52 CET 2011


4 new commits in pytest:


https://bitbucket.org/hpk42/pytest/changeset/d98c5a3cbfca/
changeset:   d98c5a3cbfca
user:        hpk42
date:        2011-12-28 16:47:18
summary:     internally keep multiple applications of the same markers as separate
entities such that the new iter() API can iterate over pytest.mark
function attributes, getting all such applications.  See added example
for more info.
affected #:  3 files

diff -r 9d37772da0aa2c5627afa54ef14e1ead7f14de59 -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 _pytest/mark.py
--- a/_pytest/mark.py
+++ b/_pytest/mark.py
@@ -191,8 +191,7 @@
                         holder = MarkInfo(self.markname, self.args, self.kwargs)
                         setattr(func, self.markname, holder)
                     else:
-                        holder.kwargs.update(self.kwargs)
-                        holder.args += self.args
+                        holder.add(self.args, self.kwargs)
                 return func
         kw = self.kwargs.copy()
         kw.update(kwargs)
@@ -208,11 +207,23 @@
         self.args = args
         #: keyword argument dictionary, empty if nothing specified
         self.kwargs = kwargs
+        self._arglist = [(args, kwargs.copy())]
 
     def __repr__(self):
         return "<MarkInfo %r args=%r kwargs=%r>" % (
                 self.name, self.args, self.kwargs)
 
+    def add(self, args, kwargs):
+        """ add a MarkInfo with the given args and kwargs. """
+        self._arglist.append((args, kwargs))
+        self.args += args
+        self.kwargs.update(kwargs)
+
+    def __iter__(self):
+        """ yield MarkInfo objects each relating to a marking-call. """
+        for args, kwargs in self._arglist:
+            yield MarkInfo(self.name, args, kwargs)
+
 def pytest_itemcollected(item):
     if not isinstance(item, pytest.Function):
         return


diff -r 9d37772da0aa2c5627afa54ef14e1ead7f14de59 -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 doc/example/markers.txt
--- a/doc/example/markers.txt
+++ b/doc/example/markers.txt
@@ -25,26 +25,26 @@
 You can then restrict a test run to only run tests marked with ``webtest``::
 
     $ py.test -v -m webtest
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3 -- /Users/hpk/venv/1/bin/python
     collecting ... collected 2 items
     
     test_server.py:3: test_send_http PASSED
     
-    =================== 1 tests deselected by "-m 'webtest'" ===================
-    ================== 1 passed, 1 deselected in 0.01 seconds ==================
+    ===================== 1 tests deselected by "-m 'webtest'" =====================
+    ==================== 1 passed, 1 deselected in 0.03 seconds ====================
 
 Or the inverse, running all tests except the webtest ones::
     
     $ py.test -v -m "not webtest"
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3 -- /Users/hpk/venv/1/bin/python
     collecting ... collected 2 items
     
     test_server.py:6: test_something_quick PASSED
     
-    ================= 1 tests deselected by "-m 'not webtest'" =================
-    ================== 1 passed, 1 deselected in 0.01 seconds ==================
+    =================== 1 tests deselected by "-m 'not webtest'" ===================
+    ==================== 1 passed, 1 deselected in 0.03 seconds ====================
 
 Registering markers
 -------------------------------------
@@ -134,6 +134,7 @@
 in which case it will be applied to all functions and
 methods defined in the module.
 
+
 Using ``-k TEXT`` to select tests
 ----------------------------------------------------
 
@@ -141,39 +142,39 @@
 the given argument::
 
     $ py.test -k send_http  # running with the above defined examples
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
     collecting ... collected 4 items
     
     test_server.py .
     
-    =================== 3 tests deselected by '-ksend_http' ====================
-    ================== 1 passed, 3 deselected in 0.02 seconds ==================
+    ===================== 3 tests deselected by '-ksend_http' ======================
+    ==================== 1 passed, 3 deselected in 0.06 seconds ====================
 
 And you can also run all tests except the ones that match the keyword::
 
     $ py.test -k-send_http
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
     collecting ... collected 4 items
     
     test_mark_classlevel.py ..
     test_server.py .
     
-    =================== 1 tests deselected by '-k-send_http' ===================
-    ================== 3 passed, 1 deselected in 0.03 seconds ==================
+    ===================== 1 tests deselected by '-k-send_http' =====================
+    ==================== 3 passed, 1 deselected in 0.05 seconds ====================
 
 Or to only select the class::
 
     $ py.test -kTestClass
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
     collecting ... collected 4 items
     
     test_mark_classlevel.py ..
     
-    =================== 2 tests deselected by '-kTestClass' ====================
-    ================== 2 passed, 2 deselected in 0.02 seconds ==================
+    ===================== 2 tests deselected by '-kTestClass' ======================
+    ==================== 2 passed, 2 deselected in 0.04 seconds ====================
 
 .. _`adding a custom marker from a plugin`:
 
@@ -221,24 +222,24 @@
 the test needs::
 
     $ py.test -E stage2
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
     collecting ... collected 1 items
     
     test_someenv.py s
     
-    ======================== 1 skipped in 0.02 seconds =========================
+    ========================== 1 skipped in 0.03 seconds ===========================
   
 and here is one that specifies exactly the environment needed::
 
     $ py.test -E stage1
-    =========================== test session starts ============================
-    platform darwin -- Python 2.7.1 -- pytest-2.2.1
+    ============================= test session starts ==============================
+    platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
     collecting ... collected 1 items
     
     test_someenv.py .
     
-    ========================= 1 passed in 0.01 seconds =========================
+    =========================== 1 passed in 0.03 seconds ===========================
 
 The ``--markers`` option always gives you a list of available markers::
 
@@ -254,4 +255,43 @@
     @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
     
     @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+
     
+Reading markers which were set from multiple places
+----------------------------------------------------
+
+.. versionadded: 2.2.2
+
+If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function.  From plugin
+code you can read over all such settings.  Example::
+
+    # content of test_mark_three_times.py
+    import pytest
+    pytestmark = pytest.mark.glob("module", x=1)
+
+    @pytest.mark.glob("class", x=2)
+    class TestClass:
+        @pytest.mark.glob("function", x=3)
+        def test_something(self):
+            pass
+
+Here we have the marker "glob" applied three times to the same
+test function.  From a conftest file we can read it like this::
+
+    # content of conftest.py
+
+    def pytest_runtest_setup(item):
+        g = getattr(item.obj, 'glob', None)
+        if g is not None:
+            for info in g:
+                print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
+
+Let's run this without capturing output and see what we get::
+
+    $ py.test -q -s 
+    collecting ... collected 2 items
+    ..
+    2 passed in 0.04 seconds
+    glob args=('function',) kwargs={'x': 3}
+    glob args=('module',) kwargs={'x': 1}
+    glob args=('class',) kwargs={'x': 2}


diff -r 9d37772da0aa2c5627afa54ef14e1ead7f14de59 -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 testing/test_mark.py
--- a/testing/test_mark.py
+++ b/testing/test_mark.py
@@ -230,6 +230,14 @@
         assert marker.args == ("pos0", "pos1")
         assert marker.kwargs == {'x': 3, 'y': 2, 'z': 4}
 
+        # test the new __iter__ interface
+        l = list(marker)
+        assert len(l) == 3
+        assert l[0].args == ("pos0",)
+        pytest.xfail(reason="needs reordering of parametrize transfermarks")
+        assert l[1].args == ()
+        assert l[2].args == ("pos1", )
+
     def test_mark_other(self, testdir):
         pytest.raises(TypeError, '''
             testdir.getitem("""
@@ -259,6 +267,23 @@
             "keyword: *hello*"
         ])
 
+    def test_merging_markers_two_functions(self, testdir):
+        p = testdir.makepyfile("""
+            import pytest
+            @pytest.mark.hello("pos1", z=4)
+            @pytest.mark.hello("pos0", z=3)
+            def test_func(self):
+                pass
+        """)
+        items, rec = testdir.inline_genitems(p)
+        item, = items
+        keywords = item.keywords
+        marker = keywords['hello']
+        l = list(marker)
+        assert len(l) == 2
+        assert l[0].args == ("pos0",)
+        assert l[1].args == ("pos1",)
+
 
 class TestKeywordSelection:
     def test_select_simple(self, testdir):



https://bitbucket.org/hpk42/pytest/changeset/dd739069b446/
changeset:   dd739069b446
user:        hpk42
date:        2011-12-28 16:47:19
summary:     fix issue106: allow parametrize to be applied per-class/per-module
affected #:  5 files

diff -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 -r dd739069b446c8edda62f3771962b503c5dab150 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,6 +7,8 @@
   produce better output
 - fix issue102: report more useful errors and hints for when a 
   test directory was renamed and some pyc/__pycache__ remain
+- fix issue106: allow parametrize to be applied multiple times
+  e.g. from module, class and at function level.
 
 Changes between 2.2.0 and 2.2.1
 ----------------------------------------


diff -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 -r dd739069b446c8edda62f3771962b503c5dab150 _pytest/mark.py
--- a/_pytest/mark.py
+++ b/_pytest/mark.py
@@ -224,22 +224,3 @@
         for args, kwargs in self._arglist:
             yield MarkInfo(self.name, args, kwargs)
 
-def pytest_itemcollected(item):
-    if not isinstance(item, pytest.Function):
-        return
-    try:
-        func = item.obj.__func__
-    except AttributeError:
-        func = getattr(item.obj, 'im_func', item.obj)
-    pyclasses = (pytest.Class, pytest.Module)
-    for node in item.listchain():
-        if isinstance(node, pyclasses):
-            marker = getattr(node.obj, 'pytestmark', None)
-            if marker is not None:
-                if isinstance(marker, list):
-                    for mark in marker:
-                        mark(func)
-                else:
-                    marker(func)
-        node = node.parent
-    item.keywords.update(py.builtin._getfuncdict(func))


diff -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 -r dd739069b446c8edda62f3771962b503c5dab150 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -33,7 +33,8 @@
         param = metafunc.function.parametrize
     except AttributeError:
         return
-    metafunc.parametrize(*param.args, **param.kwargs)
+    for p in param:
+        metafunc.parametrize(*p.args, **p.kwargs)
 
 def pytest_configure(config):
     config.addinivalue_line("markers",
@@ -222,6 +223,7 @@
         cls = clscol and clscol.obj or None
         metafunc = Metafunc(funcobj, config=self.config,
             cls=cls, module=module)
+        transfer_markers(metafunc)
         gentesthook = self.config.hook.pytest_generate_tests
         extra = [module]
         if cls is not None:
@@ -239,6 +241,20 @@
             l.append(function)
         return l
 
+def transfer_markers(metafunc):
+    # XXX this should rather be code in the mark plugin or the mark
+    # plugin should merge with the python plugin.
+    for holder in (metafunc.cls, metafunc.module):
+        try:
+            pytestmark = holder.pytestmark
+        except AttributeError:
+            continue
+        if isinstance(pytestmark, list):
+            for mark in pytestmark:
+                mark(metafunc.function)
+        else:
+            pytestmark(metafunc.function)
+
 
 class Module(pytest.File, PyCollectorMixin):
     def _getobj(self):
@@ -559,7 +575,7 @@
 
     @property
     def id(self):
-        return "-".join(filter(None, self._idlist))
+        return "-".join(map(str, filter(None, self._idlist)))
 
     def setmulti(self, valtype, argnames, valset, id):
         for arg,val in zip(argnames, valset):


diff -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 -r dd739069b446c8edda62f3771962b503c5dab150 testing/test_mark.py
--- a/testing/test_mark.py
+++ b/testing/test_mark.py
@@ -228,26 +228,26 @@
         keywords = item.keywords
         marker = keywords['hello']
         assert marker.args == ("pos0", "pos1")
-        assert marker.kwargs == {'x': 3, 'y': 2, 'z': 4}
+        assert marker.kwargs == {'x': 1, 'y': 2, 'z': 4}
 
         # test the new __iter__ interface
         l = list(marker)
         assert len(l) == 3
         assert l[0].args == ("pos0",)
-        pytest.xfail(reason="needs reordering of parametrize transfermarks")
         assert l[1].args == ()
         assert l[2].args == ("pos1", )
 
-    def test_mark_other(self, testdir):
-        pytest.raises(TypeError, '''
-            testdir.getitem("""
+    def test_mark_with_wrong_marker(self, testdir):
+        reprec = testdir.inline_runsource("""
                 import pytest
                 class pytestmark:
                     pass
                 def test_func():
                     pass
-            """)
-        ''')
+        """)
+        l = reprec.getfailedcollections()
+        assert len(l) == 1
+        assert "TypeError" in str(l[0].longrepr)
 
     def test_mark_dynamically_in_funcarg(self, testdir):
         testdir.makeconftest("""


diff -r d98c5a3cbfca6a4791a212d4ccd8596e8c417359 -r dd739069b446c8edda62f3771962b503c5dab150 testing/test_python.py
--- a/testing/test_python.py
+++ b/testing/test_python.py
@@ -1048,6 +1048,23 @@
         assert metafunc._calls[1].funcargs == dict(x=3, y=4)
         assert metafunc._calls[1].id == "3-4"
 
+    def test_parametrize_multiple_times(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            pytestmark = pytest.mark.parametrize("x", [1,2])
+            def test_func(x):
+                assert 0, x
+            class TestClass:
+                pytestmark = pytest.mark.parametrize("y", [3,4])
+                def test_meth(self, x, y):
+                    assert 0, x
+        """)
+        result = testdir.runpytest()
+        assert result.ret == 1
+        result.stdout.fnmatch_lines([
+            "*6 fail*",
+        ])
+
 class TestMetafuncFunctional:
     def test_attributes(self, testdir):
         p = testdir.makepyfile("""



https://bitbucket.org/hpk42/pytest/changeset/f2760b862746/
changeset:   f2760b862746
user:        hpk42
date:        2011-12-28 16:49:13
summary:     work around an apparent python2.4/python2.5 bug with subprocess.Popen,
causing jenkins failures.  Apparently "os.environ.popitem(name, None)"
is not the same as::

    try:
        del os.environ[name]
    except KeyError:
        pass
affected #:  3 files

diff -r dd739069b446c8edda62f3771962b503c5dab150 -r f2760b862746aa307482335dff28fb3cb99efda0 _pytest/monkeypatch.py
--- a/_pytest/monkeypatch.py
+++ b/_pytest/monkeypatch.py
@@ -95,7 +95,10 @@
         self._setattr[:] = []
         for dictionary, name, value in self._setitem:
             if value is notset:
-                dictionary.pop(name, None)
+                try:
+                    del dictionary[name]
+                except KeyError:
+                    pass # was already deleted, so we have the desired state
             else:
                 dictionary[name] = value
         self._setitem[:] = []


diff -r dd739069b446c8edda62f3771962b503c5dab150 -r f2760b862746aa307482335dff28fb3cb99efda0 testing/test_monkeypatch.py
--- a/testing/test_monkeypatch.py
+++ b/testing/test_monkeypatch.py
@@ -67,12 +67,20 @@
     monkeypatch.undo()
     assert not d
 
-def test_setenv_deleted_meanwhile():
+ at pytest.mark.parametrize("before", [True, False])
+def test_setenv_deleted_meanwhile(before):
+    key = "qwpeoip123"
+    if before:
+        os.environ[key] = "world"
     monkeypatch = MonkeyPatch()
-    monkeypatch.setenv('XYZ123', 'hello')
-    del os.environ['XYZ123']
+    monkeypatch.setenv(key, 'hello')
+    del os.environ[key]
     monkeypatch.undo()
-    assert 'XYZ123' not in os.environ
+    if before:
+        assert os.environ[key] == "world"
+        del os.environ[key]
+    else:
+        assert key not in os.environ
 
 def test_delitem():
     d = {'x': 1}


diff -r dd739069b446c8edda62f3771962b503c5dab150 -r f2760b862746aa307482335dff28fb3cb99efda0 testing/test_runner.py
--- a/testing/test_runner.py
+++ b/testing/test_runner.py
@@ -436,8 +436,7 @@
            py.test.cmdline.main([__file__])
     """)
     import subprocess
-    popen = subprocess.Popen([sys.executable, str(p)],
-        stdout=subprocess.PIPE, env={})
+    popen = subprocess.Popen([sys.executable, str(p)], stdout=subprocess.PIPE)
     s = popen.stdout.read()
     ret = popen.wait()
     assert ret == 0



https://bitbucket.org/hpk42/pytest/changeset/d2ba1109c153/
changeset:   d2ba1109c153
user:        hpk42
date:        2011-12-28 16:49:35
summary:     bump version
affected #:  2 files

diff -r f2760b862746aa307482335dff28fb3cb99efda0 -r d2ba1109c1530ae74036e08b68bc18d50e16397f _pytest/__init__.py
--- a/_pytest/__init__.py
+++ b/_pytest/__init__.py
@@ -1,2 +1,2 @@
 #
-__version__ = '2.2.2.dev3'
+__version__ = '2.2.2.dev4'


diff -r f2760b862746aa307482335dff28fb3cb99efda0 -r d2ba1109c1530ae74036e08b68bc18d50e16397f setup.py
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@
         name='pytest',
         description='py.test: simple powerful testing with Python',
         long_description = long_description,
-        version='2.2.2.dev3',
+        version='2.2.2.dev4',
         url='http://pytest.org',
         license='MIT license',
         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
@@ -70,4 +70,4 @@
     return {'console_scripts': l}
 
 if __name__ == '__main__':
-    main()
+    main()
\ No newline at end of file

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