From commits-noreply at bitbucket.org Fri Jun 1 20:13:31 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 01 Jun 2012 18:13:31 -0000 Subject: [py-svn] commit/pytest: hpk42: remove pyc file Message-ID: <20120601181331.6080.56812@bitbucket13.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/b6295824bd44/ changeset: b6295824bd44 user: hpk42 date: 2012-06-01 20:13:18 summary: remove pyc file affected #: 1 file diff -r 79a5b776a6f3f34f8699e130cfe5440b1ff67336 -r b6295824bd44b09231980f709e13de0b6153ce9b doc/example/py2py3/test_py2.pyc Binary file doc/example/py2py3/test_py2.pyc has changed 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. From commits-noreply at bitbucket.org Sun Jun 3 16:10:20 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 03 Jun 2012 14:10:20 -0000 Subject: [py-svn] commit/pytest: hpk42: fix py2py3 example tests Message-ID: <20120603141020.4635.23047@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/f26211d8ba33/ changeset: f26211d8ba33 user: hpk42 date: 2012-06-03 16:10:10 summary: fix py2py3 example tests affected #: 2 files diff -r b6295824bd44b09231980f709e13de0b6153ce9b -r f26211d8ba3360d59e60eaa379856b7fd02ce654 doc/example/py2py3/test_py2.py --- a/doc/example/py2py3/test_py2.py +++ b/doc/example/py2py3/test_py2.py @@ -3,5 +3,5 @@ try: 0/0 except ZeroDivisionError, e: - assert 0, e + pass diff -r b6295824bd44b09231980f709e13de0b6153ce9b -r f26211d8ba3360d59e60eaa379856b7fd02ce654 doc/example/py2py3/test_py3.py --- a/doc/example/py2py3/test_py3.py +++ b/doc/example/py2py3/test_py3.py @@ -3,5 +3,5 @@ try: 0/0 except ZeroDivisionError as e: - assert 0, e + pass 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. From commits-noreply at bitbucket.org Sun Jun 3 21:02:11 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 03 Jun 2012 19:02:11 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue128 - show captured output when capsys/capfd are in use Message-ID: <20120603190211.32338.80091@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/4f01dd1ab796/ changeset: 4f01dd1ab796 user: hpk42 date: 2012-06-03 21:01:27 summary: fix issue128 - show captured output when capsys/capfd are in use affected #: 5 files diff -r f26211d8ba3360d59e60eaa379856b7fd02ce654 -r 4f01dd1ab7969b6d3aba90f395d70f7250339712 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +Changes between 2.2.4 and 2.2.5.dev +----------------------------------- + +- fix issue128: show captured output when capsys/capfd are used + Changes between 2.2.3 and 2.2.4 ----------------------------------- diff -r f26211d8ba3360d59e60eaa379856b7fd02ce654 -r 4f01dd1ab7969b6d3aba90f395d70f7250339712 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.4' +__version__ = '2.2.5.dev1' diff -r f26211d8ba3360d59e60eaa379856b7fd02ce654 -r 4f01dd1ab7969b6d3aba90f395d70f7250339712 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -121,20 +121,21 @@ def activate_funcargs(self, pyfuncitem): if not hasattr(pyfuncitem, 'funcargs'): return - assert not hasattr(self, '_capturing_funcargs') - self._capturing_funcargs = capturing_funcargs = [] + assert not hasattr(self, '_capturing_funcarg') + capargs = [] for name, capfuncarg in pyfuncitem.funcargs.items(): if name in ('capsys', 'capfd'): - capturing_funcargs.append(capfuncarg) - capfuncarg._start() + capargs.append(capfuncarg) + if capargs: + self._capturing_funcarg = capargs[0] + self._capturing_funcarg._start() def deactivate_funcargs(self): - capturing_funcargs = getattr(self, '_capturing_funcargs', None) - if capturing_funcargs is not None: - while capturing_funcargs: - capfuncarg = capturing_funcargs.pop() - capfuncarg._finalize() - del self._capturing_funcargs + capturing_funcarg = getattr(self, '_capturing_funcarg', None) + if capturing_funcarg: + outerr = capturing_funcarg._finalize() + del self._capturing_funcarg + return outerr def pytest_make_collect_report(self, __multicall__, collector): method = self._getmethod(collector.config, collector.fspath) @@ -169,9 +170,12 @@ @pytest.mark.tryfirst def pytest_runtest_makereport(self, __multicall__, item, call): - self.deactivate_funcargs() + funcarg_outerr = self.deactivate_funcargs() rep = __multicall__.execute() outerr = self.suspendcapture(item) + if funcarg_outerr is not None: + outerr = (outerr[0] + funcarg_outerr[0], + outerr[1] + funcarg_outerr[1]) if not rep.passed: addouterr(rep, outerr) if not rep.passed or rep.when == "teardown": @@ -179,11 +183,15 @@ item.outerr = outerr return rep +error_capsysfderror = "cannot use capsys and capfd at the same time" + def pytest_funcarg__capsys(request): """enables capturing of writes to sys.stdout/sys.stderr and makes captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. """ + if "capfd" in request._funcargs: + raise request.LookupError(error_capsysfderror) return CaptureFuncarg(py.io.StdCapture) def pytest_funcarg__capfd(request): @@ -191,8 +199,10 @@ captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. """ + if "capsys" in request._funcargs: + raise request.LookupError(error_capsysfderror) if not hasattr(os, 'dup'): - py.test.skip("capfd funcarg needs os.dup") + pytest.skip("capfd funcarg needs os.dup") return CaptureFuncarg(py.io.StdCaptureFD) class CaptureFuncarg: @@ -204,8 +214,9 @@ def _finalize(self): if hasattr(self, 'capture'): - self.capture.reset() + outerr = self.capture.reset() del self.capture + return outerr def readouterr(self): return self.capture.readouterr() diff -r f26211d8ba3360d59e60eaa379856b7fd02ce654 -r 4f01dd1ab7969b6d3aba90f395d70f7250339712 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.4', + version='2.2.5.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r f26211d8ba3360d59e60eaa379856b7fd02ce654 -r 4f01dd1ab7969b6d3aba90f395d70f7250339712 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -373,6 +373,33 @@ """) reprec.assertoutcome(passed=1) + def test_capsyscapfd(self, testdir): + p = testdir.makepyfile(""" + def test_one(capsys, capfd): + pass + def test_two(capfd, capsys): + pass + """) + result = testdir.runpytest(p) + result.stdout.fnmatch_lines([ + "*ERROR*setup*test_one*", + "*capsys*capfd*same*time*", + "*ERROR*setup*test_two*", + "*capsys*capfd*same*time*", + "*2 error*"]) + + @pytest.mark.parametrize("method", ["sys", "fd"]) + def test_capture_is_represented_on_failure_issue128(self, testdir, method): + p = testdir.makepyfile(""" + def test_hello(cap%s): + print ("xxx42xxx") + assert 0 + """ % method) + result = testdir.runpytest(p) + result.stdout.fnmatch_lines([ + "xxx42xxx", + ]) + @needsosdup def test_stdfd_functional(self, testdir): reprec = testdir.inline_runsource(""" 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. From commits-noreply at bitbucket.org Sun Jun 3 21:06:49 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 03 Jun 2012 19:06:49 -0000 Subject: [py-svn] commit/pytest: hpk42: simplify activate_funcargs Message-ID: <20120603190649.29626.68250@bitbucket12.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/5ae8dc3cc4bf/ changeset: 5ae8dc3cc4bf user: hpk42 date: 2012-06-03 21:06:43 summary: simplify activate_funcargs affected #: 1 file diff -r 4f01dd1ab7969b6d3aba90f395d70f7250339712 -r 5ae8dc3cc4bfe64cf3ac2e43e30c7c96816737b6 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -119,16 +119,12 @@ return "", "" def activate_funcargs(self, pyfuncitem): - if not hasattr(pyfuncitem, 'funcargs'): - return - assert not hasattr(self, '_capturing_funcarg') - capargs = [] - for name, capfuncarg in pyfuncitem.funcargs.items(): - if name in ('capsys', 'capfd'): - capargs.append(capfuncarg) - if capargs: - self._capturing_funcarg = capargs[0] - self._capturing_funcarg._start() + if hasattr(pyfuncitem, 'funcargs'): + for name, capfuncarg in pyfuncitem.funcargs.items(): + if name in ('capsys', 'capfd'): + assert not hasattr(self, '_capturing_funcarg') + self._capturing_funcarg = capfuncarg + capfuncarg._start() def deactivate_funcargs(self): capturing_funcarg = getattr(self, '_capturing_funcarg', None) 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. From commits-noreply at bitbucket.org Wed Jun 6 13:49:16 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jun 2012 11:49:16 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20120606114916.14325.87467@bitbucket05.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/5329136d01fe/ changeset: 5329136d01fe user: t2y date: 2012-06-06 01:52:53 summary: added "docs/en" directory and moved affected #: 211 files Diff too large to display. https://bitbucket.org/hpk42/pytest/changeset/c35325c30698/ changeset: c35325c30698 user: t2y date: 2012-06-06 01:54:35 summary: fixed to find the CHANGELOG's path affected #: 1 file diff -r 5329136d01fe47cca7730daf9848a41b90988f70 -r c35325c3069824a3ed102001f6e4b9ad137bea57 doc/en/changelog.txt --- a/doc/en/changelog.txt +++ b/doc/en/changelog.txt @@ -4,4 +4,4 @@ Changelog history ================================= -.. include:: ../CHANGELOG +.. include:: ../../CHANGELOG https://bitbucket.org/hpk42/pytest/changeset/f88742e3fbc7/ changeset: f88742e3fbc7 user: t2y date: 2012-06-06 01:58:02 summary: added Japanese translation for 2.2.4 (79a5b776a6f3) affected #: 97 files Diff too large to display. 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. From commits-noreply at bitbucket.org Wed Jun 6 16:52:25 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jun 2012 14:52:25 -0000 Subject: [py-svn] commit/pytest: hpk42: extend marker section with a platform example Message-ID: <20120606145225.9845.93025@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/928db8912592/ changeset: 928db8912592 user: hpk42 date: 2012-06-06 16:34:13 summary: extend marker section with a platform example affected #: 1 file diff -r f88742e3fbc7f622c7ae41713bab7a8a925ab51b -r 928db89125925202e651dba1c90032350f44773f doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,7 +26,7 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 -- /home/hpk/venv/1/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED @@ -38,13 +38,13 @@ $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 -- /home/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.00 seconds ================== + ================== 1 passed, 1 deselected in 0.01 seconds ================== Registering markers ------------------------------------- @@ -143,7 +143,7 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 4 items test_server.py . @@ -155,7 +155,7 @@ $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 4 items test_mark_classlevel.py .. @@ -168,7 +168,7 @@ $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 4 items test_mark_classlevel.py .. @@ -223,23 +223,23 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 1 items test_someenv.py s - ======================== 1 skipped in 0.00 seconds ========================= + ======================== 1 skipped in 0.01 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 1 items test_someenv.py . - ========================= 1 passed in 0.00 seconds ========================= + ========================= 1 passed in 0.01 seconds ========================= The ``--markers`` option always gives you a list of available markers:: @@ -265,6 +265,8 @@ 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:: +.. regendoc:wipe + # content of test_mark_three_times.py import pytest pytestmark = pytest.mark.glob("module", x=1) @@ -279,19 +281,95 @@ test function. From a conftest file we can read it like this:: # content of conftest.py + import sys 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)) + sys.stdout.flush() Let's run this without capturing output and see what we get:: $ py.test -q -s - collecting ... collected 2 items - .. - 2 passed in 0.01 seconds + collecting ... collected 1 items glob args=('function',) kwargs={'x': 3} glob args=('class',) kwargs={'x': 2} glob args=('module',) kwargs={'x': 1} + . + 1 passed in 0.01 seconds + +marking platform specific tests with pytest +-------------------------------------------------------------- + +Consider you have a test suite which marks tests for particular platforms, +namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you +also have tests that run on all platforms and have no specific +marker. If you now want to have a way to only run the tests +for your particular platform, you could use the following plugin:: + +.. regendoc:wipe + + # content of conftest.py + # + import sys + import pytest + + ALL = set("osx linux2 win32".split()) + + def pytest_runtest_setup(item): + if isinstance(item, item.Function): + plat = sys.platform + if not hasattr(item.obj, plat): + if ALL.intersection(set(item.obj.__dict__)): + pytest.skip("cannot run on platform %s" %(plat)) + +then tests will be skipped if they were specified for a different platform. +Let's do a little test file to show how this looks like:: + + # content of test_plat.py + + import pytest + + @pytest.mark.osx + def test_if_apple_is_evil(): + pass + + @pytest.mark.linux2 + def test_if_linux_works(): + pass + + @pytest.mark.win32 + def test_if_win32_crashes(): + pass + + def test_runs_everywhere(): + pass + +then you will see two test skipped and two executed tests as expected:: + + $ py.test -rs # this option reports skip reasons + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 + collecting ... collected 4 items + + test_plat.py s.s. + ========================= short test summary info ========================== + SKIP [2] /home/hpk/tmp/doc-exec-222/conftest.py:12: cannot run on platform linux2 + + =================== 2 passed, 2 skipped in 0.01 seconds ==================== + +Note that if you specify a platform via the marker-command line option like this:: + + $ py.test -m linux2 + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 + collecting ... collected 4 items + + test_plat.py . + + =================== 3 tests deselected by "-m 'linux2'" ==================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== + +then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests. 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. From commits-noreply at bitbucket.org Thu Jun 7 12:40:02 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jun 2012 10:40:02 -0000 Subject: [py-svn] commit/pytest: hpk42: added an example on how to do python2/python3 customized test collection Message-ID: <20120607104002.30927.61038@bitbucket13.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/2815a3d12bd0/ changeset: 2815a3d12bd0 user: hpk42 date: 2012-06-07 12:39:53 summary: added an example on how to do python2/python3 customized test collection affected #: 1 file diff -r 928db89125925202e651dba1c90032350f44773f -r 2815a3d12bd049d952e213b0748fd7ff849e605b doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 2 items @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 collecting ... collected 3 items @@ -92,3 +92,55 @@ ============================= in 0.00 seconds ============================= + +customizing test collection to find all *.py files +--------------------------------------------------------- + +.. regendoc:wipe + +You can easily instruct py.test to discover tests from every python file:: + + + # content of pytest.ini + [pytest] + python_files = *.py + +However, many projects will have a ``setup.py`` which they don't want to be imported. Moreover, there may files only importable by a specific python version. +For such cases you can dynamically define files to be ignored by listing +them in a ``conftest.py`` file:: + + # content of conftest.py + import sys + + collect_ignore = ["setup.py"] + if sys.version_info[0] > 2: + collect_ignore.append("pkg/module_py2.py") + +And then if you have a module file like this:: + + # content of pkg/module_py2.py + def test_only_on_python2(): + try: + assert 0 + except Exception, e: + pass + +and a setup.py dummy file like this:: + + # content of setup.py + 0/0 # will raise exeption if imported + +then a pytest run on python2 will find the one test when run with a python2 +interpreters and will leave out the setup.py file:: + + $ py.test --collectonly + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 + collecting ... collected 1 items + + + + ============================= in 0.01 seconds ============================= + +If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection. + 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. From commits-noreply at bitbucket.org Thu Jun 7 19:19:00 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jun 2012 17:19:00 -0000 Subject: [py-svn] commit/pytest: hpk42: use doc-versions that can increment separately from the pytest version Message-ID: <20120607171900.30925.81826@bitbucket13.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/5c00fbfc6a2c/ changeset: 5c00fbfc6a2c user: hpk42 date: 2012-06-07 19:18:50 summary: use doc-versions that can increment separately from the pytest version affected #: 3 files diff -r 2815a3d12bd049d952e213b0748fd7ff849e605b -r 5c00fbfc6a2cfcd317e169d24b412053c927fbb7 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -49,10 +49,8 @@ # built documents. # # The full version, including alpha/beta/rc tags. -import pytest -release = pytest.__version__ # The short X.Y version. -version = ".".join(release.split(".")[:2]) +version = "2.2.4.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -r 2815a3d12bd049d952e213b0748fd7ff849e605b -r 5c00fbfc6a2cfcd317e169d24b412053c927fbb7 doc/ja/Makefile --- a/doc/ja/Makefile +++ b/doc/ja/Makefile @@ -40,7 +40,7 @@ -rm -rf $(BUILDDIR)/* install: html - rsync -avz _build/html/ pytest.org:/www/pytest.org/latest + rsync -avz _build/html/ pytest.org:/www/pytest.org/latest-ja installpdf: latexpdf @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/latest diff -r 2815a3d12bd049d952e213b0748fd7ff849e605b -r 5c00fbfc6a2cfcd317e169d24b412053c927fbb7 doc/ja/conf.py --- a/doc/ja/conf.py +++ b/doc/ja/conf.py @@ -49,10 +49,8 @@ # built documents. # # The full version, including alpha/beta/rc tags. -import pytest -release = pytest.__version__ # The short X.Y version. -version = ".".join(release.split(".")[:2]) +version = "2.2.4.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. 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. From commits-noreply at bitbucket.org Thu Jun 7 19:26:24 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jun 2012 17:26:24 -0000 Subject: [py-svn] commit/pytest: hpk42: also set release Message-ID: <20120607172624.18891.67365@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/5d0b5e727894/ changeset: 5d0b5e727894 user: hpk42 date: 2012-06-07 19:26:18 summary: also set release affected #: 2 files diff -r 5c00fbfc6a2cfcd317e169d24b412053c927fbb7 -r 5d0b5e72789428b1a481f0450779616f61eaba31 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -50,7 +50,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = "2.2.4.1" +version = release = "2.2.4.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -r 5c00fbfc6a2cfcd317e169d24b412053c927fbb7 -r 5d0b5e72789428b1a481f0450779616f61eaba31 doc/ja/conf.py --- a/doc/ja/conf.py +++ b/doc/ja/conf.py @@ -50,7 +50,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = "2.2.4.0" +version = release = "2.2.4.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. 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. From commits-noreply at bitbucket.org Fri Jun 8 16:55:11 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jun 2012 14:55:11 -0000 Subject: [py-svn] commit/py: hpk42: change iniconfig behaviour to only recognise comment chars if they start the line Message-ID: <20120608145511.2741.32929@bitbucket16.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/8af99f47795c/ changeset: 8af99f47795c user: hpk42 date: 2012-06-08 16:55:05 summary: change iniconfig behaviour to only recognise comment chars if they start the line affected #: 5 files diff -r 6be37b7cb55c6ac1da9e9afa4dbbae6c1f499fd2 -r 8af99f47795c5549af4f83f525904c778a036a04 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Changes between 1.4.8 and 1.4.9.DEV +================================================== + +- changed iniconfig parsing to better conform, now the chars ";" + and "#" only mark a comment at the stripped start of a line + Changes between 1.4.7 and 1.4.8 ================================================== diff -r 6be37b7cb55c6ac1da9e9afa4dbbae6c1f499fd2 -r 8af99f47795c5549af4f83f525904c778a036a04 py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.8' +__version__ = '1.4.9.dev1' from py import _apipkg diff -r 6be37b7cb55c6ac1da9e9afa4dbbae6c1f499fd2 -r 8af99f47795c5549af4f83f525904c778a036a04 py/_iniconfig.py --- a/py/_iniconfig.py +++ b/py/_iniconfig.py @@ -5,6 +5,8 @@ __all__ = ['IniConfig', 'ParseError'] +COMMENTCHARS = "#;" + class ParseError(Exception): def __init__(self, path, lineno, msg): Exception.__init__(self, path, lineno, msg) @@ -101,15 +103,21 @@ return result def _parseline(self, line, lineno): - # comments - line = line.split('#')[0].rstrip() - line = line.split(';')[0].rstrip() # blank lines + if iscommentline(line): + line = "" + else: + line = line.rstrip() if not line: return None, None # section - if line[0] == '[' and line[-1] == ']': - return line[1:-1], None + if line[0] == '[': + realline = line + for c in COMMENTCHARS: + line = line.split(c)[0].rstrip() + if line[-1] == "]": + return line[1:-1], None + return None, realline.strip() # value elif not line[0].isspace(): try: @@ -148,3 +156,7 @@ def __contains__(self, arg): return arg in self.sections + +def iscommentline(line): + c = line.lstrip()[:1] + return c in COMMENTCHARS diff -r 6be37b7cb55c6ac1da9e9afa4dbbae6c1f499fd2 -r 8af99f47795c5549af4f83f525904c778a036a04 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.8', + version='1.4.9.dev1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 6be37b7cb55c6ac1da9e9afa4dbbae6c1f499fd2 -r 8af99f47795c5549af4f83f525904c778a036a04 testing/test_iniconfig.py --- a/testing/test_iniconfig.py +++ b/testing/test_iniconfig.py @@ -1,5 +1,7 @@ import py +import pytest from py._iniconfig import IniConfig, ParseError, __all__ as ALL +from py._iniconfig import iscommentline from textwrap import dedent def pytest_generate_tests(metafunc): @@ -57,7 +59,7 @@ [] ), 'comment on value': ( - 'value = 1 # comment', + 'value = 1', [(0, None, 'value', '1')] ), @@ -69,10 +71,6 @@ '; comment', [] ), - 'comment2 on value': ( - 'value = 1 ; comment', - [(0, None, 'value', '1')] - ), 'comment2 on section': ( '[section] ;comment', @@ -100,7 +98,7 @@ ), } - + def parse(input): # only for testing purposes - _parse() does not use state except path ini = object.__new__(IniConfig) @@ -184,7 +182,7 @@ assert config.lineof('section2') == 3 assert config.lineof('section', 'value') == 2 assert config.lineof('section2','value') == 5 - + assert config['section'].lineof('value') == 2 assert config['section2'].lineof('value') == 5 @@ -288,3 +286,14 @@ def test_api_import(): assert ALL == ['IniConfig', 'ParseError'] + + at pytest.mark.parametrize("line", [ + "#qwe", + " #qwe", + ";qwe", + " ;qwe", +]) +def test_iscommentline_true(line): + assert iscommentline(line) + + Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Jun 8 22:27:25 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jun 2012 20:27:25 -0000 Subject: [py-svn] commit/apipkg: hpk42: bump version, fix tox.ini Message-ID: <20120608202725.25548.74328@bitbucket02.managed.contegix.com> 1 new commit in apipkg: https://bitbucket.org/hpk42/apipkg/changeset/f39ea665e1da/ changeset: f39ea665e1da user: hpk42 date: 2012-06-08 22:25:55 summary: bump version, fix tox.ini affected #: 4 files diff -r 7ecd687d0ae4774495c6a474a7ee7b4bb2283113 -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -1.2.dev +1.2 ---------------------------------------- - Allow to import from Aliasmodules (thanks Ralf Schmitt) diff -r 7ecd687d0ae4774495c6a474a7ee7b4bb2283113 -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 apipkg.py --- a/apipkg.py +++ b/apipkg.py @@ -9,7 +9,7 @@ import sys from types import ModuleType -__version__ = '1.2.dev7' +__version__ = '1.2' def initpkg(pkgname, exportdefs, attr=dict()): """ initialize given package from the export definitions. """ diff -r 7ecd687d0ae4774495c6a474a7ee7b4bb2283113 -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 setup.py --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ description= 'apipkg: namespace control and lazy-import mechanism', long_description = open('README.txt').read(), - version='1.2.dev7', + version='1.2', url='http://bitbucket.org/hpk42/apipkg', license='MIT License', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -38,4 +38,4 @@ ) if __name__ == '__main__': - main() + main() \ No newline at end of file diff -r 7ecd687d0ae4774495c6a474a7ee7b4bb2283113 -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 tox.ini --- a/tox.ini +++ b/tox.ini @@ -6,4 +6,4 @@ [testenv] commands=py.test --junitxml={envlogdir}/junit-{envname}.xml [] -deps= py +deps= pytest Repository URL: https://bitbucket.org/hpk42/apipkg/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Jun 8 22:31:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jun 2012 20:31:36 -0000 Subject: [py-svn] commit/py: hpk42: update apipkg in pylib Message-ID: <20120608203136.19502.13876@bitbucket01.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/3b13d4cc8424/ changeset: 3b13d4cc8424 user: hpk42 date: 2012-06-08 22:31:11 summary: update apipkg in pylib affected #: 2 files diff -r 8af99f47795c5549af4f83f525904c778a036a04 -r 3b13d4cc842450768ad74639a5612c2d18114479 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ - changed iniconfig parsing to better conform, now the chars ";" and "#" only mark a comment at the stripped start of a line +- include recent apipkg-1.2 Changes between 1.4.7 and 1.4.8 ================================================== diff -r 8af99f47795c5549af4f83f525904c778a036a04 -r 3b13d4cc842450768ad74639a5612c2d18114479 py/_apipkg.py --- a/py/_apipkg.py +++ b/py/_apipkg.py @@ -9,7 +9,7 @@ import sys from types import ModuleType -__version__ = '1.2.dev6' +__version__ = '1.2' def initpkg(pkgname, exportdefs, attr=dict()): """ initialize given package from the export definitions. """ Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 9 13:11:14 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jun 2012 11:11:14 -0000 Subject: [py-svn] commit/py: hpk42: improve terminalwriter reline/line printing behaviour (as used by detox) Message-ID: <20120609111114.25547.7085@bitbucket02.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/4dfde5019cb3/ changeset: 4dfde5019cb3 user: hpk42 date: 2012-06-09 13:11:04 summary: improve terminalwriter reline/line printing behaviour (as used by detox) affected #: 3 files diff -r 3b13d4cc842450768ad74639a5612c2d18114479 -r 4dfde5019cb35a1506aca3607d7de5245f762335 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,8 @@ - changed iniconfig parsing to better conform, now the chars ";" and "#" only mark a comment at the stripped start of a line - include recent apipkg-1.2 +- change internal terminalwriter.line/reline logic to more nicely + support file spinners Changes between 1.4.7 and 1.4.8 ================================================== diff -r 3b13d4cc842450768ad74639a5612c2d18114479 -r 4dfde5019cb35a1506aca3607d7de5245f762335 py/_io/terminalwriter.py --- a/py/_io/terminalwriter.py +++ b/py/_io/terminalwriter.py @@ -105,8 +105,6 @@ Blue=44, Purple=45, Cyan=46, White=47, bold=1, light=2, blink=5, invert=7) - _newline = None # the last line printed - # XXX deprecate stringio argument def __init__(self, file=None, stringio=False, encoding=None): if file is None: @@ -120,6 +118,7 @@ self._file = file self.fullwidth = get_terminal_width() self.hasmarkup = should_do_markup(file) + self._lastlen = 0 def _escaped(self, text, esc): if esc and self.hasmarkup: @@ -182,31 +181,22 @@ return s def line(self, s='', **kw): - if self._newline == False: - self.write("\n") self.write(s, **kw) + self._checkfill(s) self.write('\n') - self._newline = True - def reline(self, line, **opts): + def reline(self, line, **kw): if not self.hasmarkup: raise ValueError("cannot use rewrite-line without terminal") - if not self._newline: - self.write("\r") - self.write(line, **opts) - # see if we need to fill up some spaces at the end - # xxx have a more exact lastlinelen working from self.write? - lenline = len(line) - try: - lastlen = self._lastlinelen - except AttributeError: - pass - else: - if lenline < lastlen: - self.write(" " * (lastlen - lenline + 1)) - self._lastlinelen = lenline - self._newline = False + self.write(line, **kw) + self._checkfill(line) + self.write('\r') + self._lastlen = len(line) + def _checkfill(self, line): + diff2last = self._lastlen - len(line) + if diff2last > 0: + self.write(" " * diff2last) class Win32ConsoleWriter(TerminalWriter): def write(self, s, **kw): diff -r 3b13d4cc842450768ad74639a5612c2d18114479 -r 4dfde5019cb35a1506aca3607d7de5245f762335 testing/io_/test_terminalwriter.py --- a/testing/io_/test_terminalwriter.py +++ b/testing/io_/test_terminalwriter.py @@ -106,6 +106,7 @@ io.seek(0) return io.readlines() tw.getlines = getlines + tw.getvalue = lambda: "".join(getlines()) return tw def test_line(self, tw): @@ -170,18 +171,19 @@ pytest.raises(ValueError, lambda: tw.reline("x")) tw.hasmarkup = True tw.reline("0 1 2") - l = "".join(tw.getlines()).split("\n") + tw.getlines() + l = tw.getvalue().split("\n") assert len(l) == 2 tw.reline("0 1 3") - l = "".join(tw.getlines()).split("\n") + l = tw.getvalue().split("\n") assert len(l) == 2 - assert l[1].endswith("\r0 1 3") - tw.line("something") - l = "".join(tw.getlines()).split("\n") - assert len(l) == 4 + assert l[1].endswith("0 1 3\r") + tw.line("so") + l = tw.getvalue().split("\n") + assert len(l) == 3 assert l[-1] == "" - out = "\n".join(l) - assert out.endswith("\nsomething\n") + assert l[1] == ("0 1 2\r0 1 3\rso ") + assert l[0] == "hello" Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 9 23:01:53 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jun 2012 21:01:53 -0000 Subject: [py-svn] commit/apipkg: hpk42: fix issue2 - test use correct ModuleType on Jython Message-ID: <20120609210153.2046.23706@bitbucket12.managed.contegix.com> 1 new commit in apipkg: https://bitbucket.org/hpk42/apipkg/changeset/7306d9657bd0/ changeset: 7306d9657bd0 user: hpk42 date: 2012-06-09 23:00:15 summary: fix issue2 - test use correct ModuleType on Jython affected #: 5 files diff -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 -r 7306d9657bd099fbbc01369e7039837ace479bf0 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +1.3.dev +---------------------------------------- + +- fix issue2 - adapt tests on Jython + 1.2 ---------------------------------------- diff -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 -r 7306d9657bd099fbbc01369e7039837ace479bf0 apipkg.py --- a/apipkg.py +++ b/apipkg.py @@ -9,7 +9,7 @@ import sys from types import ModuleType -__version__ = '1.2' +__version__ = '1.3.dev' def initpkg(pkgname, exportdefs, attr=dict()): """ initialize given package from the export definitions. """ diff -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 -r 7306d9657bd099fbbc01369e7039837ace479bf0 setup.py --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ description= 'apipkg: namespace control and lazy-import mechanism', long_description = open('README.txt').read(), - version='1.2', + version='1.3.dev', url='http://bitbucket.org/hpk42/apipkg', license='MIT License', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 -r 7306d9657bd099fbbc01369e7039837ace479bf0 test_apipkg.py --- a/test_apipkg.py +++ b/test_apipkg.py @@ -6,6 +6,7 @@ # # test support for importing modules # +ModuleType = py.std.types.ModuleType class TestRealModule: @@ -202,7 +203,7 @@ return ns def test_initpkg_replaces_sysmodules(monkeypatch): - mod = type(sys)('hello') + mod = ModuleType('hello') monkeypatch.setitem(sys.modules, 'hello', mod) apipkg.initpkg('hello', {'x': 'os.path:abspath'}) newmod = sys.modules['hello'] @@ -210,7 +211,7 @@ assert newmod.x == py.std.os.path.abspath def test_initpkg_transfers_attrs(monkeypatch): - mod = type(sys)('hello') + mod = ModuleType('hello') mod.__version__ = 10 mod.__file__ = "hello.py" mod.__loader__ = "loader" @@ -225,7 +226,7 @@ assert newmod.__doc__ == mod.__doc__ def test_initpkg_nodoc(monkeypatch): - mod = type(sys)('hello') + mod = ModuleType('hello') mod.__file__ = "hello.py" monkeypatch.setitem(sys.modules, 'hello', mod) apipkg.initpkg('hello', {}) @@ -233,7 +234,7 @@ assert not newmod.__doc__ def test_initpkg_overwrite_doc(monkeypatch): - hello = type(sys)('hello') + hello = ModuleType('hello') hello.__doc__ = "this is the documentation" monkeypatch.setitem(sys.modules, 'hello', hello) apipkg.initpkg('hello', {"__doc__": "sys:__doc__"}) @@ -242,7 +243,7 @@ assert newhello.__doc__ == sys.__doc__ def test_initpkg_not_transfers_not_existing_attrs(monkeypatch): - mod = type(sys)('hello') + mod = ModuleType('hello') mod.__file__ = "hello.py" monkeypatch.setitem(sys.modules, 'hello', mod) apipkg.initpkg('hello', {}) @@ -253,7 +254,7 @@ assert not hasattr(newmod, '__path__') def test_initpkg_defaults(monkeypatch): - mod = type(sys)('hello') + mod = ModuleType('hello') monkeypatch.setitem(sys.modules, 'hello', mod) apipkg.initpkg('hello', {}) newmod = sys.modules['hello'] diff -r f39ea665e1da34aad8f0d459d5b0a3352e019e59 -r 7306d9657bd099fbbc01369e7039837ace479bf0 tox.ini --- a/tox.ini +++ b/tox.ini @@ -6,4 +6,7 @@ [testenv] commands=py.test --junitxml={envlogdir}/junit-{envname}.xml [] -deps= pytest +deps=pytest + +[testenv:jython] +commands=py.test-jython --junitxml={envlogdir}/junit-{envname}.xml [] Repository URL: https://bitbucket.org/hpk42/apipkg/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 9 23:06:12 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jun 2012 21:06:12 -0000 Subject: [py-svn] commit/py: hpk42: remove apipkg.py test to lower redundancy Message-ID: <20120609210612.24999.23698@bitbucket03.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/c467a077be40/ changeset: c467a077be40 user: hpk42 date: 2012-06-09 23:05:55 summary: remove apipkg.py test to lower redundancy affected #: 1 file diff -r 4dfde5019cb35a1506aca3607d7de5245f762335 -r c467a077be406daf8890fa239c9fdccf9be195d3 testing/test_apipkg.py --- a/testing/test_apipkg.py +++ /dev/null @@ -1,473 +0,0 @@ -import types -import sys -import py -import py._apipkg as apipkg -import subprocess -import types - -ModuleType = types.ModuleType -# -# test support for importing modules -# - -class TestRealModule: - - def setup_class(cls): - cls.tmpdir = py.test.ensuretemp('test_apipkg') - sys.path = [str(cls.tmpdir)] + sys.path - pkgdir = cls.tmpdir.ensure('realtest', dir=1) - - tfile = pkgdir.join('__init__.py') - tfile.write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, { - 'x': { - 'module': { - '__doc__': '_xyz.testmodule:__doc__', - 'mytest0': '_xyz.testmodule:mytest0', - 'mytest1': '_xyz.testmodule:mytest1', - 'MyTest': '_xyz.testmodule:MyTest', - } - } - } - ) - """)) - - ipkgdir = cls.tmpdir.ensure("_xyz", dir=1) - tfile = ipkgdir.join('testmodule.py') - ipkgdir.ensure("__init__.py") - tfile.write(py.code.Source(""" - 'test module' - from _xyz.othermodule import MyTest - - #__all__ = ['mytest0', 'mytest1', 'MyTest'] - - def mytest0(): - pass - def mytest1(): - pass - """)) - ipkgdir.join("othermodule.py").write("class MyTest: pass") - - def setup_method(self, *args): - # Unload the test modules before each test. - module_names = ['realtest', 'realtest.x', 'realtest.x.module'] - for modname in module_names: - if modname in sys.modules: - del sys.modules[modname] - - def test_realmodule(self): - import realtest.x - assert 'realtest.x.module' in sys.modules - assert getattr(realtest.x.module, 'mytest0') - - def test_realmodule_repr(self): - import realtest.x - assert "" == repr(realtest.x) - - def test_realmodule_from(self): - from realtest.x import module - assert getattr(module, 'mytest1') - - def test_realmodule__all__(self): - import realtest.x.module - assert realtest.x.__all__ == ['module'] - assert len(realtest.x.module.__all__) == 4 - - def test_realmodule_dict_import(self): - "Test verifying that accessing the __dict__ invokes the import" - import realtest.x.module - moddict = realtest.x.module.__dict__ - assert 'mytest0' in moddict - assert 'mytest1' in moddict - assert 'MyTest' in moddict - - def test_realmodule___doc__(self): - """test whether the __doc__ attribute is set properly from initpkg""" - import realtest.x.module - print (realtest.x.module.__map__) - assert realtest.x.module.__doc__ == 'test module' - -class TestScenarios: - def test_relative_import(self, monkeypatch, tmpdir): - pkgdir = tmpdir.mkdir("mymodule") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - '__doc__': '.submod:maindoc', - 'x': '.submod:x', - 'y': { - 'z': '.submod:x' - }, - }) - """)) - pkgdir.join('submod.py').write("x=3\nmaindoc='hello'") - monkeypatch.syspath_prepend(tmpdir) - import mymodule - assert isinstance(mymodule, apipkg.ApiModule) - assert mymodule.x == 3 - assert mymodule.__doc__ == 'hello' - assert mymodule.y.z == 3 - - def test_recursive_import(self, monkeypatch, tmpdir): - pkgdir = tmpdir.mkdir("recmodule") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - 'some': '.submod:someclass', - }) - """)) - pkgdir.join('submod.py').write(py.code.Source(""" - import recmodule - class someclass: pass - print (recmodule.__dict__) - """)) - monkeypatch.syspath_prepend(tmpdir) - import recmodule - assert isinstance(recmodule, apipkg.ApiModule) - assert recmodule.some.__name__ == "someclass" - - def test_module_alias_import(self, monkeypatch, tmpdir): - pkgdir = tmpdir.mkdir("aliasimport") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - 'some': 'os.path', - }) - """)) - monkeypatch.syspath_prepend(tmpdir) - import aliasimport - for k, v in py.std.os.path.__dict__.items(): - assert getattr(aliasimport.some, k) == v - - def test_from_module_alias_import(self, monkeypatch, tmpdir): - pkgdir = tmpdir.mkdir("fromaliasimport") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - 'some': 'os.path', - }) - """)) - monkeypatch.syspath_prepend(tmpdir) - from fromaliasimport.some import join - assert join is py.std.os.path.join - -def xtest_nested_absolute_imports(): - import email - api_email = apipkg.ApiModule('email',{ - 'message2': { - 'Message': 'email.message:Message', - }, - }) - # nesting is supposed to put nested items into sys.modules - assert 'email.message2' in sys.modules - -# alternate ideas for specifying package + preliminary code -# -def test_parsenamespace(): - spec = """ - path.local __.path.local::LocalPath - path.svnwc __.path.svnwc::WCCommandPath - test.raises __.test.outcome::raises - """ - d = parsenamespace(spec) - print (d) - assert d == {'test': {'raises': '__.test.outcome::raises'}, - 'path': {'svnwc': '__.path.svnwc::WCCommandPath', - 'local': '__.path.local::LocalPath'} - } -def xtest_parsenamespace_errors(): - py.test.raises(ValueError, """ - parsenamespace('path.local xyz') - """) - py.test.raises(ValueError, """ - parsenamespace('x y z') - """) - -def parsenamespace(spec): - ns = {} - for line in spec.split("\n"): - line = line.strip() - if not line or line[0] == "#": - continue - parts = [x.strip() for x in line.split()] - if len(parts) != 2: - raise ValueError("Wrong format: %r" %(line,)) - apiname, spec = parts - if not spec.startswith("__"): - raise ValueError("%r does not start with __" %(spec,)) - apinames = apiname.split(".") - cur = ns - for name in apinames[:-1]: - cur.setdefault(name, {}) - cur = cur[name] - cur[apinames[-1]] = spec - return ns - -def test_initpkg_replaces_sysmodules(monkeypatch): - mod = ModuleType('hello') - monkeypatch.setitem(sys.modules, 'hello', mod) - apipkg.initpkg('hello', {'x': 'os.path:abspath'}) - newmod = sys.modules['hello'] - assert newmod != mod - assert newmod.x == py.std.os.path.abspath - -def test_initpkg_transfers_attrs(monkeypatch): - mod = ModuleType('hello') - mod.__version__ = 10 - mod.__file__ = "hello.py" - mod.__loader__ = "loader" - mod.__doc__ = "this is the documentation" - monkeypatch.setitem(sys.modules, 'hello', mod) - apipkg.initpkg('hello', {}) - newmod = sys.modules['hello'] - assert newmod != mod - assert newmod.__file__ == py.path.local(mod.__file__) - assert newmod.__version__ == mod.__version__ - assert newmod.__loader__ == mod.__loader__ - assert newmod.__doc__ == mod.__doc__ - -def test_initpkg_nodoc(monkeypatch): - mod = ModuleType('hello') - mod.__file__ = "hello.py" - monkeypatch.setitem(sys.modules, 'hello', mod) - apipkg.initpkg('hello', {}) - newmod = sys.modules['hello'] - assert not newmod.__doc__ - -def test_initpkg_overwrite_doc(monkeypatch): - hello = ModuleType('hello') - hello.__doc__ = "this is the documentation" - monkeypatch.setitem(sys.modules, 'hello', hello) - apipkg.initpkg('hello', {"__doc__": "sys:__doc__"}) - newhello = sys.modules['hello'] - assert newhello != hello - assert newhello.__doc__ == sys.__doc__ - -def test_initpkg_not_transfers_not_existing_attrs(monkeypatch): - mod = ModuleType('hello') - mod.__file__ = "hello.py" - monkeypatch.setitem(sys.modules, 'hello', mod) - apipkg.initpkg('hello', {}) - newmod = sys.modules['hello'] - assert newmod != mod - assert newmod.__file__ == py.path.local(mod.__file__) - assert not hasattr(newmod, '__loader__') - assert not hasattr(newmod, '__path__') - -def test_initpkg_defaults(monkeypatch): - mod = ModuleType('hello') - monkeypatch.setitem(sys.modules, 'hello', mod) - apipkg.initpkg('hello', {}) - newmod = sys.modules['hello'] - assert newmod.__file__ == None - assert not hasattr(newmod, '__version__') - -def test_name_attribute(): - api = apipkg.ApiModule('name_test', { - 'subpkg': {}, - }) - assert api.__name__ == 'name_test' - assert api.subpkg.__name__ == 'name_test.subpkg' - -def test_error_loading_one_element(monkeypatch, tmpdir): - pkgdir = tmpdir.mkdir("errorloading1") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - 'x': '.notexists:x', - 'y': '.submod:y' - }, - ) - """)) - pkgdir.join('submod.py').write("y=0") - monkeypatch.syspath_prepend(tmpdir) - import errorloading1 - assert isinstance(errorloading1, apipkg.ApiModule) - assert errorloading1.y == 0 - py.test.raises(ImportError, 'errorloading1.x') - py.test.raises(ImportError, 'errorloading1.x') - -def test_onfirstaccess(tmpdir, monkeypatch): - pkgdir = tmpdir.mkdir("firstaccess") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - '__onfirstaccess__': '.submod:init', - 'l': '.submod:l', - }, - ) - """)) - pkgdir.join('submod.py').write(py.code.Source(""" - l = [] - def init(): - l.append(1) - """)) - monkeypatch.syspath_prepend(tmpdir) - import firstaccess - assert isinstance(firstaccess, apipkg.ApiModule) - assert len(firstaccess.l) == 1 - assert len(firstaccess.l) == 1 - assert '__onfirstaccess__' not in firstaccess.__all__ - - at py.test.mark.multi(mode=['attr', 'dict', 'onfirst']) -def test_onfirstaccess_setsnewattr(tmpdir, monkeypatch, mode): - pkgname = tmpdir.basename.replace("-", "") - pkgdir = tmpdir.mkdir(pkgname) - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, exportdefs={ - '__onfirstaccess__': '.submod:init', - }, - ) - """)) - pkgdir.join('submod.py').write(py.code.Source(""" - def init(): - import %s as pkg - pkg.newattr = 42 - """ % pkgname)) - monkeypatch.syspath_prepend(tmpdir) - mod = __import__(pkgname) - assert isinstance(mod, apipkg.ApiModule) - if mode == 'attr': - assert mod.newattr == 42 - elif mode == "dict": - print (list(mod.__dict__.keys())) - assert 'newattr' in mod.__dict__ - elif mode == "onfirst": - assert not hasattr(mod, '__onfirstaccess__') - assert not hasattr(mod, '__onfirstaccess__') - assert '__onfirstaccess__' not in vars(mod) - -def test_bpython_getattr_override(tmpdir, monkeypatch): - def patchgetattr(self, name): - raise AttributeError(name) - monkeypatch.setattr(apipkg.ApiModule, '__getattr__', patchgetattr) - api = apipkg.ApiModule('bpy', { - 'abspath': 'os.path:abspath', - }) - d = api.__dict__ - assert 'abspath' in d - - - - -def test_chdir_with_relative_imports_shouldnt_break_lazy_loading(tmpdir): - tmpdir.join('apipkg.py').write(py.code.Source(apipkg)) - pkg = tmpdir.mkdir('pkg') - messy = tmpdir.mkdir('messy') - pkg.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, { - 'test': '.sub:test', - }) - """)) - pkg.join('sub.py').write('def test(): pass') - - tmpdir.join('main.py').write(py.code.Source(""" - import os - import sys - sys.path.insert(0, '') - import pkg - import py - print(py.__file__) - py.builtin.print_(pkg.__path__, file=sys.stderr) - py.builtin.print_(pkg.__file__, file=sys.stderr) - py.builtin.print_(pkg, file=sys.stderr) - os.chdir('messy') - pkg.test() - assert os.path.isabs(pkg.sub.__file__), pkg.sub.__file__ - """)) - res = subprocess.call( - [py.std.sys.executable, 'main.py'], - cwd=str(tmpdir), - ) - assert res == 0 - - -def test_dotted_name_lookup(tmpdir, monkeypatch): - pkgdir = tmpdir.mkdir("dotted_name_lookup") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, dict(abs='os:path.abspath')) - """)) - monkeypatch.syspath_prepend(tmpdir) - import dotted_name_lookup - assert dotted_name_lookup.abs == py.std.os.path.abspath - -def test_extra_attributes(tmpdir, monkeypatch): - pkgdir = tmpdir.mkdir("extra_attributes") - pkgdir.join('__init__.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, dict(abs='os:path.abspath'), dict(foo='bar')) - """)) - monkeypatch.syspath_prepend(tmpdir) - import extra_attributes - assert extra_attributes.foo == 'bar' - -def test_aliasmodule_repr(): - am = apipkg.AliasModule("mymod", "sys") - r = repr(am) - assert "" == r - am.version - assert repr(am) == r - -def test_aliasmodule_proxy_methods(tmpdir, monkeypatch): - pkgdir = tmpdir - pkgdir.join('aliasmodule_proxy.py').write(py.code.Source(""" - def doit(): - return 42 - """)) - - pkgdir.join('my_aliasmodule_proxy.py').write(py.code.Source(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, dict(proxy='aliasmodule_proxy')) - - def doit(): - return 42 - """)) - - monkeypatch.syspath_prepend(tmpdir) - import aliasmodule_proxy as orig - from my_aliasmodule_proxy import proxy - - doit = proxy.doit - assert doit is orig.doit - - del proxy.doit - py.test.raises(AttributeError, "orig.doit") - - proxy.doit = doit - assert orig.doit is doit - -def test_aliasmodule_nested_import_with_from(tmpdir, monkeypatch): - import os - pkgdir = tmpdir.mkdir("api1") - pkgdir.ensure("__init__.py").write(py.std.textwrap.dedent(""" - import py._apipkg as apipkg - apipkg.initpkg(__name__, { - 'os2': 'api2', - 'os2.path': 'api2.path2', - }) - """)) - tmpdir.join("api2.py").write(py.std.textwrap.dedent(""" - import os, sys - from os import path - sys.modules['api2.path2'] = path - x = 3 - """)) - monkeypatch.syspath_prepend(tmpdir) - from api1 import os2 - from api1.os2.path import abspath - assert abspath == os.path.abspath - # check that api1.os2 mirrors os.* - assert os2.x == 3 - import api1 - assert 'os2.path' not in api1.__dict__ - - -def test_initpkg_without_old_module(): - apipkg.initpkg("initpkg_without_old_module", - dict(modules="sys:modules")) - from initpkg_without_old_module import modules - assert modules is sys.modules Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Jun 11 14:24:53 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 11 Jun 2012 12:24:53 -0000 Subject: [py-svn] commit/pytest: flub: Don't use deprecated API in example Message-ID: <20120611122453.9960.85706@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/3bdb19989d5e/ changeset: 3bdb19989d5e user: flub date: 2012-06-11 14:24:30 summary: Don't use deprecated API in example affected #: 1 file diff -r 5d0b5e72789428b1a481f0450779616f61eaba31 -r 3bdb19989d5e3244730305cd570963b94c7599fe doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -201,7 +201,7 @@ "env(name): mark test to run only on named environment") def pytest_runtest_setup(item): - if not isinstance(item, item.Function): + if not isinstance(item, pytest.Function): return if hasattr(item.obj, 'env'): envmarker = getattr(item.obj, 'env') 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. From commits-noreply at bitbucket.org Mon Jun 11 16:26:53 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 11 Jun 2012 14:26:53 -0000 Subject: [py-svn] commit/pytest: hpk42: fix ReST errors, increment doc-version and push to pytest.org Message-ID: <20120611142653.31962.24006@bitbucket15.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/a6b5be9d2809/ changeset: a6b5be9d2809 user: hpk42 date: 2012-06-11 16:24:42 summary: fix ReST errors, increment doc-version and push to pytest.org affected #: 3 files diff -r 3bdb19989d5e3244730305cd570963b94c7599fe -r a6b5be9d280994ebb5eda37c38b3ff9c17963772 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -11,6 +11,14 @@ # All configuration values have a default; values that are commented out # serve to show the default. +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +# The short X.Y version. +version = release = "2.2.4.2" + import sys, os # If extensions (or modules to document with autodoc) are in another directory, @@ -44,13 +52,6 @@ project = u'pytest' copyright = u'2011, holger krekel et alii' -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The full version, including alpha/beta/rc tags. -# The short X.Y version. -version = release = "2.2.4.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -r 3bdb19989d5e3244730305cd570963b94c7599fe -r a6b5be9d280994ebb5eda37c38b3ff9c17963772 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -262,11 +262,11 @@ .. versionadded: 2.2.2 +.. regendoc:wipe + 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:: -.. regendoc:wipe - # content of test_mark_three_times.py import pytest pytestmark = pytest.mark.glob("module", x=1) @@ -303,14 +303,14 @@ marking platform specific tests with pytest -------------------------------------------------------------- +.. regendoc:wipe + Consider you have a test suite which marks tests for particular platforms, namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you also have tests that run on all platforms and have no specific marker. If you now want to have a way to only run the tests for your particular platform, you could use the following plugin:: -.. regendoc:wipe - # content of conftest.py # import sys diff -r 3bdb19989d5e3244730305cd570963b94c7599fe -r a6b5be9d280994ebb5eda37c38b3ff9c17963772 doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -93,7 +93,7 @@ ============================= in 0.00 seconds ============================= -customizing test collection to find all *.py files +customizing test collection to find all .py files --------------------------------------------------------- .. regendoc:wipe 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. From commits-noreply at bitbucket.org Mon Jun 11 19:08:32 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 11 Jun 2012 17:08:32 -0000 Subject: [py-svn] commit/py: hpk42: fix visit() bug Message-ID: <20120611170832.17386.85760@bitbucket01.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/5e251d9b4c21/ changeset: 5e251d9b4c21 user: hpk42 date: 2012-06-11 19:08:25 summary: fix visit() bug affected #: 5 files diff -r c467a077be406daf8890fa239c9fdccf9be195d3 -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 1.4.8 and 1.4.9.DEV ================================================== +- fix bug of path.visit() which would not recognize glob-style patterns + for the "rec" recursion argument - changed iniconfig parsing to better conform, now the chars ";" and "#" only mark a comment at the stripped start of a line - include recent apipkg-1.2 diff -r c467a077be406daf8890fa239c9fdccf9be195d3 -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.9.dev1' +__version__ = '1.4.9.dev2' from py import _apipkg diff -r c467a077be406daf8890fa239c9fdccf9be195d3 -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea py/_path/common.py --- a/py/_path/common.py +++ b/py/_path/common.py @@ -67,7 +67,7 @@ except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY): # EBUSY feels not entirely correct, # but its kind of necessary since ENOMEDIUM - # is not accessible in python + # is not accessible in python for name in self._depend_on_existence: if name in kw: if kw.get(name): @@ -177,7 +177,7 @@ exists=1 # exists You can specify multiple checker definitions, for example:: - + path.check(file=1, link=1) # a link pointing to a file """ if not kw: @@ -331,7 +331,7 @@ if isinstance(fil, str): fil = FNMatcher(fil) if isinstance(rec, str): - self.rec = fnmatch(fil) + self.rec = FNMatcher(rec) elif not hasattr(rec, '__call__') and rec: self.rec = lambda path: True else: diff -r c467a077be406daf8890fa239c9fdccf9be195d3 -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.9.dev1', + version='1.4.9.dev2', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r c467a077be406daf8890fa239c9fdccf9be195d3 -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea testing/path/test_local.py --- a/testing/path/test_local.py +++ b/testing/path/test_local.py @@ -213,6 +213,14 @@ # check that breadth comes last assert l[2] == p3 + def test_visit_rec_fnmatch(self, tmpdir): + p1 = tmpdir.ensure("a","123") + p2 = tmpdir.ensure(".b","345") + l = list(tmpdir.visit("???", rec="[!.]*")) + assert len(l) == 1 + # check that breadth comes last + assert l[0] == p1 + class TestExecutionOnWindows: pytestmark = win32only Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Tue Jun 12 12:37:24 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jun 2012 10:37:24 -0000 Subject: [py-svn] commit/py: hpk42: prepare 1.4.9 release Message-ID: <20120612103724.21111.85351@bitbucket12.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/abfabd07a1d3/ changeset: abfabd07a1d3 user: hpk42 date: 2012-06-12 12:34:19 summary: prepare 1.4.9 release affected #: 3 files diff -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea -r abfabd07a1d328f13c730e8a50d80d2e470afd3b CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Changes between 1.4.8 and 1.4.9.DEV +Changes between 1.4.8 and 1.4.9 ================================================== - fix bug of path.visit() which would not recognize glob-style patterns diff -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea -r abfabd07a1d328f13c730e8a50d80d2e470afd3b py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.9.dev2' +__version__ = '1.4.9' from py import _apipkg diff -r 5e251d9b4c2104e46a43800a358bb0144ebe81ea -r abfabd07a1d328f13c730e8a50d80d2e470afd3b setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.9.dev2', + version='1.4.9', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Tue Jun 12 13:41:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jun 2012 11:41:36 -0000 Subject: [py-svn] commit/pytest: hpk42: fix comment handling Message-ID: <20120612114136.5978.36144@bitbucket15.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/eabc28a991bb/ changeset: eabc28a991bb user: hpk42 date: 2012-06-12 13:41:29 summary: fix comment handling affected #: 1 file diff -r a6b5be9d280994ebb5eda37c38b3ff9c17963772 -r eabc28a991bb3675fb06c1a7318537338886fcc2 tox.ini --- a/tox.ini +++ b/tox.ini @@ -71,7 +71,8 @@ [pytest] minversion=2.0 plugins=pytester -addopts= -rxs #--pyargs --doctest-modules --ignore=.tox +#--pyargs --doctest-modules --ignore=.tox +addopts= -rxs rsyncdirs=tox.ini pytest.py _pytest testing python_files=test_*.py *_test.py python_classes=Test Acceptance 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. From commits-noreply at bitbucket.org Wed Jun 13 16:36:26 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 13 Jun 2012 14:36:26 -0000 Subject: [py-svn] commit/py: hpk42: Added tag 1.4.9 for changeset abfabd07a1d3 Message-ID: <20120613143626.11807.35293@bitbucket15.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/f2b3a3da15d3/ changeset: f2b3a3da15d3 user: hpk42 date: 2012-06-13 15:50:37 summary: Added tag 1.4.9 for changeset abfabd07a1d3 affected #: 1 file diff -r abfabd07a1d328f13c730e8a50d80d2e470afd3b -r f2b3a3da15d3ac8fe3641302504ab71973ea4d28 .hgtags --- a/.hgtags +++ b/.hgtags @@ -38,3 +38,4 @@ d9951e3bdbc765e73835ae13012f6a074d13d8bf 1.4.4 b827dd156a36753e32c7f3f15ce82d6fe9e356c8 1.4.6 f15726f9e5a67cc6221c499affa4840e9d591763 1.4.7 +abfabd07a1d328f13c730e8a50d80d2e470afd3b 1.4.9 Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Jun 14 17:41:09 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jun 2012 15:41:09 -0000 Subject: [py-svn] commit/apipkg: hpk42: fix envlist Message-ID: <20120614154109.18544.1929@bitbucket01.managed.contegix.com> 1 new commit in apipkg: https://bitbucket.org/hpk42/apipkg/changeset/99c97a16360a/ changeset: 99c97a16360a user: hpk42 date: 2012-06-14 17:41:03 summary: fix envlist affected #: 1 file diff -r 7306d9657bd099fbbc01369e7039837ace479bf0 -r 99c97a16360ab551455e00861c3e92269d3fa3f7 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py26,py25,py24,py31 +envlist=py27,py26,py25,py24,py31,py32,jython [tox:hudson] sdistsrc={distshare}/apipkg-* Repository URL: https://bitbucket.org/hpk42/apipkg/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Jun 14 17:41:40 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jun 2012 15:41:40 -0000 Subject: [py-svn] commit/apipkg: hpk42: Added tag 1.2 for changeset 99c97a16360a Message-ID: <20120614154140.9144.99608@bitbucket12.managed.contegix.com> 1 new commit in apipkg: https://bitbucket.org/hpk42/apipkg/changeset/2f5164953a4a/ changeset: 2f5164953a4a user: hpk42 date: 2012-06-14 17:41:30 summary: Added tag 1.2 for changeset 99c97a16360a affected #: 1 file diff -r 99c97a16360ab551455e00861c3e92269d3fa3f7 -r 2f5164953a4aa40fa974eb414c4adc5bf5ca2f32 .hgtags --- a/.hgtags +++ b/.hgtags @@ -2,3 +2,4 @@ 976aa691cfaeeb9793057d09ae9f9e6fd162be69 1.0b5 6cb3976c8d8aac3d332ed8f507cfdad34b4279a1 1.0b6 d83f3dccbd847d054cacaaca24f4a93e04f57219 1.0 +99c97a16360ab551455e00861c3e92269d3fa3f7 1.2 Repository URL: https://bitbucket.org/hpk42/apipkg/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Jun 14 17:43:52 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jun 2012 15:43:52 -0000 Subject: [py-svn] commit/py: hpk42: bump version to 1.4.10.dev1 Message-ID: <20120614154352.32175.24067@bitbucket13.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/0fca612a4bbd/ changeset: 0fca612a4bbd user: hpk42 date: 2012-06-14 17:43:46 summary: bump version to 1.4.10.dev1 affected #: 2 files diff -r f2b3a3da15d3ac8fe3641302504ab71973ea4d28 -r 0fca612a4bbdb14513a2b014c8459e7859f2fbc7 py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.9' +__version__ = '1.4.10.dev1' from py import _apipkg diff -r f2b3a3da15d3ac8fe3641302504ab71973ea4d28 -r 0fca612a4bbdb14513a2b014c8459e7859f2fbc7 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.9', + version='1.4.10.dev1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 16 10:14:58 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 16 Jun 2012 08:14:58 -0000 Subject: [py-svn] commit/pytest: hpk42: mention pep302 in docstring Message-ID: <20120616081458.6745.33132@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/6821b39204a3/ changeset: 6821b39204a3 user: hpk42 date: 2012-06-16 10:14:52 summary: mention pep302 in docstring affected #: 1 file diff -r eabc28a991bb3675fb06c1a7318537338886fcc2 -r 6821b39204a30739807058e209efc566274b91be _pytest/assertion/rewrite.py --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -40,7 +40,7 @@ REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2) class AssertionRewritingHook(object): - """Import hook which rewrites asserts.""" + """PEP302 Import hook which rewrites asserts.""" def __init__(self): self.session = None 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. From commits-noreply at bitbucket.org Sat Jun 16 21:47:45 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 16 Jun 2012 19:47:45 -0000 Subject: [py-svn] commit/pytest: hpk42: change pluginmanager.register API to raise ValueError if the plugin object or the name is already registered Message-ID: <20120616194745.29156.86934@bitbucket12.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/19b833627c88/ changeset: 19b833627c88 user: hpk42 date: 2012-06-16 21:29:04 summary: change pluginmanager.register API to raise ValueError if the plugin object or the name is already registered affected #: 5 files diff -r 6821b39204a30739807058e209efc566274b91be -r 19b833627c882e7c0db2b566da7c31ea3c68b57c CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ ----------------------------------- - fix issue128: show captured output when capsys/capfd are used +- pluginmanager.register(...) now raises ValueError if the + plugin has been already registered or the name is taken Changes between 2.2.3 and 2.2.4 ----------------------------------- diff -r 6821b39204a30739807058e209efc566274b91be -r 19b833627c882e7c0db2b566da7c31ea3c68b57c _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.5.dev1' +__version__ = '2.2.5.dev2' diff -r 6821b39204a30739807058e209efc566274b91be -r 19b833627c882e7c0db2b566da7c31ea3c68b57c _pytest/core.py --- a/_pytest/core.py +++ b/_pytest/core.py @@ -79,10 +79,11 @@ self.import_plugin(spec) def register(self, plugin, name=None, prepend=False): - assert not self.isregistered(plugin), plugin + if self._name2plugin.get(name, None) == -1: + return name = name or getattr(plugin, '__name__', str(id(plugin))) - if name in self._name2plugin: - return False + if self.isregistered(plugin, name): + raise ValueError("Plugin already registered: %s=%s" %(name, plugin)) #self.trace("registering", name, plugin) self._name2plugin[name] = plugin self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self}) diff -r 6821b39204a30739807058e209efc566274b91be -r 19b833627c882e7c0db2b566da7c31ea3c68b57c 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.5.dev1', + version='2.2.5.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 6821b39204a30739807058e209efc566274b91be -r 19b833627c882e7c0db2b566da7c31ea3c68b57c testing/test_core.py --- a/testing/test_core.py +++ b/testing/test_core.py @@ -32,6 +32,11 @@ l2 = pluginmanager.getplugins() assert 42 not in l2 + def test_plugin_double_register(self): + pm = PluginManager() + pm.register(42, name="abc") + pytest.raises(ValueError, lambda: pm.register(42, name="abc")) + def test_plugin_skip(self, testdir, monkeypatch): p = testdir.makepyfile(skipping1=""" import pytest @@ -203,10 +208,10 @@ assert pp.isregistered(mod) l = pp.getplugins() assert mod in l - pytest.raises(AssertionError, "pp.register(mod)") + pytest.raises(ValueError, "pp.register(mod)") mod2 = py.std.types.ModuleType("pytest_hello") #pp.register(mod2) # double pm - pytest.raises(AssertionError, "pp.register(mod)") + pytest.raises(ValueError, "pp.register(mod)") #assert not pp.isregistered(mod2) assert pp.getplugins() == l 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. From commits-noreply at bitbucket.org Sun Jun 17 10:59:39 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 17 Jun 2012 08:59:39 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue129 - improve http://pytest.org/latest/faq.html Message-ID: <20120617085939.14800.77552@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/e2de61f25c36/ changeset: e2de61f25c36 user: hpk42 date: 2012-06-17 10:59:30 summary: fix issue129 - improve http://pytest.org/latest/faq.html especially with respect to the "magic" history, also mention pytest-django, trial and unittest integration. affected #: 6 files diff -r 19b833627c882e7c0db2b566da7c31ea3c68b57c -r e2de61f25c36040ec51922d5741a938d4ba9ee70 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,9 @@ - fix issue128: show captured output when capsys/capfd are used - pluginmanager.register(...) now raises ValueError if the plugin has been already registered or the name is taken +- fix issue129: improve http://pytest.org/latest/faq.html + especially with respect to the "magic" history, also mention + pytest-django, trial and unittest integration. Changes between 2.2.3 and 2.2.4 ----------------------------------- diff -r 19b833627c882e7c0db2b566da7c31ea3c68b57c -r e2de61f25c36040ec51922d5741a938d4ba9ee70 doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -2,6 +2,7 @@ The writing and reporting of assertions in tests ================================================== +.. _`assertfeedback`: .. _`assert with the assert statement`: Asserting with the ``assert`` statement diff -r 19b833627c882e7c0db2b566da7c31ea3c68b57c -r e2de61f25c36040ec51922d5741a938d4ba9ee70 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.2.4.2" +version = release = "2.2.4.3" import sys, os diff -r 19b833627c882e7c0db2b566da7c31ea3c68b57c -r e2de61f25c36040ec51922d5741a938d4ba9ee70 doc/en/customize.txt --- a/doc/en/customize.txt +++ b/doc/en/customize.txt @@ -23,8 +23,9 @@ tox.ini setup.cfg -Searching stops when the first ``[pytest]`` section is found. -There is no merging of configuration values from multiple files. Example:: +Searching stops when the first ``[pytest]`` section is found in any of +these files. There is no merging of configuration values from multiple +files. Example:: py.test path/to/testdir diff -r 19b833627c882e7c0db2b566da7c31ea3c68b57c -r e2de61f25c36040ec51922d5741a938d4ba9ee70 doc/en/faq.txt --- a/doc/en/faq.txt +++ b/doc/en/faq.txt @@ -9,6 +9,75 @@ On naming, nosetests, licensing and magic ------------------------------------------------ +How does py.test relate to nose and unittest? ++++++++++++++++++++++++++++++++++++++++++++++++++ + +py.test and nose_ share basic philosophy when it comes +to running and writing Python tests. In fact, you can run many tests +written for nose with py.test. nose_ was originally created +as a clone of ``py.test`` when py.test was in the ``0.8`` release +cycle. Note that starting with pytest-2.0 support for running unittest +test suites is majorly improved. + +how does py.test relate to twisted's trial? +++++++++++++++++++++++++++++++++++++++++++++++ + +Since some time py.test has builtin support for supporting tests +written using trial. It does not itself start a reactor, however, +and does not handle Deferreds returned from a test. Someone using +these features might eventually write a dedicated ``pytest-twisted`` +plugin which will surely see strong support from the pytest development +team. + +how does py.test work with Django? +++++++++++++++++++++++++++++++++++++++++++++++ + +In 2012, some work is going into the `pytest-django plugin `_. It substitutes the usage of Django's +``manage.py test`` and allows to use all pytest features_ most of which +are not available from Django directly. + +.. _features: test/features.html + + +What's this "magic" with py.test? (historic notes) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Around 2007 (version ``0.8``) some people thought that py.test +was using too much "magic". It had been part of the `pylib`_ which +contains a lot of unreleated python library code. Around 2010 there +was a major cleanup refactoring, which removed unused or deprecated code +and resulted in the new ``pytest`` PyPI package which strictly contains +only test-related code. This relese also brought a complete pluginification +such that the core is around 300 lines of code and everything else is +implemented in plugins. Thus ``pytest`` today is a small, universally runnable +and customizable testing framework for Python. Note, however, that +``pytest`` uses metaprogramming techniques and reading its source is +thus likely not something for Python beginners. + +A second "magic" issue was the assert statement debugging feature. +Nowadays, py.test explicitely rewrites assert statements in test modules +in order to provide more useful :ref:`assert feedback `. +This completely avoids previous issues of confusing assertion-reporting. +It also means, that you can use Python's ``-O`` optimization without loosing +assertions in test modules. + +py.test contains a second assert debugging technique, invoked via +``--assert=reinterpret``, activated by default on Python-2.5: When an +``assert`` statement that was missed by the rewriter fails, py.test +re-interprets the expression to show intermediate values if a test +fails. This second technique suffers from a caveat that the rewriting +does not: If your expression has side effects (better to avoid them +anyway!) the intermediate values may not be the same, confusing the +reinterpreter and obfuscating the initial error (this is also explained +at the command line if it happens). + +You can also turn off all assertion interaction using the +``--assertmode=off`` option. + +.. _`py namespaces`: index.html +.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py + + Why a ``py.test`` instead of a ``pytest`` command? ++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -20,48 +89,7 @@ package. These days the command line tool could be called ``pytest`` but since many people have gotten used to the old name and there is another tool named "pytest" we just decided to stick with -``py.test``. - -How does py.test relate to nose and unittest? -+++++++++++++++++++++++++++++++++++++++++++++++++ - -py.test and nose_ share basic philosophy when it comes -to running and writing Python tests. In fact, you can run many tests -written for nose with py.test. nose_ was originally created -as a clone of ``py.test`` when py.test was in the ``0.8`` release -cycle. Note that starting with pytest-2.0 support for running unittest -test suites is majorly improved and you should be able to run -many Django and Twisted test suites without modification. - -.. _features: test/features.html - - -What's this "magic" with py.test? -++++++++++++++++++++++++++++++++++++++++++ - -Around 2007 (version ``0.8``) some people claimed that py.test -was using too much "magic". Partly this has been fixed by removing -unused, deprecated or complicated code. It is today probably one -of the smallest, most universally runnable and most -customizable testing frameworks for Python. However, -``py.test`` still uses many metaprogramming techniques and -reading its source is thus likely not something for Python beginners. - -A second "magic" issue is arguably the assert statement debugging feature. When -loading test modules py.test rewrites the source code of assert statements. When -a rewritten assert statement fails, its error message has more information than -the original. py.test also has a second assert debugging technique. When an -``assert`` statement that was missed by the rewriter fails, py.test -re-interprets the expression to show intermediate values if a test fails. This -second technique suffers from a caveat that the rewriting does not: If your -expression has side effects (better to avoid them anyway!) the intermediate -values may not be the same, confusing the reinterpreter and obfuscating the -initial error (this is also explained at the command line if it happens). -You can turn off all assertion debugging with ``py.test --assertmode=off``. - -.. _`py namespaces`: index.html -.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py - +``py.test`` for now. Function arguments, parametrized tests and setup ------------------------------------------------------- diff -r 19b833627c882e7c0db2b566da7c31ea3c68b57c -r e2de61f25c36040ec51922d5741a938d4ba9ee70 doc/en/links.inc --- a/doc/en/links.inc +++ b/doc/en/links.inc @@ -18,3 +18,4 @@ .. _hudson: http://hudson-ci.org/ .. _jenkins: http://jenkins-ci.org/ .. _tox: http://codespeak.net/tox +.. _pylib: http://pylib.org 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. From commits-noreply at bitbucket.org Sun Jun 17 11:01:23 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 17 Jun 2012 09:01:23 -0000 Subject: [py-svn] commit/pytest: hpk42: (correction-commit for wrong previous changelog message) Message-ID: <20120617090123.14797.68338@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/bc9f8b3754ce/ changeset: bc9f8b3754ce user: hpk42 date: 2012-06-17 11:01:14 summary: (correction-commit for wrong previous changelog message) fix issue159 -- improve http://pytest.org/latest/faq.html especially with respect to the "magic" history, also mention pytest-django, trial and unittest integration. affected #: 1 file diff -r e2de61f25c36040ec51922d5741a938d4ba9ee70 -r bc9f8b3754ce06935187ad2eb69eceadb562779e CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ - fix issue128: show captured output when capsys/capfd are used - pluginmanager.register(...) now raises ValueError if the plugin has been already registered or the name is taken -- fix issue129: improve http://pytest.org/latest/faq.html +- fix issue159: improve http://pytest.org/latest/faq.html especially with respect to the "magic" history, also mention pytest-django, trial and unittest integration. 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. From commits-noreply at bitbucket.org Mon Jun 18 11:48:08 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 18 Jun 2012 09:48:08 -0000 Subject: [py-svn] commit/pytest: hpk42: fix faq once more to get rid of the strange "missed" bit. Message-ID: <20120618094808.5624.78475@bitbucket05.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/66d31afddbe6/ changeset: 66d31afddbe6 user: hpk42 date: 2012-06-18 11:47:57 summary: fix faq once more to get rid of the strange "missed" bit. affected #: 1 file diff -r bc9f8b3754ce06935187ad2eb69eceadb562779e -r 66d31afddbe676427298a4badbcb9696102411b1 doc/en/faq.txt --- a/doc/en/faq.txt +++ b/doc/en/faq.txt @@ -61,15 +61,14 @@ It also means, that you can use Python's ``-O`` optimization without loosing assertions in test modules. -py.test contains a second assert debugging technique, invoked via -``--assert=reinterpret``, activated by default on Python-2.5: When an -``assert`` statement that was missed by the rewriter fails, py.test -re-interprets the expression to show intermediate values if a test -fails. This second technique suffers from a caveat that the rewriting -does not: If your expression has side effects (better to avoid them -anyway!) the intermediate values may not be the same, confusing the -reinterpreter and obfuscating the initial error (this is also explained -at the command line if it happens). +py.test contains a second mostly obsolete assert debugging technique, +invoked via ``--assert=reinterpret``, activated by default on +Python-2.5: When an ``assert`` statement fails, py.test re-interprets +the expression part to show intermediate values. This technique suffers +from a caveat that the rewriting does not: If your expression has side +effects (better to avoid them anyway!) the intermediate values may not +be the same, confusing the reinterpreter and obfuscating the initial +error (this is also explained at the command line if it happens). You can also turn off all assertion interaction using the ``--assertmode=off`` option. 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. From commits-noreply at bitbucket.org Tue Jun 19 23:48:47 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 19 Jun 2012 21:48:47 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue160 a failing setup of an xfail-marked tests should Message-ID: <20120619214847.8201.44799@bitbucket12.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/247f79e22b50/ changeset: 247f79e22b50 user: hpk42 date: 2012-06-19 23:48:39 summary: fix issue160 a failing setup of an xfail-marked tests should be reported as xfail (not xpass) affected #: 4 files diff -r 66d31afddbe676427298a4badbcb9696102411b1 -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.2.4 and 2.2.5.dev ----------------------------------- +- fix issue160: a failing setup of an xfail-marked tests should + be reported as xfail (not xpass) - fix issue128: show captured output when capsys/capfd are used - pluginmanager.register(...) now raises ValueError if the plugin has been already registered or the name is taken diff -r 66d31afddbe676427298a4badbcb9696102411b1 -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -110,6 +110,7 @@ return expl + at pytest.mark.tryfirst def pytest_runtest_setup(item): if not isinstance(item, pytest.Function): return diff -r 66d31afddbe676427298a4badbcb9696102411b1 -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 testing/test_skipping.py --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -556,3 +556,19 @@ "*xfail(*conditions, reason=None, run=True)*expected failure*", ]) + +def test_xfail_test_setup_exception(testdir): + testdir.makeconftest(""" + def pytest_runtest_setup(): + 0 / 0 + """) + p = testdir.makepyfile(""" + import pytest + @pytest.mark.xfail + def test_func(): + assert 0 + """) + result = testdir.runpytest(p) + assert result.ret == 0 + assert 'xfailed' in result.stdout.str() + assert 'xpassed' not in result.stdout.str() diff -r 66d31afddbe676427298a4badbcb9696102411b1 -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 tox.ini --- a/tox.ini +++ b/tox.ini @@ -77,4 +77,4 @@ python_files=test_*.py *_test.py python_classes=Test Acceptance python_functions=test -pep8ignore = E401 +pep8ignore = E401 E225 E261 E128 E124 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. From commits-noreply at bitbucket.org Wed Jun 20 00:16:56 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 19 Jun 2012 22:16:56 -0000 Subject: [py-svn] commit/pytest: hpk42: add header info: always report 3rd party plugins in test runs Message-ID: <20120619221656.5956.16319@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/10adfe5e262e/ changeset: 10adfe5e262e user: hpk42 date: 2012-06-20 00:16:47 summary: add header info: always report 3rd party plugins in test runs affected #: 3 files diff -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Changes between 2.2.4 and 2.2.5.dev ----------------------------------- +- always report installed 3rd party plugins - fix issue160: a failing setup of an xfail-marked tests should be reported as xfail (not xpass) - fix issue128: show captured output when capsys/capfd are used diff -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -263,6 +263,17 @@ for line in flatten(lines): self.write_line(line) + def pytest_report_header(self, config): + plugininfo = config.pluginmanager._plugin_distinfo + if plugininfo: + l = [] + for dist, plugin in plugininfo: + name = dist.project_name + if name.startswith("pytest-"): + name = name[7:] + l.append(name) + return "plugins: %s" % ", ".join(l) + def pytest_collection_finish(self, session): if self.config.option.collectonly: self._printcollecteditems(session.items) diff -r 247f79e22b50601a1af3a18a137ecdc5c3278e64 -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -416,6 +416,10 @@ "*test_header_trailer_info.py .", "=* 1 passed in *.[0-9][0-9] seconds *=", ]) + if pytest.config.pluginmanager._plugin_distinfo: + result.stdout.fnmatch_lines([ + "plugins: *", + ]) def test_showlocals(self, testdir): p1 = 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. From commits-noreply at bitbucket.org Wed Jun 20 23:26:49 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 20 Jun 2012 21:26:49 -0000 Subject: [py-svn] commit/pytest-codecheckers: RonnyPfannschmidt: add tox envs, fixes Message-ID: <20120620212649.8696.92696@bitbucket13.managed.contegix.com> 1 new commit in pytest-codecheckers: https://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/changeset/d40bf6cb8f5b/ changeset: d40bf6cb8f5b user: RonnyPfannschmidt date: 2012-06-20 23:26:38 summary: add tox envs, fixes affected #: 2 files diff -r da2f2f4b7d93205727b2dda050a02e15a9834e0b -r d40bf6cb8f5b3b832755cdfa5b94622529696140 setup.py --- a/setup.py +++ b/setup.py @@ -4,7 +4,8 @@ Usage --------- -after installation (e.g. via ``pip install pytest-codecheckers``) you can type:: +after installation (e.g. via ``pip install pytest-codecheckers``) +you can type:: py.test [path/to/mypkg] @@ -14,10 +15,9 @@ .. _`pytest-dev`: http://codespeak.net/mailman/listinfo/py-dev """ from setuptools import setup -from hgdistver import get_version setup( name='pytest-codecheckers', - description='pytest plugin to add source code sanity checks (pep8 and friends)', + description='pytest plugin to add source code sanity checks', long_description=__doc__, get_version_from_scm=True, author='Ronny Pfannschmidt', @@ -25,20 +25,20 @@ url='http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/', packages=[ 'codecheckers', - ], + ], entry_points={ 'pytest11': [ 'codechecker = codecheckers.plugin', - ], + ], 'codechecker': [ 'pep8 = codecheckers.pep', 'pyflakes = codecheckers.flakes', - ], - }, + ], + }, install_requires=[ 'pytest>=2.0', 'pyflakes>=0.4', 'pep8', - ], + ], setup_requires=['hgdistver'], - ) +) diff -r da2f2f4b7d93205727b2dda050a02e15a9834e0b -r d40bf6cb8f5b3b832755cdfa5b94622529696140 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [pytest] codechecks = pep8 pyflakes [tox] +envlist= py26,py27,py32 indexserver = default = http://pypi.testrun.org pypi = http://pypi.python.org/simple Repository URL: https://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Jun 21 10:29:12 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 21 Jun 2012 08:29:12 -0000 Subject: [py-svn] commit/pytest-pep8: 3 new changesets Message-ID: <20120621082912.28377.72729@bitbucket12.managed.contegix.com> 3 new commits in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/changeset/724106e3aca0/ changeset: 724106e3aca0 user: hpk42 date: 2012-06-20 22:24:42 summary: bump to version 1.0, fix tox.ini to use pypi packages affected #: 4 files diff -r c0bd7d35d8adac8b98acfc1db50de59585e42d97 -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da pytest_pep8.py --- a/pytest_pep8.py +++ b/pytest_pep8.py @@ -3,7 +3,7 @@ import pytest import pep8 -__version__ = '0.9.2.dev2' +__version__ = '1.0' HISTKEY = "pep8/mtimes" diff -r c0bd7d35d8adac8b98acfc1db50de59585e42d97 -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da setup.py --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pytest-pep8', description='pytest plugin to check PEP8 requirements', long_description=open("README.txt").read(), - version='0.9.2.dev2', + version='1.0', author='Holger Krekel and Ronny Pfannschmidt', author_email='holger.krekel at gmail.com', url='http://bitbucket.org/hpk42/pytest-pep8/', diff -r c0bd7d35d8adac8b98acfc1db50de59585e42d97 -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da test_pep8.py --- a/test_pep8.py +++ b/test_pep8.py @@ -43,7 +43,7 @@ def test_w293w292(self, testdir, example): result = testdir.runpytest("--pep8", ) result.stdout.fnmatch_lines([ - "*plugins*pep8*", + #"*plugins*pep8*", "*W293*", "*W292*", ]) @@ -53,7 +53,7 @@ testdir.tmpdir.ensure("hello.py") result = testdir.runpytest("--pep8", ) result.stdout.fnmatch_lines([ - "*plugins*pep8*", + #"*plugins*pep8*", "*W293*", "*W292*", "*1 failed*1 passed*", diff -r c0bd7d35d8adac8b98acfc1db50de59585e42d97 -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da tox.ini --- a/tox.ini +++ b/tox.ini @@ -7,8 +7,8 @@ [testenv] deps= - :testrun:pytest-cache - :testrun:pytest + :pypi:pytest-cache + :pypi:pytest commands= py.test --junitxml={envlogdir}/junit-{envname}.xml {posargs} https://bitbucket.org/hpk42/pytest-pep8/changeset/dc4ae3a75b4c/ changeset: dc4ae3a75b4c user: hpk42 date: 2012-06-21 10:29:02 summary: add pytest-cache dep (doh) affected #: 4 files diff -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da -r dc4ae3a75b4c285c81c7044232b0426a23c9a221 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,9 @@ -0.9.2.dev +1.0.1 +---------------------------------------------- + +- add pytest-cache dep to setup.py + +1.0 ---------------------------------------------- - extend pep8ignore to allow lines of form diff -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da -r dc4ae3a75b4c285c81c7044232b0426a23c9a221 pytest_pep8.py --- a/pytest_pep8.py +++ b/pytest_pep8.py @@ -3,7 +3,7 @@ import pytest import pep8 -__version__ = '1.0' +__version__ = '1.0.1' HISTKEY = "pep8/mtimes" diff -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da -r dc4ae3a75b4c285c81c7044232b0426a23c9a221 setup.py --- a/setup.py +++ b/setup.py @@ -5,11 +5,11 @@ name='pytest-pep8', description='pytest plugin to check PEP8 requirements', long_description=open("README.txt").read(), - version='1.0', + version='1.0.1', author='Holger Krekel and Ronny Pfannschmidt', author_email='holger.krekel at gmail.com', url='http://bitbucket.org/hpk42/pytest-pep8/', py_modules=['pytest_pep8'], entry_points={'pytest11': ['pep8 = pytest_pep8']}, - install_requires=['pytest>=2.0', 'pep8>=1.3', ], + install_requires=['pytest-cache', 'pytest>=2.0', 'pep8>=1.3', ], ) diff -r 724106e3aca0eb7e11de4c6ef6acefa8b3be86da -r dc4ae3a75b4c285c81c7044232b0426a23c9a221 tox.ini --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,6 @@ [testenv] deps= - :pypi:pytest-cache :pypi:pytest commands= py.test --junitxml={envlogdir}/junit-{envname}.xml {posargs} https://bitbucket.org/hpk42/pytest-pep8/changeset/b8b3bbe4935a/ changeset: b8b3bbe4935a user: hpk42 date: 2012-06-21 10:29:04 summary: Added tag 1.0.1 for changeset dc4ae3a75b4c affected #: 1 file diff -r dc4ae3a75b4c285c81c7044232b0426a23c9a221 -r b8b3bbe4935a7e79af32de98c5749f9b462a319e .hgtags --- a/.hgtags +++ b/.hgtags @@ -4,3 +4,4 @@ 14359fa2c9eb31e5cdf7414603a14b943327bc07 0.7 04e2de41d527876325038b22a1721b9290b0db8d 0.7 1160e1328095d879238cdda32173476ecc5a5072 0.9.1 +dc4ae3a75b4c285c81c7044232b0426a23c9a221 1.0.1 Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Jun 21 11:07:39 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 21 Jun 2012 09:07:39 -0000 Subject: [py-svn] commit/pytest: hpk42: some refinements to reporting and hook order Message-ID: <20120621090739.3924.18466@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/6b8f9ccbaa28/ changeset: 6b8f9ccbaa28 user: hpk42 date: 2012-06-21 11:07:22 summary: some refinements to reporting and hook order affected #: 8 files diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,13 @@ - fix issue159: improve http://pytest.org/latest/faq.html especially with respect to the "magic" history, also mention pytest-django, trial and unittest integration. +- reporting refinements: + - pytest_report_header now receives a "startdir" so that + you can use startdir.bestrelpath(yourpath) to show + nice relative path + - allow plugins to implement both pytest_report_header and + pytest_sessionstart (sessionstart is invoked first). + - don't show deselected reason line if there is none Changes between 2.2.3 and 2.2.4 ----------------------------------- diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.5.dev2' +__version__ = '2.2.5.dev3' diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 _pytest/hookspec.py --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -193,7 +193,7 @@ # hooks for influencing reporting (invoked from _pytest_terminal) # ------------------------------------------------------------------------- -def pytest_report_header(config): +def pytest_report_header(config, startdir): """ return a string to be displayed as header info for terminal reporting.""" def pytest_report_teststatus(report): diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -29,7 +29,6 @@ group._addoption('--maxfail', metavar="num", action="store", type="int", dest="maxfail", default=0, help="exit after first num failures or errors.") - group._addoption('--strict', action="store_true", help="run pytest in strict mode, warnings become errors.") diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -2,7 +2,8 @@ This is a good source for looking at the various reporting hooks. """ -import pytest, py +import pytest +import py import sys import os @@ -94,7 +95,7 @@ self._numcollected = 0 self.stats = {} - self.curdir = py.path.local() + self.startdir = self.curdir = py.path.local() if file is None: file = py.std.sys.stdout self._tw = py.io.TerminalWriter(file) @@ -109,9 +110,9 @@ def write_fspath_result(self, fspath, res): if fspath != self.currentfspath: self.currentfspath = fspath - #fspath = self.curdir.bestrelpath(fspath) + #fspath = self.startdir.bestrelpath(fspath) self._tw.line() - #relpath = self.curdir.bestrelpath(fspath) + #relpath = self.startdir.bestrelpath(fspath) self._tw.write(fspath + " ") self._tw.write(res) @@ -243,6 +244,7 @@ def pytest_collection_modifyitems(self): self.report_collect(True) + @pytest.mark.trylast def pytest_sessionstart(self, session): self._sessionstarttime = py.std.time.time() if not self.showheader: @@ -258,7 +260,8 @@ getattr(self.config.option, 'pastebin', None): msg += " -- " + str(sys.executable) self.write_line(msg) - lines = self.config.hook.pytest_report_header(config=self.config) + lines = self.config.hook.pytest_report_header( + config=self.config, startdir=self.startdir) lines.reverse() for line in flatten(lines): self.write_line(line) @@ -463,8 +466,9 @@ m = self.config.option.markexpr if m: l.append("-m %r" % m) - self.write_sep("=", "%d tests deselected by %r" %( - len(self.stats['deselected']), " ".join(l)), bold=True) + if l: + self.write_sep("=", "%d tests deselected by %r" %( + len(self.stats['deselected']), " ".join(l)), bold=True) def repr_pythonversion(v=None): if v is None: diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 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.5.dev2', + version='2.2.5.dev3', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -632,17 +632,20 @@ def test_pytest_report_header(self, testdir, option): testdir.makeconftest(""" + def pytest_sessionstart(session): + session.config._somevalue = 42 def pytest_report_header(config): - return "hello: info" + return "hello: %s" % config._somevalue """) testdir.mkdir("a").join("conftest.py").write(""" -def pytest_report_header(config): - return ["line1", "line2"]""") +def pytest_report_header(config, startdir): + return ["line1", str(startdir)] +""") result = testdir.runpytest("a") result.stdout.fnmatch_lines([ "line1", - "line2", - "*hello: info*", + str(testdir.tmpdir), + "*hello: 42*", ]) @pytest.mark.xfail("not hasattr(os, 'dup')") diff -r 10adfe5e262e33cc45dbf356daf1dfb5050e73d8 -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 tox.ini --- a/tox.ini +++ b/tox.ini @@ -72,7 +72,7 @@ minversion=2.0 plugins=pytester #--pyargs --doctest-modules --ignore=.tox -addopts= -rxs +addopts= -rxs rsyncdirs=tox.ini pytest.py _pytest testing python_files=test_*.py *_test.py python_classes=Test Acceptance 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. From commits-noreply at bitbucket.org Thu Jun 21 11:20:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 21 Jun 2012 09:20:36 -0000 Subject: [py-svn] commit/pytest: hpk42: fix some pep8 issues, more to go ... is there a tool that helps with pep8-ifying? Message-ID: <20120621092036.25816.29778@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/07e63ecf9c17/ changeset: 07e63ecf9c17 user: hpk42 date: 2012-06-21 11:20:29 summary: fix some pep8 issues, more to go ... is there a tool that helps with pep8-ifying? affected #: 6 files diff -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 _pytest/assertion/oldinterpret.py --- a/_pytest/assertion/oldinterpret.py +++ b/_pytest/assertion/oldinterpret.py @@ -526,10 +526,13 @@ # example: def f(): return 5 + def g(): return 3 + def h(x): return 'never' + check("f() * g() == 5") check("not f()") check("not (f() and g() or 0)") diff -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 _pytest/assertion/reinterpret.py --- a/_pytest/assertion/reinterpret.py +++ b/_pytest/assertion/reinterpret.py @@ -44,4 +44,3 @@ from _pytest.assertion.newinterpret import interpret as reinterpret else: reinterpret = reinterpret_old - diff -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 _pytest/assertion/rewrite.py --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -95,7 +95,8 @@ finally: self.session = sess else: - state.trace("matched test file (was specified on cmdline): %r" % (fn,)) + state.trace("matched test file (was specified on cmdline): %r" % + (fn,)) # The requested module looks like a test file, so rewrite it. This is # the most magical part of the process: load the source, rewrite the # asserts, and load the rewritten source. We also cache the rewritten @@ -121,14 +122,14 @@ # because we're in a zip file. write = False elif e == errno.EACCES: - state.trace("read only directory: %r" % (fn_pypath.dirname,)) + state.trace("read only directory: %r" % fn_pypath.dirname) write = False else: raise cache_name = fn_pypath.basename[:-3] + PYC_TAIL pyc = os.path.join(cache_dir, cache_name) - # Notice that even if we're in a read-only directory, I'm going to check - # for a cached pyc. This may not be optimal... + # Notice that even if we're in a read-only directory, I'm going + # to check for a cached pyc. This may not be optimal... co = _read_pyc(fn_pypath, pyc) if co is None: state.trace("rewriting %r" % (fn,)) @@ -160,10 +161,11 @@ return sys.modules[name] def _write_pyc(co, source_path, pyc): - # Technically, we don't have to have the same pyc format as (C)Python, since - # these "pycs" should never be seen by builtin import. However, there's - # little reason deviate, and I hope sometime to be able to use - # imp.load_compiled to load them. (See the comment in load_module above.) + # Technically, we don't have to have the same pyc format as + # (C)Python, since these "pycs" should never be seen by builtin + # import. However, there's little reason deviate, and I hope + # sometime to be able to use imp.load_compiled to load them. (See + # the comment in load_module above.) mtime = int(source_path.mtime()) try: fp = open(pyc, "wb") @@ -240,9 +242,8 @@ except EnvironmentError: return None # Check for invalid or out of date pyc file. - if (len(data) != 8 or - data[:4] != imp.get_magic() or - struct.unpack(">", - ast.Add : "+", - ast.Sub : "-", - ast.Mult : "*", - ast.Div : "/", - ast.FloorDiv : "//", - ast.Mod : "%%", # escaped for string formatting - ast.Eq : "==", - ast.NotEq : "!=", - ast.Lt : "<", - ast.LtE : "<=", - ast.Gt : ">", - ast.GtE : ">=", - ast.Pow : "**", - ast.Is : "is", - ast.IsNot : "is not", - ast.In : "in", - ast.NotIn : "not in" + ast.BitOr: "|", + ast.BitXor: "^", + ast.BitAnd: "&", + ast.LShift: "<<", + ast.RShift: ">>", + ast.Add: "+", + ast.Sub: "-", + ast.Mult: "*", + ast.Div: "/", + ast.FloorDiv: "//", + ast.Mod: "%%", # escaped for string formatting + ast.Eq: "==", + ast.NotEq: "!=", + ast.Lt: "<", + ast.LtE: "<=", + ast.Gt: ">", + ast.GtE: ">=", + ast.Pow: "**", + ast.Is: "is", + ast.IsNot: "is not", + ast.In: "in", + ast.NotIn: "not in" } @@ -341,7 +342,7 @@ lineno = 0 for item in mod.body: if (expect_docstring and isinstance(item, ast.Expr) and - isinstance(item.value, ast.Str)): + isinstance(item.value, ast.Str)): doc = item.value.s if "PYTEST_DONT_REWRITE" in doc: # The module has disabled assertion rewriting. @@ -462,7 +463,8 @@ body.append(raise_) # Clear temporary variables by setting them to None. if self.variables: - variables = [ast.Name(name, ast.Store()) for name in self.variables] + variables = [ast.Name(name, ast.Store()) + for name in self.variables] clear = ast.Assign(variables, ast.Name("None", ast.Load())) self.statements.append(clear) # Fix line numbers. @@ -548,7 +550,8 @@ new_kwarg, expl = self.visit(call.kwargs) arg_expls.append("**" + expl) expl = "%s(%s)" % (func_expl, ', '.join(arg_expls)) - new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg) + new_call = ast.Call(new_func, new_args, new_kwargs, + new_star, new_kwarg) res = self.assign(new_call) res_expl = self.explanation_param(self.display(res)) outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl) diff -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 _pytest/assertion/util.py --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -116,9 +116,7 @@ excinfo = py.code.ExceptionInfo() explanation = ['(pytest_assertion plugin: representation of ' 'details failed. Probably an object has a faulty __repr__.)', - str(excinfo) - ] - + str(excinfo)] if not explanation: return None diff -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -133,8 +133,10 @@ def __init__(self, fspath, config): self.fspath = fspath self.config = config + def __getattr__(self, name): hookmethod = getattr(self.config.hook, name) + def call_matching_hooks(**kwargs): plugins = self.config._getmatchingplugins(self.fspath) return hookmethod.pcall(plugins, **kwargs) @@ -143,8 +145,9 @@ def compatproperty(name): def fget(self): return getattr(pytest, name) + return property(fget, None, None, - "deprecated attribute %r, use pytest.%s" % (name,name)) + "deprecated attribute %r, use pytest.%s" % (name, name)) class Node(object): """ base class for all Nodes in the collection tree. @@ -184,7 +187,8 @@ return cls def __repr__(self): - return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None)) + return "<%s %r>" %(self.__class__.__name__, + getattr(self, 'name', None)) # methods for ordering nodes @property @@ -201,8 +205,8 @@ def __eq__(self, other): if not isinstance(other, Node): return False - return self.__class__ == other.__class__ and \ - self.name == other.name and self.parent == other.parent + return (self.__class__ == other.__class__ and + self.name == other.name and self.parent == other.parent) def __ne__(self, other): return not self == other @@ -371,7 +375,8 @@ def __init__(self, config): super(Session, self).__init__(py.path.local(), parent=None, config=config, session=self) - assert self.config.pluginmanager.register(self, name="session", prepend=True) + assert self.config.pluginmanager.register( + self, name="session", prepend=True) self._testsfailed = 0 self.shouldstop = False self.trace = config.trace.root.get("collection") @@ -457,7 +462,7 @@ if path.check(dir=1): assert not names, "invalid arg %r" %(arg,) for path in path.visit(fil=lambda x: x.check(file=1), - rec=self._recurse, bf=True, sort=True): + rec=self._recurse, bf=True, sort=True): for x in self._collectfile(path): yield x else: @@ -469,13 +474,13 @@ ihook = self.gethookproxy(path) if not self.isinitpath(path): if ihook.pytest_ignore_collect(path=path, config=self.config): - return () + return () return ihook.pytest_collect_file(path=path, parent=self) def _recurse(self, path): ihook = self.gethookproxy(path.dirpath()) if ihook.pytest_ignore_collect(path=path, config=self.config): - return + return for pat in self._norecursepatterns: if path.check(fnmatch=pat): return False diff -r 6b8f9ccbaa284ea7d466d9b2fab4a5bf81872876 -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 tox.ini --- a/tox.ini +++ b/tox.ini @@ -77,4 +77,4 @@ python_files=test_*.py *_test.py python_classes=Test Acceptance python_functions=test -pep8ignore = E401 E225 E261 E128 E124 +pep8ignore = E401 E225 E261 E128 E124 E302 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. From commits-noreply at bitbucket.org Thu Jun 21 11:30:19 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 21 Jun 2012 09:30:19 -0000 Subject: [py-svn] commit/pytest: hpk42: fix internal test setup failure Message-ID: <20120621093019.29095.44644@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/bc2690555863/ changeset: bc2690555863 user: hpk42 date: 2012-06-21 11:30:10 summary: fix internal test setup failure affected #: 1 file diff -r 07e63ecf9c174c05d81344f67e22f047a7a6e541 -r bc2690555863e75d66ca3fcaf5ca78eb6cd15887 testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -46,6 +46,7 @@ config._numfiles = len(lines2) raise AssertionError("\n".join(error)) + at pytest.mark.tryfirst # XXX rather do item.addfinalizer def pytest_runtest_setup(item): item._oldir = py.path.local() 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. From commits-noreply at bitbucket.org Thu Jun 21 12:57:57 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 21 Jun 2012 10:57:57 -0000 Subject: [py-svn] commit/pytest-codecheckers: 2 new changesets Message-ID: <20120621105757.23712.85596@bitbucket15.managed.contegix.com> 2 new commits in pytest-codecheckers: https://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/changeset/4a027892c04e/ changeset: 4a027892c04e user: RonnyPfannschmidt date: 2012-06-21 12:42:42 summary: complete cleanup affected #: 4 files diff -r d40bf6cb8f5b3b832755cdfa5b94622529696140 -r 4a027892c04e4dc49954f81ccc13d5fb9e563100 codecheckers/flakes.py --- a/codecheckers/flakes.py +++ b/codecheckers/flakes.py @@ -2,6 +2,7 @@ from pyflakes.checker import Binding, Assignment + def assignment_monkeypatched_init(self, name, source): Binding.__init__(self, name, source) if name == '__tracebackhide__': @@ -9,6 +10,6 @@ Assignment.__init__ = assignment_monkeypatched_init -def check_file(path, filename, io): - return pyflakes_check(path.read(), filename, io) +def check_file(path, filename): + return pyflakes_check(path.read(), filename) diff -r d40bf6cb8f5b3b832755cdfa5b94622529696140 -r 4a027892c04e4dc49954f81ccc13d5fb9e563100 codecheckers/pep.py --- a/codecheckers/pep.py +++ b/codecheckers/pep.py @@ -1,29 +1,8 @@ -import sys import pep8 -class PyTestChecker(pep8.Checker): - ignored_errors = 0 - def report_error(self, line_number, offset, text, check): - #XXX: pep8 is a retarded module! - if pep8.ignore_code(text[:4]): - self.ignored_errors += 1 - pep8.Checker.report_error(self, line_number, offset, text, check) -def check_file(path, filename, io): - oldio = sys.stdout, sys.stderr - try: - sys.stdout = sys.stderr = io - pep8.process_options(['pep8', - # ignore list taken from moin - '--ignore=E202,E221,E222,E241,E301,E302,E401,E501,E701,W391,W601,W602', - '--show-source', - '--repeat', - 'dummy file', - ]) - checker = PyTestChecker(filename, path.readlines()) - #XXX: bails out on death - error_count = checker.check_all() - ignored = checker.ignored_errors - return max(error_count - ignored, 0) - finally: - sys.stdout , sys.stderr = oldio +def check_file(path, filename): + checker = pep8.Checker(filename, path.readlines(), + show_source=True, + repeat=True) + return checker.check_all() diff -r d40bf6cb8f5b3b832755cdfa5b94622529696140 -r 4a027892c04e4dc49954f81ccc13d5fb9e563100 codecheckers/plugin.py --- a/codecheckers/plugin.py +++ b/codecheckers/plugin.py @@ -3,6 +3,13 @@ import pkg_resources +class FoundErrors(Exception): + def __init__(self, count, out, err): + self.count = count + self.out = out + self.err = err + + class PyCodeCheckItem(py.test.collect.Item): def __init__(self, ep, parent): py.test.collect.Item.__init__(self, ep.name, parent) @@ -11,29 +18,20 @@ self._ep = ep def runtest(self): - mod = self._ep.load() - io = py.io.BytesIO() + check = self._ep.load().check_file + call = py.io.StdCapture.call - try: - found_errors = mod.check_file(self.fspath, self.filename, io) - self.out = io.getvalue() - except: - found_errors = True - self.info = py.code.ExceptionInfo() - assert not found_errors + found_errors, out, err = call(check, self.fspath, self.filename) + if found_errors: + raise FoundErrors(FoundErrors, out, err) - def repr_failure(self, exc_info): - try: - return self.out - except AttributeError: - #XXX: internal error ?! - self.info = py.code.ExceptionInfo() - info = getattr(self, 'info', exc_info) - return super(PyCodeCheckItem, self).repr_failure(info) + def repr_failure(self, excinfo): + if excinfo.errisinstance(FoundErrors): + return excinfo.value.out + return super(PyCodeCheckItem, self).repr_failure(excinfo) def reportinfo(self): - return (self.fspath, -1, "codecheck %s %s" % ( - self._ep.name, self.filename)) + return (self.fspath, -1, "codecheck " + self._ep.name) class PyCheckerCollector(py.test.collect.File): @@ -46,9 +44,8 @@ return [] checkers = self.config.getini('codechecks') entrypoints = pkg_resources.iter_entry_points('codechecker') - - items = [PyCodeCheckItem(ep, self) for ep in entrypoints if ep.name in checkers] - return items + wanted = (ep for ep in entrypoints if ep.name in checkers) + return [PyCodeCheckItem(wep, self) for wep in wanted] def pytest_collect_file(path, parent): @@ -57,5 +54,7 @@ def pytest_addoption(parser): - parser.addini('codechecks', type='args', help='listings of the codechecks to use') + parser.addini('codechecks', type='args', + help='listings of the codechecks to use', + default=['pep8', 'pyflakes']) parser.addoption('--no-codechecks', action='store_true') diff -r d40bf6cb8f5b3b832755cdfa5b94622529696140 -r 4a027892c04e4dc49954f81ccc13d5fb9e563100 tests/test_pyflakes.py --- a/tests/test_pyflakes.py +++ b/tests/test_pyflakes.py @@ -16,7 +16,7 @@ out.stdout.fnmatch_lines([ '*abc*', '*1 failed*', - ]) + ]) def test_reportinfo_verbose(testdir): @@ -27,4 +27,4 @@ out = testdir.runpytest('-v') out.stdout.fnmatch_lines([ '*test_reportinfo_verbose.py:0: codecheck pyflakes PASSED', - ]) + ]) https://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/changeset/069dd9d1aead/ changeset: 069dd9d1aead user: RonnyPfannschmidt date: 2012-06-21 12:43:16 summary: add .cache to hgignore affected #: 1 file diff -r 4a027892c04e4dc49954f81ccc13d5fb9e563100 -r 069dd9d1aead5de036a5c315be204e774272bbb4 .hgignore --- a/.hgignore +++ b/.hgignore @@ -2,4 +2,5 @@ build dist .*\.pyc +.cache \.tox Repository URL: https://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Jun 22 10:53:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 22 Jun 2012 08:53:36 -0000 Subject: [py-svn] commit/pytest: hpk42: ignore .cache Message-ID: <20120622085336.24372.58000@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/2bfb9da30dfd/ changeset: 2bfb9da30dfd user: hpk42 date: 2012-06-22 10:53:28 summary: ignore .cache affected #: 1 file diff -r bc2690555863e75d66ca3fcaf5ca78eb6cd15887 -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd .hgignore --- a/.hgignore +++ b/.hgignore @@ -23,3 +23,4 @@ env/ 3rdparty/ .tox +.cache 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. From commits-noreply at bitbucket.org Sat Jun 23 11:33:03 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 23 Jun 2012 09:33:03 -0000 Subject: [py-svn] commit/pytest: hpk42: fix skip/xfail confusion, reported and discussed on Message-ID: <20120623093303.13800.89987@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/86ea199221ab/ changeset: 86ea199221ab user: hpk42 date: 2012-06-23 11:32:32 summary: fix skip/xfail confusion, reported and discussed on http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get affected #: 10 files diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,21 +1,33 @@ Changes between 2.2.4 and 2.2.5.dev ----------------------------------- +- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip + will now take precedence before xfail-markers because we + can't determine xfail/xpass status in case of a skip. see also: + http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get + - always report installed 3rd party plugins + - fix issue160: a failing setup of an xfail-marked tests should be reported as xfail (not xpass) + - fix issue128: show captured output when capsys/capfd are used + - pluginmanager.register(...) now raises ValueError if the plugin has been already registered or the name is taken + - fix issue159: improve http://pytest.org/latest/faq.html especially with respect to the "magic" history, also mention pytest-django, trial and unittest integration. + - reporting refinements: - pytest_report_header now receives a "startdir" so that you can use startdir.bestrelpath(yourpath) to show nice relative path + - allow plugins to implement both pytest_report_header and pytest_sessionstart (sessionstart is invoked first). + - don't show deselected reason line if there is none Changes between 2.2.3 and 2.2.4 diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 ISSUES.txt --- a/ISSUES.txt +++ b/ISSUES.txt @@ -355,3 +355,31 @@ result = pytester.runpytest(p) assert result.ret == 0 assert result.passed == 1 + +Another idea is to allow to define a full scenario including the run +in one content string:: + + runscenario(""" + test_{TESTNAME}.py: + import pytest + @pytest.mark.xfail + def test_that_fails(): + assert 0 + + @pytest.mark.skipif("True") + def test_hello(): + pass + + conftest.py: + import pytest + def pytest_runsetup_setup(item): + pytest.skip("abc") + + runpytest -rsxX + *SKIP*{TESTNAME}* + *1 skipped* + """) + +This could be run with at least three different ways to invoke pytest: +through the shell, through "python -m pytest" and inlined. As inlined +would be the fastest it could be run first (or "--fast" mode). diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.5.dev3' +__version__ = '2.2.5.dev4' diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 _pytest/junitxml.py --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -114,7 +114,7 @@ def append_failure(self, report): #msg = str(report.longrepr.reprtraceback.extraline) - if "xfail" in report.keywords: + if hasattr(report, "wasxfail"): self.append( Junit.skipped(message="xfail-marked test passes unexpectedly")) self.skipped += 1 @@ -148,8 +148,8 @@ self.errors += 1 def append_skipped(self, report): - if "xfail" in report.keywords: - self.append(Junit.skipped(str(report.keywords['xfail']), + if hasattr(report, "wasxfail"): + self.append(Junit.skipped(str(report.wasxfail), message="expected test failure")) else: filename, lineno, skipreason = report.longrepr diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -387,7 +387,7 @@ raise self.Interrupted(self.shouldstop) def pytest_runtest_logreport(self, report): - if report.failed and 'xfail' not in getattr(report, 'keywords', []): + if report.failed and not hasattr(report, 'wasxfail'): self._testsfailed += 1 maxfail = self.config.getvalue("maxfail") if maxfail and self._testsfailed >= maxfail: diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 _pytest/pdb.py --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -50,7 +50,7 @@ def pytest_runtest_makereport(): pytestPDB.item = None - + class PdbInvoke: @pytest.mark.tryfirst def pytest_runtest_makereport(self, item, call, __multicall__): @@ -59,7 +59,7 @@ call.excinfo.errisinstance(pytest.skip.Exception) or \ call.excinfo.errisinstance(py.std.bdb.BdbQuit): return rep - if "xfail" in rep.keywords: + if hasattr(rep, "wasxfail"): return rep # we assume that the above execute() suspended capturing # XXX we re-use the TerminalReporter's terminalwriter diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -138,7 +138,7 @@ rep = __multicall__.execute() if rep.when == "call": # we need to translate into how py.test encodes xpass - rep.keywords['xfail'] = "reason: " + repr(item._unexpectedsuccess) + rep.wasxfail = "reason: " + repr(item._unexpectedsuccess) rep.outcome = "failed" return rep if not (call.excinfo and @@ -149,27 +149,27 @@ if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception): if not item.config.getvalue("runxfail"): rep = __multicall__.execute() - rep.keywords['xfail'] = "reason: " + call.excinfo.value.msg + rep.wasxfail = "reason: " + call.excinfo.value.msg rep.outcome = "skipped" return rep rep = __multicall__.execute() evalxfail = item._evalxfail - if not item.config.option.runxfail: - if evalxfail.wasvalid() and evalxfail.istrue(): - if call.excinfo: - rep.outcome = "skipped" - rep.keywords['xfail'] = evalxfail.getexplanation() - elif call.when == "call": - rep.outcome = "failed" - rep.keywords['xfail'] = evalxfail.getexplanation() - return rep - if 'xfail' in rep.keywords: - del rep.keywords['xfail'] + if not rep.skipped: + if not item.config.option.runxfail: + if evalxfail.wasvalid() and evalxfail.istrue(): + if call.excinfo: + rep.outcome = "skipped" + elif call.when == "call": + rep.outcome = "failed" + else: + return rep + rep.wasxfail = evalxfail.getexplanation() + return rep return rep # called by terminalreporter progress reporting def pytest_report_teststatus(report): - if 'xfail' in report.keywords: + if hasattr(report, "wasxfail"): if report.skipped: return "xfailed", "x", "xfail" elif report.failed: @@ -216,7 +216,7 @@ if xfailed: for rep in xfailed: pos = rep.nodeid - reason = rep.keywords['xfail'] + reason = rep.wasxfail lines.append("XFAIL %s" % (pos,)) if reason: lines.append(" " + str(reason)) @@ -226,7 +226,7 @@ if xpassed: for rep in xpassed: pos = rep.nodeid - reason = rep.keywords['xfail'] + reason = rep.wasxfail lines.append("XPASS %s %s" %(pos, reason)) def cached_eval(config, expr, d): diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 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.5.dev3', + version='2.2.5.dev4', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -138,7 +138,7 @@ sys.stderr.write("hello-stderr\\n") raise ValueError(42) """) - + result, dom = runandparse(testdir) assert result.ret node = dom.getElementsByTagName("testsuite")[0] @@ -366,7 +366,7 @@ 27, # issue #126 0xD800, 0xDFFF, 0xFFFE, 0x0FFFF) #, 0x110000) valid = (0x9, 0xA, 0x20,) # 0xD, 0xD7FF, 0xE000, 0xFFFD, 0x10000, 0x10FFFF) - + from _pytest.junitxml import bin_xml_escape diff -r 2bfb9da30dfdd53c7f4aeaf5bfe0d82280d0ccdd -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 testing/test_skipping.py --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -113,8 +113,7 @@ assert len(reports) == 3 callreport = reports[1] assert callreport.skipped - expl = callreport.keywords['xfail'] - assert expl == "" + assert callreport.wasxfail == "" def test_xfail_xpassed(self, testdir): item = testdir.getitem(""" @@ -127,8 +126,7 @@ assert len(reports) == 3 callreport = reports[1] assert callreport.failed - expl = callreport.keywords['xfail'] - assert expl == "" + assert callreport.wasxfail == "" def test_xfail_run_anyway(self, testdir): testdir.makepyfile(""" @@ -155,7 +153,8 @@ reports = runtestprotocol(item, log=False) callreport = reports[1] assert callreport.failed - assert 'xfail' not in callreport.keywords + assert not hasattr(callreport, "wasxfail") + assert 'xfail' in callreport.keywords def test_xfail_not_report_default(self, testdir): p = testdir.makepyfile(test_one=""" @@ -572,3 +571,28 @@ assert result.ret == 0 assert 'xfailed' in result.stdout.str() assert 'xpassed' not in result.stdout.str() + +def test_imperativeskip_on_xfail_test(testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.xfail + def test_that_fails(): + assert 0 + + @pytest.mark.skipif("True") + def test_hello(): + pass + """) + testdir.makeconftest(""" + import pytest + def pytest_runtest_setup(item): + pytest.skip("abc") + """) + result = testdir.runpytest("-rsxX") + result.stdout.fnmatch_lines_random(""" + *SKIP*abc* + *SKIP*condition: True* + *2 skipped* + """) + + 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. From commits-noreply at bitbucket.org Sun Jun 24 15:24:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 24 Jun 2012 13:24:36 -0000 Subject: [py-svn] commit/pytest-pep8: 2 new changesets Message-ID: <20120624132436.19887.31471@bitbucket02.managed.contegix.com> 2 new commits in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/changeset/2e5abd094f9f/ changeset: 2e5abd094f9f user: hpk42 date: 2012-06-24 15:24:25 summary: allow comments in ignore-lines, recognize ALL always affected #: 4 files diff -r b8b3bbe4935a7e79af32de98c5749f9b462a319e -r 2e5abd094f9f1abe88d764f3070dbfdb0823213f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +1.0.2 +---------------------------------------------- + +- fix a parsing bug - # is now recognized as + as comment and ALL will now be recognized + even if other codes are specified (nonsensically) + 1.0.1 ---------------------------------------------- diff -r b8b3bbe4935a7e79af32de98c5749f9b462a319e -r 2e5abd094f9f1abe88d764f3070dbfdb0823213f pytest_pep8.py --- a/pytest_pep8.py +++ b/pytest_pep8.py @@ -3,7 +3,7 @@ import pytest import pep8 -__version__ = '1.0.1' +__version__ = '1.0.2' HISTKEY = "pep8/mtimes" @@ -84,16 +84,18 @@ def __init__(self, ignorelines, coderex=re.compile("[EW]\d\d\d")): self.ignores = ignores = [] for line in ignorelines: + i = line.find("#") + if i != -1: + line = line[:i] try: glob, ign = line.split(None, 1) except ValueError: glob, ign = None, line if glob and coderex.match(glob): glob, ign = None, line - if ign == "ALL": + ign = ign.split() + if "ALL" in ign: ign = None - else: - ign = ign.split() ignores.append((glob, ign)) def __call__(self, path): diff -r b8b3bbe4935a7e79af32de98c5749f9b462a319e -r 2e5abd094f9f1abe88d764f3070dbfdb0823213f setup.py --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pytest-pep8', description='pytest plugin to check PEP8 requirements', long_description=open("README.txt").read(), - version='1.0.1', + version='1.0.2', author='Holger Krekel and Ronny Pfannschmidt', author_email='holger.krekel at gmail.com', url='http://bitbucket.org/hpk42/pytest-pep8/', diff -r b8b3bbe4935a7e79af32de98c5749f9b462a319e -r 2e5abd094f9f1abe88d764f3070dbfdb0823213f test_pep8.py --- a/test_pep8.py +++ b/test_pep8.py @@ -27,8 +27,8 @@ testdir.makeini(""" [pytest] pep8ignore = E203 - tests/*.py ALL *.py E300 + tests/*.py ALL E203 # something """) testdir.tmpdir.ensure("xy.py") testdir.tmpdir.ensure("tests/hello.py") https://bitbucket.org/hpk42/pytest-pep8/changeset/6ea1b7ce7fde/ changeset: 6ea1b7ce7fde user: hpk42 date: 2012-06-24 15:24:30 summary: Added tag 1.0.2 for changeset 2e5abd094f9f affected #: 1 file diff -r 2e5abd094f9f1abe88d764f3070dbfdb0823213f -r 6ea1b7ce7fde9a4a89df643844f4db04a6ba22a0 .hgtags --- a/.hgtags +++ b/.hgtags @@ -5,3 +5,4 @@ 04e2de41d527876325038b22a1721b9290b0db8d 0.7 1160e1328095d879238cdda32173476ecc5a5072 0.9.1 dc4ae3a75b4c285c81c7044232b0426a23c9a221 1.0.1 +2e5abd094f9f1abe88d764f3070dbfdb0823213f 1.0.2 Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Jun 24 16:43:06 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 24 Jun 2012 14:43:06 -0000 Subject: [py-svn] commit/pytest: hpk42: fix problem with unicode in writing failure representations to terminal, thanks ThomasWaldmann Message-ID: <20120624144306.19887.17978@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/5bcd225c3b5e/ changeset: 5bcd225c3b5e user: hpk42 date: 2012-06-24 16:42:31 summary: fix problem with unicode in writing failure representations to terminal, thanks ThomasWaldmann affected #: 4 files diff -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 -r 5bcd225c3b5eb502209b537546ad0f83e40de032 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,12 +1,14 @@ Changes between 2.2.4 and 2.2.5.dev ----------------------------------- +- catch unicode-issues when writing failure representations + to terminal to prevent the whole session from crashing - fix xfail/skip confusion: a skip-mark or an imperative pytest.skip will now take precedence before xfail-markers because we can't determine xfail/xpass status in case of a skip. see also: http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get -- always report installed 3rd party plugins +- always report installed 3rd party plugins in the header of a test run - fix issue160: a failing setup of an xfail-marked tests should be reported as xfail (not xpass) diff -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 -r 5bcd225c3b5eb502209b537546ad0f83e40de032 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -154,7 +154,10 @@ if hasattr(longrepr, 'toterminal'): longrepr.toterminal(out) else: - out.line(str(longrepr)) + try: + out.line(longrepr) + except UnicodeEncodeError: + out.line("") passed = property(lambda x: x.outcome == "passed") failed = property(lambda x: x.outcome == "failed") @@ -279,7 +282,7 @@ def __init__(self, msg): self.longrepr = msg def toterminal(self, out): - out.line(str(self.longrepr), red=True) + out.line(self.longrepr, red=True) class SetupState(object): """ shared state for setting up/tearing down test items or collectors. """ diff -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 -r 5bcd225c3b5eb502209b537546ad0f83e40de032 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -38,13 +38,14 @@ stdout = py.std.sys.stdout if hasattr(os, 'dup') and hasattr(stdout, 'fileno'): try: - newfd = os.dup(stdout.fileno()) - #print "got newfd", newfd + newstdout = py.io.dupfile(stdout, buffering=1, + encoding=stdout.encoding) except ValueError: pass else: - stdout = os.fdopen(newfd, stdout.mode, 1) - config._cleanup.append(lambda: stdout.close()) + config._cleanup.append(lambda: newstdout.close()) + assert stdout.encoding == newstdout.encoding + stdout = newstdout reporter = TerminalReporter(config, stdout) config.pluginmanager.register(reporter, 'terminalreporter') diff -r 86ea199221ab3ee87432d2f55ed81f9b18da7f04 -r 5bcd225c3b5eb502209b537546ad0f83e40de032 testing/test_runner.py --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -456,3 +456,21 @@ ret = popen.wait() assert ret == 0 + +def test_unicode_in_longrepr(testdir): + testdir.makeconftest(""" + import py + def pytest_runtest_makereport(__multicall__): + rep = __multicall__.execute() + if rep.when == "call": + rep.longrepr = py.builtin._totext("\\xc3\\xa4", "utf8") + return rep + """) + testdir.makepyfile(""" + def test_out(): + assert 0 + """) + result = testdir.runpytest() + assert result.ret == 1 + assert "UnicodeEncodeError" not in result.stderr.str() + 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. From commits-noreply at bitbucket.org Mon Jun 25 17:38:58 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 25 Jun 2012 15:38:58 -0000 Subject: [py-svn] commit/pytest: hpk42: mid-scale refactoring to make request API available directly on items. Message-ID: <20120625153858.26078.24329@bitbucket15.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/c529c3593197/ changeset: c529c3593197 user: hpk42 date: 2012-06-25 17:35:33 summary: mid-scale refactoring to make request API available directly on items. This commit was slightly tricky because i want to backward compatibility especially for the oejskit plugin which uses Funcarg-filling for non-Function objects. affected #: 16 files diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,19 @@ -Changes between 2.2.4 and 2.2.5.dev +Changes between 2.2.4 and 2.3.0.dev ----------------------------------- +- merge FuncargRequest and Item API such that funcarg-functionality + is now natively available on the "item" object passed to the various + pytest_runtest hooks. This allows more sensitive behaviour + of e.g. the pytest-django plugin which previously had no full + access to all instantiated funcargs. + This internal API re-organisation is a fully backward compatible + change: existing factories accepting a "request" object will + get a Function "item" object which carries the same API. In fact, + the FuncargRequest API (or rather then a ResourceRequestAPI) + could be available for all collection and item nodes but this is + left for later consideration because it would render the documentation + invalid and the "funcarg" naming sounds odd in context of + directory, file, class, etc. nodes. - catch unicode-issues when writing failure representations to terminal to prevent the whole session from crashing - fix xfail/skip confusion: a skip-mark or an imperative pytest.skip @@ -23,6 +36,7 @@ pytest-django, trial and unittest integration. - reporting refinements: + - pytest_report_header now receives a "startdir" so that you can use startdir.bestrelpath(yourpath) to show nice relative path diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.5.dev4' +__version__ = '2.3.0.dev1' diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -119,7 +119,7 @@ return "", "" def activate_funcargs(self, pyfuncitem): - if hasattr(pyfuncitem, 'funcargs'): + if pyfuncitem.funcargs: for name, capfuncarg in pyfuncitem.funcargs.items(): if name in ('capsys', 'capfd'): assert not hasattr(self, '_capturing_funcarg') @@ -186,7 +186,7 @@ captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. """ - if "capfd" in request._funcargs: + if "capfd" in request.funcargs: raise request.LookupError(error_capsysfderror) return CaptureFuncarg(py.io.StdCapture) @@ -195,7 +195,7 @@ captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. """ - if "capsys" in request._funcargs: + if "capsys" in request.funcargs: raise request.LookupError(error_capsysfderror) if not hasattr(os, 'dup'): pytest.skip("capfd funcarg needs os.dup") diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -3,6 +3,8 @@ import py import pytest, _pytest import os, sys, imp +from _pytest.monkeypatch import monkeypatch + tracebackcutdir = py.path.local(_pytest.__file__).dirpath() # exitcodes for the command line @@ -144,32 +146,161 @@ def compatproperty(name): def fget(self): + # deprecated - use pytest.name return getattr(pytest, name) - return property(fget, None, None, - "deprecated attribute %r, use pytest.%s" % (name, name)) + return property(fget) + +def pyobj_property(name): + def get(self): + node = self.getparent(getattr(pytest, name)) + if node is not None: + return node.obj + doc = "python %s object this node was collected from (can be None)." % ( + name.lower(),) + return property(get, None, None, doc) + +class Request(object): + _argprefix = "pytest_funcarg__" + + class LookupError(LookupError): + """ error while performing funcarg factory lookup. """ + + def _initattr(self): + self._name2factory = {} + self._currentarg = None + + @property + def _plugins(self): + extra = [obj for obj in (self.module, self.instance) if obj] + return self.getplugins() + extra + + def _getscopeitem(self, scope): + if scope == "function": + return self + elif scope == "session": + return None + elif scope == "class": + x = self.getparent(pytest.Class) + if x is not None: + return x + scope = "module" + if scope == "module": + return self.getparent(pytest.Module) + raise ValueError("unknown scope %r" %(scope,)) + + def getfuncargvalue(self, argname): + """ Retrieve a named function argument value. + + This function looks up a matching factory and invokes + it to obtain the return value. The factory receives + the same request object and can itself perform recursive + calls to this method, effectively allowing to make use of + multiple other funcarg values or to decorate values from + other name-matching factories. + """ + try: + return self.funcargs[argname] + except KeyError: + pass + except TypeError: + self.funcargs = getattr(self, "_funcargs", {}) + if argname not in self._name2factory: + self._name2factory[argname] = self.config.pluginmanager.listattr( + plugins=self._plugins, + attrname=self._argprefix + str(argname) + ) + #else: we are called recursively + if not self._name2factory[argname]: + self._raiselookupfailed(argname) + funcargfactory = self._name2factory[argname].pop() + oldarg = self._currentarg + mp = monkeypatch() + mp.setattr(self, '_currentarg', argname) + try: + param = self.callspec.getparam(argname) + except (AttributeError, ValueError): + pass + else: + mp.setattr(self, 'param', param, raising=False) + try: + self.funcargs[argname] = res = funcargfactory(self) + finally: + mp.undo() + return res + + def addfinalizer(self, finalizer): + """ add a no-args finalizer function to be called when the underlying + node is torn down.""" + self.session._setupstate.addfinalizer(finalizer, self) + + def cached_setup(self, setup, teardown=None, + scope="module", extrakey=None): + """ Return a cached testing resource created by ``setup`` & + detroyed by a respective ``teardown(resource)`` call. + + :arg teardown: function receiving a previously setup resource. + :arg setup: a no-argument function creating a resource. + :arg scope: a string value out of ``function``, ``class``, ``module`` + or ``session`` indicating the caching lifecycle of the resource. + :arg extrakey: added to internal caching key. + """ + if not hasattr(self.config, '_setupcache'): + self.config._setupcache = {} # XXX weakref? + colitem = self._getscopeitem(scope) + cachekey = (self._currentarg, colitem, extrakey) + cache = self.config._setupcache + try: + val = cache[cachekey] + except KeyError: + val = setup() + cache[cachekey] = val + if teardown is not None: + def finalizer(): + del cache[cachekey] + teardown(val) + self.session._setupstate.addfinalizer(finalizer, colitem) + return val + + def _raiselookupfailed(self, argname): + available = [] + for plugin in self._plugins: + for name in vars(plugin): + if name.startswith(self._argprefix): + name = name[len(self._argprefix):] + if name not in available: + available.append(name) + fspath, lineno, msg = self.reportinfo() + msg = "LookupError: no factory found for function argument %r" % (argname,) + msg += "\n available funcargs: %s" %(", ".join(available),) + msg += "\n use 'py.test --funcargs [testpath]' for help on them." + raise self.LookupError(msg) class Node(object): - """ base class for all Nodes in the collection tree. + """ base class for Collector and Item the test collection tree. Collector subclasses have children, Items are terminal nodes.""" def __init__(self, name, parent=None, config=None, session=None): - #: a unique name with the scope of the parent + #: a unique name within the scope of the parent node self.name = name #: the parent collector node. self.parent = parent - #: the test config object + #: the pytest config object self.config = config or parent.config - #: the collection this node is part of + #: the session this node is part of self.session = session or parent.session - #: filesystem path where this node was collected from + #: filesystem path where this node was collected from (can be None) self.fspath = getattr(parent, 'fspath', None) + + #: keywords on this node (node name is always contained) + self.keywords = {self.name: True} + + #: fspath sensitive hook proxy used to call pytest hooks self.ihook = self.session.gethookproxy(self.fspath) - self.keywords = {self.name: True} Module = compatproperty("Module") Class = compatproperty("Class") @@ -178,6 +309,11 @@ File = compatproperty("File") Item = compatproperty("Item") + module = pyobj_property("Module") + cls = pyobj_property("Class") + instance = pyobj_property("Instance") + + def _getcustomclass(self, name): cls = getattr(self, name) if cls != getattr(pytest, name): @@ -193,12 +329,14 @@ # methods for ordering nodes @property def nodeid(self): + """ a ::-separated string denoting its collection tree address. """ try: return self._nodeid except AttributeError: self._nodeid = x = self._makeid() return x + def _makeid(self): return self.parent.nodeid + "::" + self.name @@ -338,15 +476,36 @@ class File(FSCollector): """ base class for collecting tests from a file. """ -class Item(Node): +class Item(Node, Request): """ a basic test invocation item. Note that for a single function there might be multiple test invocation items. """ nextitem = None + def __init__(self, name, parent=None, config=None, session=None): + super(Item, self).__init__(name, parent, config, session) + self._initattr() + self.funcargs = None # later set to a dict from fillfuncargs() or + # from getfuncargvalue(). Setting it to + # None prevents users from performing + # "name in item.funcargs" checks too early. + def reportinfo(self): return self.fspath, None, "" + def applymarker(self, marker): + """ Apply a marker to this item. This method is + useful if you have several parametrized function + and want to mark a single one of them. + + :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object + created by a call to ``py.test.mark.NAME(...)``. + """ + if not isinstance(marker, pytest.mark.XYZ.__class__): + raise ValueError("%r is not a py.test.mark.* object") + self.keywords[marker.markname] = marker + + @property def location(self): try: diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/pytester.py --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -318,7 +318,7 @@ # used from runner functional tests item = self.getitem(source) # the test class where we are called from wants to provide the runner - testclassinstance = py.builtin._getimself(self.request.function) + testclassinstance = self.request.instance runner = testclassinstance.getrunner() return runner(item) diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -4,11 +4,27 @@ import sys import pytest from py._code.code import TerminalRepr -from _pytest.monkeypatch import monkeypatch +from _pytest.main import Request, Item import _pytest cutdir = py.path.local(_pytest.__file__).dirpath() +def cached_property(f): + """returns a cached property that is calculated by function f. + taken from http://code.activestate.com/recipes/576563-cached-property/""" + def get(self): + try: + return self._property_cache[f] + except AttributeError: + self._property_cache = {} + x = self._property_cache[f] = f(self) + return x + except KeyError: + x = self._property_cache[f] = f(self) + return x + return property(get) + + def pytest_addoption(parser): group = parser.getgroup("general") group.addoption('--funcargs', @@ -60,13 +76,21 @@ """ the pytest config object with access to command line opts.""" return request.config + def pytest_pyfunc_call(__multicall__, pyfuncitem): if not __multicall__.execute(): testfunction = pyfuncitem.obj if pyfuncitem._isyieldedfunction(): testfunction(*pyfuncitem._args) else: - funcargs = pyfuncitem.funcargs + try: + funcargnames = pyfuncitem.funcargnames + except AttributeError: + funcargs = pyfuncitem.funcargs + else: + funcargs = {} + for name in funcargnames: + funcargs[name] = pyfuncitem.funcargs[name] testfunction(**funcargs) def pytest_collect_file(path, parent): @@ -110,6 +134,7 @@ return False class PyobjMixin(object): + def obj(): def fget(self): try: @@ -232,12 +257,15 @@ gentesthook.pcall(plugins, metafunc=metafunc) Function = self._getcustomclass("Function") if not metafunc._calls: - return Function(name, parent=self) + return Function(name, parent=self, + funcargnames=metafunc.funcargnames) l = [] for callspec in metafunc._calls: subname = "%s[%s]" %(name, callspec.id) function = Function(name=subname, parent=self, - callspec=callspec, callobj=funcobj, keywords={callspec.id:True}) + callspec=callspec, callobj=funcobj, + funcargnames=metafunc.funcargnames, + keywords={callspec.id:True}) l.append(function) return l @@ -256,6 +284,7 @@ pytestmark(funcobj) class Module(pytest.File, PyCollectorMixin): + """ Collector for test classes and functions. """ def _getobj(self): return self._memoizedcall('_obj', self._importtestmodule) @@ -303,7 +332,7 @@ self.obj.teardown_module() class Class(PyCollectorMixin, pytest.Collector): - + """ Collector for test methods. """ def collect(self): return [self._getcustomclass("Instance")(name="()", parent=self)] @@ -373,7 +402,7 @@ excinfo.traceback = ntraceback.filter() def _repr_failure_py(self, excinfo, style="long"): - if excinfo.errisinstance(FuncargRequest.LookupError): + if excinfo.errisinstance(Request.LookupError): fspath, lineno, msg = self.reportinfo() lines, _ = inspect.getsourcelines(self.obj) for i, line in enumerate(lines): @@ -445,77 +474,6 @@ return name, call, args -# -# Test Items -# -_dummy = object() -class Function(FunctionMixin, pytest.Item): - """ a Function Item is responsible for setting up - and executing a Python callable test object. - """ - _genid = None - def __init__(self, name, parent=None, args=None, config=None, - callspec=None, callobj=_dummy, keywords=None, session=None): - super(Function, self).__init__(name, parent, - config=config, session=session) - self._args = args - if self._isyieldedfunction(): - assert not callspec, ( - "yielded functions (deprecated) cannot have funcargs") - else: - if callspec is not None: - self.callspec = callspec - self.funcargs = callspec.funcargs or {} - self._genid = callspec.id - if hasattr(callspec, "param"): - self._requestparam = callspec.param - else: - self.funcargs = {} - if callobj is not _dummy: - self._obj = callobj - self.function = getattr(self.obj, 'im_func', self.obj) - self.keywords.update(py.builtin._getfuncdict(self.obj) or {}) - if keywords: - self.keywords.update(keywords) - - def _getobj(self): - name = self.name - i = name.find("[") # parametrization - if i != -1: - name = name[:i] - return getattr(self.parent.obj, name) - - def _isyieldedfunction(self): - return self._args is not None - - def runtest(self): - """ execute the underlying test function. """ - self.ihook.pytest_pyfunc_call(pyfuncitem=self) - - def setup(self): - super(Function, self).setup() - if hasattr(self, 'funcargs'): - fillfuncargs(self) - - def __eq__(self, other): - try: - return (self.name == other.name and - self._args == other._args and - self.parent == other.parent and - self.obj == other.obj and - getattr(self, '_genid', None) == - getattr(other, '_genid', None) - ) - except AttributeError: - pass - return False - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.parent, self.name)) - def hasinit(obj): init = getattr(obj, '__init__', None) if init: @@ -535,10 +493,20 @@ return argnames[startindex:-numdefaults] return argnames[startindex:] -def fillfuncargs(function): +def fillfuncargs(node): """ fill missing funcargs. """ - request = FuncargRequest(pyfuncitem=function) - request._fillfuncargs() + if not isinstance(node, Function): + node = FuncargRequest(pyfuncitem=node) + if node.funcargs is None: + node.funcargs = getattr(node, "_funcargs", {}) + if not isinstance(node, Function) or not node._isyieldedfunction(): + try: + funcargnames = node.funcargnames + except AttributeError: + funcargnames = getfuncargnames(node.function) + if funcargnames: + for argname in funcargnames: + node.getfuncargvalue(argname) _notexists = object() @@ -697,195 +665,6 @@ l.append(str(val)) return "-".join(l) -class FuncargRequest: - """ A request for function arguments from a test function. - - Note that there is an optional ``param`` attribute in case - there was an invocation to metafunc.addcall(param=...). - If no such call was done in a ``pytest_generate_tests`` - hook, the attribute will not be present. - """ - _argprefix = "pytest_funcarg__" - _argname = None - - class LookupError(LookupError): - """ error on performing funcarg request. """ - - def __init__(self, pyfuncitem): - self._pyfuncitem = pyfuncitem - if hasattr(pyfuncitem, '_requestparam'): - self.param = pyfuncitem._requestparam - extra = [obj for obj in (self.module, self.instance) if obj] - self._plugins = pyfuncitem.getplugins() + extra - self._funcargs = self._pyfuncitem.funcargs.copy() - self._name2factory = {} - self._currentarg = None - - @property - def function(self): - """ function object of the test invocation. """ - return self._pyfuncitem.obj - - @property - def keywords(self): - """ keywords of the test function item. - - .. versionadded:: 2.0 - """ - return self._pyfuncitem.keywords - - @property - def module(self): - """ module where the test function was collected. """ - return self._pyfuncitem.getparent(pytest.Module).obj - - @property - def cls(self): - """ class (can be None) where the test function was collected. """ - clscol = self._pyfuncitem.getparent(pytest.Class) - if clscol: - return clscol.obj - @property - def instance(self): - """ instance (can be None) on which test function was collected. """ - return py.builtin._getimself(self.function) - - @property - def config(self): - """ the pytest config object associated with this request. """ - return self._pyfuncitem.config - - @property - def fspath(self): - """ the file system path of the test module which collected this test. """ - return self._pyfuncitem.fspath - - def _fillfuncargs(self): - argnames = getfuncargnames(self.function) - if argnames: - assert not getattr(self._pyfuncitem, '_args', None), ( - "yielded functions cannot have funcargs") - for argname in argnames: - if argname not in self._pyfuncitem.funcargs: - self._pyfuncitem.funcargs[argname] = self.getfuncargvalue(argname) - - - def applymarker(self, marker): - """ Apply a marker to a single test function invocation. - This method is useful if you don't want to have a keyword/marker - on all function invocations. - - :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object - created by a call to ``py.test.mark.NAME(...)``. - """ - if not isinstance(marker, py.test.mark.XYZ.__class__): - raise ValueError("%r is not a py.test.mark.* object") - self._pyfuncitem.keywords[marker.markname] = marker - - def cached_setup(self, setup, teardown=None, scope="module", extrakey=None): - """ Return a testing resource managed by ``setup`` & - ``teardown`` calls. ``scope`` and ``extrakey`` determine when the - ``teardown`` function will be called so that subsequent calls to - ``setup`` would recreate the resource. - - :arg teardown: function receiving a previously setup resource. - :arg setup: a no-argument function creating a resource. - :arg scope: a string value out of ``function``, ``class``, ``module`` - or ``session`` indicating the caching lifecycle of the resource. - :arg extrakey: added to internal caching key of (funcargname, scope). - """ - if not hasattr(self.config, '_setupcache'): - self.config._setupcache = {} # XXX weakref? - cachekey = (self._currentarg, self._getscopeitem(scope), extrakey) - cache = self.config._setupcache - try: - val = cache[cachekey] - except KeyError: - val = setup() - cache[cachekey] = val - if teardown is not None: - def finalizer(): - del cache[cachekey] - teardown(val) - self._addfinalizer(finalizer, scope=scope) - return val - - def getfuncargvalue(self, argname): - """ Retrieve a function argument by name for this test - function invocation. This allows one function argument factory - to call another function argument factory. If there are two - funcarg factories for the same test function argument the first - factory may use ``getfuncargvalue`` to call the second one and - do something additional with the resource. - """ - try: - return self._funcargs[argname] - except KeyError: - pass - if argname not in self._name2factory: - self._name2factory[argname] = self.config.pluginmanager.listattr( - plugins=self._plugins, - attrname=self._argprefix + str(argname) - ) - #else: we are called recursively - if not self._name2factory[argname]: - self._raiselookupfailed(argname) - funcargfactory = self._name2factory[argname].pop() - oldarg = self._currentarg - mp = monkeypatch() - mp.setattr(self, '_currentarg', argname) - try: - param = self._pyfuncitem.callspec.getparam(argname) - except (AttributeError, ValueError): - pass - else: - mp.setattr(self, 'param', param, raising=False) - try: - self._funcargs[argname] = res = funcargfactory(request=self) - finally: - mp.undo() - return res - - def _getscopeitem(self, scope): - if scope == "function": - return self._pyfuncitem - elif scope == "session": - return None - elif scope == "class": - x = self._pyfuncitem.getparent(pytest.Class) - if x is not None: - return x - scope = "module" - if scope == "module": - return self._pyfuncitem.getparent(pytest.Module) - raise ValueError("unknown finalization scope %r" %(scope,)) - - def addfinalizer(self, finalizer): - """add finalizer function to be called after test function - finished execution. """ - self._addfinalizer(finalizer, scope="function") - - def _addfinalizer(self, finalizer, scope): - colitem = self._getscopeitem(scope) - self._pyfuncitem.session._setupstate.addfinalizer( - finalizer=finalizer, colitem=colitem) - - def __repr__(self): - return "" %(self._pyfuncitem) - - def _raiselookupfailed(self, argname): - available = [] - for plugin in self._plugins: - for name in vars(plugin): - if name.startswith(self._argprefix): - name = name[len(self._argprefix):] - if name not in available: - available.append(name) - fspath, lineno, msg = self._pyfuncitem.reportinfo() - msg = "LookupError: no factory found for function argument %r" % (argname,) - msg += "\n available funcargs: %s" %(", ".join(available),) - msg += "\n use 'py.test --funcargs [testpath]' for help on them." - raise self.LookupError(msg) def showfuncargs(config): from _pytest.main import wrap_session @@ -903,8 +682,8 @@ for plugin in plugins: available = [] for name, factory in vars(plugin).items(): - if name.startswith(FuncargRequest._argprefix): - name = name[len(FuncargRequest._argprefix):] + if name.startswith(Request._argprefix): + name = name[len(Request._argprefix):] if name not in available: available.append([name, factory]) if available: @@ -1009,3 +788,131 @@ self.excinfo.__init__(tp) return issubclass(self.excinfo.type, self.ExpectedException) +# +# the basic py.test Function item +# +_dummy = object() +class Function(FunctionMixin, pytest.Item): + """ a Function Item is responsible for setting up and executing a + Python test function. + """ + _genid = None + def __init__(self, name, parent=None, args=None, config=None, + callspec=None, callobj=_dummy, keywords=None, + session=None, funcargnames=()): + super(Function, self).__init__(name, parent, config=config, + session=session) + self.funcargnames = funcargnames + self._args = args + if self._isyieldedfunction(): + assert not callspec, ( + "yielded functions (deprecated) cannot have funcargs") + else: + if callspec is not None: + self.callspec = callspec + self._funcargs = callspec.funcargs or {} + self._genid = callspec.id + if hasattr(callspec, "param"): + self.param = callspec.param + if callobj is not _dummy: + self._obj = callobj + + self.keywords.update(py.builtin._getfuncdict(self.obj) or {}) + if keywords: + self.keywords.update(keywords) + + @property + def function(self): + "underlying python 'function' object" + return getattr(self.obj, 'im_func', self.obj) + + def _getobj(self): + name = self.name + i = name.find("[") # parametrization + if i != -1: + name = name[:i] + return getattr(self.parent.obj, name) + + @property + def _pyfuncitem(self): + "(compatonly) for code expecting pytest-2.2 style request objects" + return self + + def _isyieldedfunction(self): + return getattr(self, "_args", None) is not None + + def runtest(self): + """ execute the underlying test function. """ + self.ihook.pytest_pyfunc_call(pyfuncitem=self) + + def setup(self): + super(Function, self).setup() + fillfuncargs(self) + + def __eq__(self, other): + try: + return (self.name == other.name and + self._args == other._args and + self.parent == other.parent and + self.obj == other.obj and + getattr(self, '_genid', None) == + getattr(other, '_genid', None) + ) + except AttributeError: + pass + return False + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.parent, self.name)) + + +def itemapi_property(name, set=False): + prop = getattr(Function, name, None) + doc = getattr(prop, "__doc__", None) + def get(self): + return getattr(self._pyfuncitem, name) + if set: + def set(self, value): + setattr(self._pyfuncitem, name, value) + else: + set = None + return property(get, set, None, doc) + + +class FuncargRequest(Request): + """ (deprecated) helper interactions with a test function invocation. + + Note that there is an optional ``param`` attribute in case + there was an invocation to metafunc.addcall(param=...). + If no such call was done in a ``pytest_generate_tests`` + hook, the attribute will not be present. + """ + def __init__(self, pyfuncitem): + self._pyfuncitem = pyfuncitem + Request._initattr(self) + self.getplugins = self._pyfuncitem.getplugins + self.reportinfo = self._pyfuncitem.reportinfo + try: + self.param = self._pyfuncitem.param + except AttributeError: + pass + + def __repr__(self): + return "" % (self._pyfuncitem.name) + + _getscopeitem = itemapi_property("_getscopeitem") + funcargs = itemapi_property("funcargs", set=True) + keywords = itemapi_property("keywords") + module = itemapi_property("module") + cls = itemapi_property("cls") + instance = itemapi_property("instance") + config = itemapi_property("config") + session = itemapi_property("session") + fspath = itemapi_property("fspath") + applymarker = itemapi_property("applymarker") + @property + def function(self): + return self._pyfuncitem.obj diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/resultlog.py --- a/_pytest/resultlog.py +++ b/_pytest/resultlog.py @@ -1,4 +1,6 @@ -""" (disabled by default) create result information in a plain text file. """ +""" log machine-parseable test session result information in a plain +text file. +""" import py diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c _pytest/tmpdir.py --- a/_pytest/tmpdir.py +++ b/_pytest/tmpdir.py @@ -54,15 +54,15 @@ mp.setattr(config, '_tmpdirhandler', t, raising=False) mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False) -def pytest_funcarg__tmpdir(request): +def pytest_funcarg__tmpdir(item): """return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary directory. The returned object is a `py.path.local`_ path object. """ - name = request._pyfuncitem.name + name = item.name name = py.std.re.sub("[\W]", "_", name) - x = request.config._tmpdirhandler.mktemp(name, numbered=True) + x = item.config._tmpdirhandler.mktemp(name, numbered=True) return x diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.2.4.3" +version = release = "2.3.0.dev1" import sys, os @@ -26,6 +26,8 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) +autodoc_member_order = "bysource" + # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. @@ -53,6 +55,7 @@ copyright = u'2011, holger krekel et alii' + # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None @@ -78,7 +81,7 @@ # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. @@ -87,6 +90,8 @@ # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' + + # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c doc/en/funcargs.txt --- a/doc/en/funcargs.txt +++ b/doc/en/funcargs.txt @@ -11,26 +11,27 @@ Dependency injection through function arguments ================================================= -py.test lets you inject objects into test functions and precisely -control their life cycle in relation to the test execution. It is -also possible to run a test function multiple times with different objects. +py.test lets you inject objects into test invocations and precisely +control their life cycle in relation to the overall test execution. Moreover, +you can run a test function multiple times injecting different objects. The basic mechanism for injecting objects is also called the *funcarg mechanism* because objects are ultimately injected by calling a test function with it as an argument. Unlike the classical xUnit approach *funcargs* relate more to `Dependency Injection`_ because they help to de-couple test code from objects required for -them to execute. +them to execute. At test writing time you do not need to care for the +details of how your required resources are constructed or if they +live through a function, class, module or session scope. .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection To create a value with which to call a test function a factory function is called which gets full access to the test function context and can register finalizers or invoke lifecycle-caching helpers. The factory -can be implemented in same test class or test module, or in a -per-directory ``conftest.py`` file or even in an external plugin. This -allows full de-coupling of test code and objects needed for test -execution. +can be implemented in same test class or test module, in a +per-directory ``conftest.py`` file or in an external plugin. This +allows total de-coupling of test and setup code. A test function may be invoked multiple times in which case we speak of :ref:`parametrized testing `. This can be @@ -38,7 +39,7 @@ or with multiple numerical arguments sets and want to reuse the same set of test functions. -py.test comes with :ref:`builtinfuncargs` and there are some refined usages in the examples section. +py.test comes with some :ref:`builtinfuncargs` and there are some refined usages in the examples section. .. _funcarg: @@ -55,10 +56,8 @@ assert myfuncarg == 17 This test function needs an injected object named ``myfuncarg``. -py.test will discover and call the factory named -``pytest_funcarg__myfuncarg`` within the same module in this case. - -Running the test looks like this:: +py.test will automatically discover and call the ``pytest_funcarg__myfuncarg`` +factory. Running the test looks like this:: $ py.test test_simplefactory.py =========================== test session starts ============================ @@ -79,9 +78,9 @@ test_simplefactory.py:5: AssertionError ========================= 1 failed in 0.01 seconds ========================= -This means that indeed the test function was called with a ``myfuncarg`` -argument value of ``42`` and the assert fails. Here is how py.test -comes to call the test function this way: +This shows that the test function was called with a ``myfuncarg`` +argument value of ``42`` and the assert fails as expected. Here is +how py.test comes to call the test function this way: 1. py.test :ref:`finds ` the ``test_function`` because of the ``test_`` prefix. The test function needs a function argument @@ -99,13 +98,22 @@ to use one that isn't available, you'll see an error with a list of available function arguments. -You can always issue:: +.. Note:: - py.test --funcargs test_simplefactory.py + You can always issue:: -to see available function arguments (which you can also -think of as "resources"). + py.test --funcargs test_simplefactory.py + to see available function arguments. + +The request object passed to factories +----------------------------------------- + +Each funcarg factory receives a :py:class:`~_pytest.main.Request` object which +provides methods to manage caching and finalization in the context of the +test invocation as well as several attributes of the the underlying test item. In fact, as of version pytest-2.3, the request API is implemented on all Item +objects and therefore the request object has general :py:class:`Node attributes and methods <_pytest.main.Node>` attributes. This is a backward compatible +change so no changes are neccessary for pre-2.3 funcarg factories. .. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/ @@ -116,27 +124,6 @@ .. _`funcarg factory`: .. _factory: -The funcarg **request** object -============================================= - -Each funcarg factory receives a **request** object tied to a specific test -function call. A request object is passed to a funcarg factory and provides -access to test configuration and context: - -.. autoclass:: _pytest.python.FuncargRequest() - :members: function,cls,module,keywords,config - -.. _`useful caching and finalization helpers`: - -.. automethod:: FuncargRequest.addfinalizer - -.. automethod:: FuncargRequest.cached_setup - -.. automethod:: FuncargRequest.applymarker - -.. automethod:: FuncargRequest.getfuncargvalue - - .. _`test generators`: .. _`parametrizing-tests`: .. _`parametrized test functions`: diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -296,7 +296,6 @@ The :py:mod:`_pytest.terminal` reported specifically uses the reporting hook to print information about a test run. - Collection hooks ------------------------------ @@ -327,37 +326,44 @@ .. autofunction: pytest_runtest_logreport -Reference of important objects involved in hooks +Reference of objects involved in hooks =========================================================== -.. autoclass:: _pytest.config.Config +.. autoclass:: _pytest.main.Request() :members: -.. autoclass:: _pytest.config.Parser +.. autoclass:: _pytest.config.Config() :members: -.. autoclass:: _pytest.main.Node(name, parent) +.. autoclass:: _pytest.config.Parser() :members: -.. - .. autoclass:: _pytest.main.File(fspath, parent) - :members: - - .. autoclass:: _pytest.main.Item(name, parent) - :members: - - .. autoclass:: _pytest.python.Module(name, parent) - :members: - - .. autoclass:: _pytest.python.Class(name, parent) - :members: - - .. autoclass:: _pytest.python.Function(name, parent) - :members: - -.. autoclass:: _pytest.runner.CallInfo +.. autoclass:: _pytest.main.Node() :members: -.. autoclass:: _pytest.runner.TestReport +.. autoclass:: _pytest.main.Collector() + :members: + :show-inheritance: + +.. autoclass:: _pytest.main.Item() + :members: + :show-inheritance: + +.. autoclass:: _pytest.python.Module() + :members: + :show-inheritance: + +.. autoclass:: _pytest.python.Class() + :members: + :show-inheritance: + +.. autoclass:: _pytest.python.Function() + :members: + :show-inheritance: + +.. autoclass:: _pytest.runner.CallInfo() :members: +.. autoclass:: _pytest.runner.TestReport() + :members: + diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c 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.5.dev4', + version='2.3.0.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c testing/test_assertion.py --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -12,33 +12,25 @@ class TestBinReprIntegration: pytestmark = needsnewassert - def pytest_funcarg__hook(self, request): - class MockHook(object): - def __init__(self): - self.called = False - self.args = tuple() - self.kwargs = dict() - - def __call__(self, op, left, right): - self.called = True - self.op = op - self.left = left - self.right = right - mockhook = MockHook() - monkeypatch = request.getfuncargvalue("monkeypatch") - monkeypatch.setattr(util, '_reprcompare', mockhook) - return mockhook - - def test_pytest_assertrepr_compare_called(self, hook): - interpret('assert 0 == 1') - assert hook.called - - - def test_pytest_assertrepr_compare_args(self, hook): - interpret('assert [0, 1] == [0, 2]') - assert hook.op == '==' - assert hook.left == [0, 1] - assert hook.right == [0, 2] + def test_pytest_assertrepr_compare_called(self, testdir): + testdir.makeconftest(""" + l = [] + def pytest_assertrepr_compare(op, left, right): + l.append((op, left, right)) + def pytest_funcarg__l(request): + return l + """) + testdir.makepyfile(""" + def test_hello(): + assert 0 == 1 + def test_check(l): + assert l == [("==", 0, 1)] + """) + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines([ + "*test_hello*FAIL*", + "*test_check*PASS*", + ]) def callequal(left, right): return plugin.pytest_assertrepr_compare('==', left, right) diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -690,8 +690,8 @@ assert val2 == 2 val2 = req.getfuncargvalue("other") # see about caching assert val2 == 2 - req._fillfuncargs() - assert item.funcargs == {'something': 1} + pytest._fillfuncargs(item) + assert item.funcargs == {'something': 1, "other": 2} def test_request_addfinalizer(self, testdir): item = testdir.getitem(""" @@ -700,9 +700,8 @@ request.addfinalizer(lambda: teardownlist.append(1)) def test_func(something): pass """) - req = funcargs.FuncargRequest(item) - req._pyfuncitem.session._setupstate.prepare(item) # XXX - req._fillfuncargs() + item.session._setupstate.prepare(item) + pytest._fillfuncargs(item) # successively check finalization calls teardownlist = item.getparent(pytest.Module).obj.teardownlist ss = item.session._setupstate @@ -799,7 +798,8 @@ req3 = funcargs.FuncargRequest(item3) ret3a = req3.cached_setup(setup, scope="class") ret3b = req3.cached_setup(setup, scope="class") - assert ret3a == ret3b == "hello2" + assert ret3a == "hello2" + assert ret3b == "hello2" req4 = funcargs.FuncargRequest(item4) ret4 = req4.cached_setup(setup, scope="class") assert ret4 == ret3a @@ -830,11 +830,12 @@ ret1 = req1.cached_setup(setup, teardown, scope="function") assert l == ['setup'] # artificial call of finalizer - req1._pyfuncitem.session._setupstate._callfinalizers(item1) + setupstate = req1._pyfuncitem.session._setupstate + setupstate._callfinalizers(item1) assert l == ["setup", "teardown"] ret2 = req1.cached_setup(setup, teardown, scope="function") assert l == ["setup", "teardown", "setup"] - req1._pyfuncitem.session._setupstate._callfinalizers(item1) + setupstate._callfinalizers(item1) assert l == ["setup", "teardown", "setup", "teardown"] def test_request_cached_setup_two_args(self, testdir): @@ -1092,9 +1093,9 @@ def pytest_generate_tests(metafunc): metafunc.addcall(param=metafunc) - def pytest_funcarg__metafunc(request): - assert request._pyfuncitem._genid == "0" - return request.param + def pytest_funcarg__metafunc(item): + assert item._genid == "0" + return item.param def test_function(metafunc, pytestconfig): assert metafunc.config == pytestconfig @@ -1588,3 +1589,61 @@ "*3/x*", "*ZeroDivisionError*", ]) + +class TestRequestAPI: + def test_addfinalizer_cachedsetup_getfuncargvalue(self, testdir): + testdir.makeconftest(""" + l = [] + def pytest_runtest_setup(item): + item.addfinalizer(lambda: l.append(1)) + l2 = item.getfuncargvalue("l") + assert l2 is l + item.cached_setup(lambda: l.append(2), lambda val: l.append(3), + scope="function") + def pytest_funcarg__l(request): + return l + """) + testdir.makepyfile(""" + def test_hello(): + pass + def test_hello2(l): + assert l == [2, 3, 1, 2] + """) + result = testdir.runpytest() + assert result.ret == 0 + result.stdout.fnmatch_lines([ + "*2 passed*", + ]) + + def test_runtest_setup_sees_filled_funcargs(self, testdir): + testdir.makeconftest(""" + def pytest_runtest_setup(item): + assert item.funcargs is None + """) + testdir.makepyfile(""" + def pytest_funcarg__a(request): + return 1 + def pytest_funcarg__b(request): + return request.getfuncargvalue("a") + 1 + def test_hello(b): + assert b == 2 + """) + result = testdir.runpytest() + assert result.ret == 0 + result.stdout.fnmatch_lines([ + "*1 passed*", + ]) + + result = testdir.makeconftest(""" + import pytest + @pytest.mark.trylast + def pytest_runtest_setup(item): + assert item.funcargs == {"a": 1, "b": 2} + """) + result = testdir.runpytest() + assert result.ret == 0 + result.stdout.fnmatch_lines([ + "*1 passed*", + ]) + + diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c testing/test_tmpdir.py --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -2,7 +2,6 @@ import os from _pytest.tmpdir import pytest_funcarg__tmpdir, TempdirHandler -from _pytest.python import FuncargRequest def test_funcarg(testdir): item = testdir.getitem(""" @@ -11,12 +10,12 @@ metafunc.addcall(id='b') def test_func(tmpdir): pass """, 'test_func[a]') - p = pytest_funcarg__tmpdir(FuncargRequest(item)) + p = pytest_funcarg__tmpdir(item) assert p.check() bn = p.basename.strip("0123456789") assert bn.endswith("test_func_a_") item.name = "qwe/\\abc" - p = pytest_funcarg__tmpdir(FuncargRequest(item)) + p = pytest_funcarg__tmpdir(item) assert p.check() bn = p.basename.strip("0123456789") assert bn == "qwe__abc" diff -r 5bcd225c3b5eb502209b537546ad0f83e40de032 -r c529c3593197291538b5b55f59dd1da608433e6c tox.ini --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps=pytest-xdist commands= py.test -n3 -rfsxX \ - --ignore .tox --junitxml={envlogdir}/junit-{envname}.xml [] + --ignore .tox --junitxml={envlogdir}/junit-{envname}.xml testing [testenv:trial] changedir=. 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. From commits-noreply at bitbucket.org Mon Jun 25 18:08:19 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 25 Jun 2012 16:08:19 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20120625160819.21062.32993@bitbucket05.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/a01438c16511/ changeset: a01438c16511 user: hpk42 date: 2012-06-25 17:49:13 summary: better name for the oejskit-compatibility-class. affected #: 2 files diff -r c529c3593197291538b5b55f59dd1da608433e6c -r a01438c16511c56cdc6c608f5327901094ae509b _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -496,7 +496,7 @@ def fillfuncargs(node): """ fill missing funcargs. """ if not isinstance(node, Function): - node = FuncargRequest(pyfuncitem=node) + node = OldFuncargRequest(pyfuncitem=node) if node.funcargs is None: node.funcargs = getattr(node, "_funcargs", {}) if not isinstance(node, Function) or not node._isyieldedfunction(): @@ -882,7 +882,7 @@ return property(get, set, None, doc) -class FuncargRequest(Request): +class OldFuncargRequest(Request): """ (deprecated) helper interactions with a test function invocation. Note that there is an optional ``param`` attribute in case @@ -901,7 +901,7 @@ pass def __repr__(self): - return "" % (self._pyfuncitem.name) + return "" % (self._pyfuncitem.name) _getscopeitem = itemapi_property("_getscopeitem") funcargs = itemapi_property("funcargs", set=True) diff -r c529c3593197291538b5b55f59dd1da608433e6c -r a01438c16511c56cdc6c608f5327901094ae509b testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -549,7 +549,7 @@ return 42 """) item = testdir.getitem("def test_func(some): pass") - exc = pytest.raises(funcargs.FuncargRequest.LookupError, + exc = pytest.raises(funcargs.OldFuncargRequest.LookupError, "funcargs.fillfuncargs(item)") s = str(exc.value) assert s.find("xyzsomething") != -1 @@ -624,7 +624,7 @@ def pytest_funcarg__something(request): pass def test_func(something): pass """) - req = funcargs.FuncargRequest(item) + req = funcargs.OldFuncargRequest(item) assert req.function == item.obj assert req.keywords is item.keywords assert hasattr(req.module, 'test_func') @@ -639,7 +639,7 @@ def test_func(self, something): pass """) - req = funcargs.FuncargRequest(item) + req = funcargs.OldFuncargRequest(item) assert req.cls.__name__ == "TestB" assert req.instance.__class__ == req.cls @@ -653,7 +653,7 @@ """) item1, = testdir.genitems([modcol]) assert item1.name == "test_method" - name2factory = funcargs.FuncargRequest(item1)._name2factory + name2factory = funcargs.OldFuncargRequest(item1)._name2factory assert len(name2factory) == 1 assert name2factory[0].__name__ == "pytest_funcarg__something" @@ -668,7 +668,7 @@ def test_func(something): assert something == 2 """) - req = funcargs.FuncargRequest(item) + req = funcargs.OldFuncargRequest(item) val = req.getfuncargvalue("something") assert val == 2 @@ -680,7 +680,7 @@ return l.pop() def test_func(something): pass """) - req = funcargs.FuncargRequest(item) + req = funcargs.OldFuncargRequest(item) pytest.raises(req.LookupError, req.getfuncargvalue, "notexists") val = req.getfuncargvalue("something") assert val == 1 @@ -728,7 +728,7 @@ def test_request_getmodulepath(self, testdir): modcol = testdir.getmodulecol("def test_somefunc(): pass") item, = testdir.genitems([modcol]) - req = funcargs.FuncargRequest(item) + req = funcargs.OldFuncargRequest(item) assert req.fspath == modcol.fspath def test_applymarker(testdir): @@ -739,7 +739,7 @@ def test_func2(self, something): pass """) - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.OldFuncargRequest(item1) assert 'xfail' not in item1.keywords req1.applymarker(pytest.mark.xfail) assert 'xfail' in item1.keywords @@ -757,7 +757,7 @@ def test_func2(self, something): pass """) - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.OldFuncargRequest(item1) l = ["hello"] def setup(): return l.pop() @@ -766,7 +766,7 @@ assert ret1 == "hello" ret1b = req1.cached_setup(setup) assert ret1 == ret1b - req2 = funcargs.FuncargRequest(item2) + req2 = funcargs.OldFuncargRequest(item2) ret2 = req2.cached_setup(setup) assert ret2 == ret1 @@ -782,7 +782,7 @@ def test_func2b(self, something): pass """) - req1 = funcargs.FuncargRequest(item2) + req1 = funcargs.OldFuncargRequest(item2) l = ["hello2", "hello"] def setup(): return l.pop() @@ -791,22 +791,22 @@ # automatically turn "class" to "module" scope ret1 = req1.cached_setup(setup, scope="class") assert ret1 == "hello" - req2 = funcargs.FuncargRequest(item2) + req2 = funcargs.OldFuncargRequest(item2) ret2 = req2.cached_setup(setup, scope="class") assert ret2 == "hello" - req3 = funcargs.FuncargRequest(item3) + req3 = funcargs.OldFuncargRequest(item3) ret3a = req3.cached_setup(setup, scope="class") ret3b = req3.cached_setup(setup, scope="class") assert ret3a == "hello2" assert ret3b == "hello2" - req4 = funcargs.FuncargRequest(item4) + req4 = funcargs.OldFuncargRequest(item4) ret4 = req4.cached_setup(setup, scope="class") assert ret4 == ret3a def test_request_cachedsetup_extrakey(self, testdir): item1 = testdir.getitem("def test_func(): pass") - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.OldFuncargRequest(item1) l = ["hello", "world"] def setup(): return l.pop() @@ -821,7 +821,7 @@ def test_request_cachedsetup_cache_deletion(self, testdir): item1 = testdir.getitem("def test_func(): pass") - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.OldFuncargRequest(item1) l = [] def setup(): l.append("setup") https://bitbucket.org/hpk42/pytest/changeset/6c84905dcbf3/ changeset: 6c84905dcbf3 user: hpk42 date: 2012-06-25 18:08:12 summary: fix issue139 - make it possible to access funcargs from pytest_runtest_setup affected #: 1 file diff -r a01438c16511c56cdc6c608f5327901094ae509b -r 6c84905dcbf31ab327ca02491013208a2effd525 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,11 @@ Changes between 2.2.4 and 2.3.0.dev ----------------------------------- -- merge FuncargRequest and Item API such that funcarg-functionality - is now natively available on the "item" object passed to the various - pytest_runtest hooks. This allows more sensitive behaviour - of e.g. the pytest-django plugin which previously had no full - access to all instantiated funcargs. +- fix issue139 - merge FuncargRequest and Item API such that + funcarg-functionality is now directly available on the "item" + object passed to the various pytest_runtest hooks. This allows more + sensitive behaviour of e.g. the pytest-django plugin which previously + had no full access to all instantiated funcargs. This internal API re-organisation is a fully backward compatible change: existing factories accepting a "request" object will get a Function "item" object which carries the same API. In fact, 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. From commits-noreply at bitbucket.org Tue Jun 26 20:28:17 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 26 Jun 2012 18:28:17 -0000 Subject: [py-svn] commit/pytest: hpk42: remove unused code Message-ID: <20120626182817.25542.86954@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/b6692c800dd4/ changeset: b6692c800dd4 user: hpk42 date: 2012-06-26 20:28:09 summary: remove unused code affected #: 1 file diff -r 6c84905dcbf31ab327ca02491013208a2effd525 -r b6692c800dd434d1b6abca3e4b4bb261b59911eb _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -35,9 +35,6 @@ if option.dest: self._processopt(option) - def addnote(self, note): - self._notes.append(note) - def getgroup(self, name, description="", after=None): """ get (or create) a named option Group. 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. From commits-noreply at bitbucket.org Tue Jun 26 21:56:19 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 26 Jun 2012 19:56:19 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue151 - heuristcally lookup conftest files on all command line arguments, not just the first existing dir/file Message-ID: <20120626195619.11526.21397@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/6d5db6fbe405/ changeset: 6d5db6fbe405 user: hpk42 date: 2012-06-26 21:56:03 summary: fix issue151 - heuristcally lookup conftest files on all command line arguments, not just the first existing dir/file you can install the corresponding pytest-2.3.dev2 via pip install -i http:/pypi.testrun.org -U pytest affected #: 4 files diff -r b6692c800dd434d1b6abca3e4b4bb261b59911eb -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev1' +__version__ = '2.3.0.dev2' diff -r b6692c800dd434d1b6abca3e4b4bb261b59911eb -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -151,20 +151,24 @@ p = current.join(opt1[len(opt)+1:], abs=1) self._confcutdir = p break - for arg in args + [current]: + foundanchor = False + for arg in args: if hasattr(arg, 'startswith') and arg.startswith("--"): continue anchor = current.join(arg, abs=1) if anchor.check(): # we found some file object - self._path2confmods[None] = self.getconftestmodules(anchor) - # let's also consider test* dirs - if anchor.check(dir=1): - for x in anchor.listdir("test*"): - if x.check(dir=1): - self.getconftestmodules(x) - break - else: - assert 0, "no root of filesystem?" + self._try_load_conftest(anchor) + foundanchor = True + if not foundanchor: + self._try_load_conftest(current) + + def _try_load_conftest(self, anchor): + self._path2confmods[None] = self.getconftestmodules(anchor) + # let's also consider test* subdirs + if anchor.check(dir=1): + for x in anchor.listdir("test*"): + if x.check(dir=1): + self.getconftestmodules(x) def getconftestmodules(self, path): """ return a list of imported conftest modules for the given path. """ diff -r b6692c800dd434d1b6abca3e4b4bb261b59911eb -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 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.3.0.dev1', + version='2.3.0.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r b6692c800dd434d1b6abca3e4b4bb261b59911eb -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 testing/test_conftest.py --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -15,7 +15,8 @@ d.ensure("adir/__init__.py") d.ensure("adir/b/__init__.py") return d - return request.cached_setup(lambda: basedirmaker(request), extrakey=request.param) + return request.cached_setup( + lambda: basedirmaker(request), extrakey=request.param) def ConftestWithSetinitial(path): conftest = Conftest() @@ -106,6 +107,17 @@ l = conftest.getconftestmodules(None) assert len(l) == 0 +def test_issue151_load_all_conftests(testdir): + names = "code proj src".split() + for name in names: + p = testdir.mkdir(name) + p.ensure("conftest.py") + + conftest = Conftest() + conftest.setinitial(names) + d = list(conftest._conftestpath2mod.values()) + assert len(d) == len(names) + def test_conftest_global_import(testdir): testdir.makeconftest("x=3") p = 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. From commits-noreply at bitbucket.org Wed Jun 27 17:27:40 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 27 Jun 2012 15:27:40 -0000 Subject: [py-svn] commit/pytest: RonnyPfannschmidt: test and implement showing verbose assert repr for py.test -vv Message-ID: <20120627152740.27721.95851@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/2e06f917619d/ changeset: 2e06f917619d user: RonnyPfannschmidt date: 2012-06-27 17:26:55 summary: test and implement showing verbose assert repr for py.test -vv affected #: 4 files diff -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 -r 2e06f917619d045818b48651714c1ea5d59056e4 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -46,6 +46,8 @@ - don't show deselected reason line if there is none + - py.test -vv will show all of assert comparisations instead of truncating + Changes between 2.2.3 and 2.2.4 ----------------------------------- diff -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 -r 2e06f917619d045818b48651714c1ea5d59056e4 _pytest/assertion/__init__.py --- a/_pytest/assertion/__init__.py +++ b/_pytest/assertion/__init__.py @@ -73,8 +73,12 @@ def callbinrepr(op, left, right): hook_result = item.ihook.pytest_assertrepr_compare( config=item.config, op=op, left=left, right=right) + for new_expl in hook_result: if new_expl: + # Don't include pageloads of data unless we are very verbose (-vv) + if len(''.join(new_expl[1:])) > 80*8 and item.config.option.verbose < 2: + new_expl[1:] = ['Detailed information too verbose, truncated'] res = '\n~'.join(new_expl) if item.config.getvalue("assertmode") == "rewrite": # The result will be fed back a python % formatting diff -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 -r 2e06f917619d045818b48651714c1ea5d59056e4 _pytest/assertion/util.py --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -121,9 +121,6 @@ if not explanation: return None - # Don't include pageloads of data, should be configurable - if len(''.join(explanation)) > 80*8: - explanation = ['Detailed information too verbose, truncated'] return [summary] + explanation diff -r 6d5db6fbe405aacc5b828ec1f461f7699fa25664 -r 2e06f917619d045818b48651714c1ea5d59056e4 testing/test_assertion.py --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -150,6 +150,29 @@ "*E*'y'*", ]) + +def test_assert_compare_truncate_longmessage(testdir): + testdir.makepyfile(r""" + def test_long(): + a = list(range(200)) + b = a[::2] + a = '\n'.join(map(str, a)) + b = '\n'.join(map(str, b)) + assert a == b + """) + + result = testdir.runpytest() + result.stdout.fnmatch_lines([ + "*too verbose, truncated*", + ]) + + + result = testdir.runpytest('-vv') + result.stdout.fnmatch_lines([ + "*- 197", + ]) + + @needsnewassert def test_assertrepr_loaded_per_dir(testdir): testdir.makepyfile(test_base=['def test_base(): assert 1 == 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. From py-svn at codespeak.net Thu Jun 28 13:55:35 2012 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Thu, 28 Jun 2012 13:55:35 +0200 (CEST) Subject: [py-svn] py-svn@codespeak.net Breitling Discount ID77911 Message-ID: <20120628115535.490EF2A202F@codespeak.net> An HTML attachment was scrubbed... URL: