From fad6266a474a216e21335e45e90e25020bc98457 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 1 Jun 2016 08:18:10 +0200 Subject: [PATCH 01/25] finish up the version bump in __init__ i forgot --- _pytest/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/__init__.py b/_pytest/__init__.py index 23dac6d05..ed5e26626 100644 --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.9.2' +__version__ = '2.9.3.dev0' From 7305adfdbafd9ecf97ad5469f0ff6d92d4f16268 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 1 Jun 2016 08:55:44 +0200 Subject: [PATCH 02/25] fix another copy&paste error in the announcement list --- doc/en/announce/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 85bb3415b..1afee3813 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -7,7 +7,7 @@ Release announcements sprint2016 - release-2.9.1 + release-2.9.2 release-2.9.1 release-2.9.0 release-2.8.7 From 2898dffb9e3b80fecb44c8201b3d6a8d3910edd7 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 1 Jun 2016 17:43:41 +0100 Subject: [PATCH 03/25] Fix rst-lint CHANGELOG error --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c85a0e8d0..2be8c9aae 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,5 +1,5 @@ 2.9.3.dev -===== +========= **Bug Fixes** From 74862b8f2fa483389b2b79cb2b76954e3c5ff7df Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 1 Jun 2016 17:16:05 +0100 Subject: [PATCH 04/25] Don't mark empty doctest files as skipped, fixes #1578 --- _pytest/doctest.py | 14 ++++++-------- testing/test_doctest.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/_pytest/doctest.py b/_pytest/doctest.py index a57f7a494..72b94f488 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -144,20 +144,18 @@ def get_optionflags(parent): return flag_acc -class DoctestTextfile(DoctestItem, pytest.Module): +class DoctestTextfile(pytest.Module): + obj = None - def runtest(self): + def collect(self): import doctest - fixture_request = _setup_fixtures(self) # inspired by doctest.testfile; ideally we would use it directly, # but it doesn't support passing a custom checker text = self.fspath.read() filename = str(self.fspath) name = self.fspath.basename - globs = dict(getfixture=fixture_request.getfuncargvalue) - if '__name__' not in globs: - globs['__name__'] = '__main__' + globs = {'__name__': '__main__'} optionflags = get_optionflags(self) runner = doctest.DebugRunner(verbose=0, optionflags=optionflags, @@ -165,8 +163,8 @@ class DoctestTextfile(DoctestItem, pytest.Module): parser = doctest.DocTestParser() test = parser.get_doctest(text, globs, name, filename, 0) - _check_all_skipped(test) - runner.run(test) + if test.examples: + yield DoctestItem(test.name, self, runner, test) def _check_all_skipped(test): diff --git a/testing/test_doctest.py b/testing/test_doctest.py index a4821ee4c..816f069d3 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -14,13 +14,16 @@ class TestDoctests: >>> i-1 4 """) + for x in (testdir.tmpdir, checkfile): #print "checking that %s returns custom items" % (x,) items, reprec = testdir.inline_genitems(x) assert len(items) == 1 - assert isinstance(items[0], DoctestTextfile) + assert isinstance(items[0], DoctestItem) + assert isinstance(items[0].parent, DoctestTextfile) + # Empty file has no items. items, reprec = testdir.inline_genitems(w) - assert len(items) == 1 + assert len(items) == 0 def test_collect_module_empty(self, testdir): path = testdir.makepyfile(whatever="#") @@ -595,6 +598,11 @@ class TestDoctestSkips: reprec = testdir.inline_run("--doctest-modules") reprec.assertoutcome(skipped=1) + def test_vacuous_all_skipped(self, testdir, makedoctest): + makedoctest('') + reprec = testdir.inline_run("--doctest-modules") + reprec.assertoutcome(passed=0, skipped=0) + class TestDoctestAutoUseFixtures: From dd9c81ca26fae53e2c9dc545d7779aa221763bc0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Jun 2016 19:56:45 -0300 Subject: [PATCH 05/25] Mention #1580 in the CHANGELOG and add Thomas Grainger to AUTHORS --- AUTHORS | 1 + CHANGELOG.rst | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index f4a21b22d..76726af2b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -79,6 +79,7 @@ Ross Lawley Ryan Wooden Samuele Pedroni Tom Viner +Thomas Grainger Trevor Bekolay Wouter van Ackooy David Díaz-Barquero diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2be8c9aae..4783395a8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,12 +3,18 @@ **Bug Fixes** -* +* Text documents without any doctests no longer appear as "skipped". + Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_). * * +.. _#1580: https://github.com/pytest-dev/pytest/issues/1580 + +.. _@graingert: https://github.com/graingert + + 2.9.2 ===== From 63ced4d486c3d6906eadaa39ec8f557a6afa4aff Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Jun 2016 20:21:51 -0300 Subject: [PATCH 06/25] List contributors alphabetically in AUTHORS --- AUTHORS | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 76726af2b..b41e4c43d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,6 +4,7 @@ merlinux GmbH, Germany, office at merlinux eu Contributors include:: Abhijeet Kasurde +Alexei Kozlenok Anatoly Bubenkoff Andreas Zeidler Andy Freeland @@ -11,12 +12,14 @@ Anthon van der Neut Armin Rigo Aron Curzon Aviv Palivoda +Ben Webb Benjamin Peterson Bob Ippolito Brian Dorsey Brian Okken Brianna Laugher Bruno Oliveira +Cal Leeming Carl Friedrich Bolz Charles Cloud Chris Lamb @@ -27,15 +30,18 @@ Daniel Grana Daniel Hahler Daniel Nuri Dave Hunt +David Díaz-Barquero David Mohr David Vierra Edison Gustavo Muenz Eduardo Schettino -Endre Galaczi Elizaveta Shashkova +Endre Galaczi +Eric Hunsberger Eric Hunsberger Eric Siegerman Erik M. Bray +Feng Ma Florian Bruhin Floris Bruynooghe Gabriel Reis @@ -76,17 +82,11 @@ Ralf Schmitt Raphael Pierzina Ronny Pfannschmidt Ross Lawley +Russel Winder Ryan Wooden Samuele Pedroni -Tom Viner +Simon Gomizelj Thomas Grainger +Tom Viner Trevor Bekolay Wouter van Ackooy -David Díaz-Barquero -Eric Hunsberger -Simon Gomizelj -Russel Winder -Ben Webb -Alexei Kozlenok -Cal Leeming -Feng Ma From 2c4b76b7542449e29531ba291174fdb3383d4c86 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Jun 2016 20:29:55 -0300 Subject: [PATCH 07/25] Use same python which invoked tox for "doctesting" env This will work for Travis and AppVeyor because both start tox using Python 3 --- CONTRIBUTING.rst | 6 +++--- tox.ini | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 75ee3ec32..5a00c191b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -211,7 +211,7 @@ but here is a simple overview: You need to have Python 2.7 and 3.5 available in your system. Now running tests is as simple as issuing this command:: - $ python runtox.py -e linting,py27,py35 + $ python3 runtox.py -e linting,py27,py35 This command will run tests via the "tox" tool against Python 2.7 and 3.5 and also perform "lint" coding-style checks. ``runtox.py`` is @@ -226,11 +226,11 @@ but here is a simple overview: To run tests on py27 and pass options to pytest (e.g. enter pdb on failure) to pytest you can do:: - $ python runtox.py -e py27 -- --pdb + $ python3 runtox.py -e py27 -- --pdb or to only run tests in a particular test module on py35:: - $ python runtox.py -e py35 -- testing/test_config.py + $ python3 runtox.py -e py35 -- testing/test_config.py #. Commit and push once your tests pass and you are happy with your change(s):: diff --git a/tox.ini b/tox.ini index 5cf6e5133..e3922959e 100644 --- a/tox.ini +++ b/tox.ini @@ -100,7 +100,7 @@ commands= make html [testenv:doctesting] -basepython = python3 +basepython = python changedir=doc/en deps=PyYAML commands= py.test -rfsxX {posargs} From 3ad5b9de86c38ba9015495ec30dcf81672a4a2a3 Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Thu, 2 Jun 2016 09:52:56 +0300 Subject: [PATCH 08/25] Docs: config.option is deprecated https://pytest.org/latest/writing_plugins.html#_pytest.config.Config says config.option is deprecated and one should use config.getoption() instead. --- doc/en/example/simple.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 5aaf17cc2..1007ee1e1 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -273,13 +273,13 @@ which will add the string to the test header accordingly:: You can also return a list of strings which will be considered as several lines of information. You can of course also make the amount of reporting -information on e.g. the value of ``config.option.verbose`` so that +information on e.g. the value of ``config.getoption('verbose')`` so that you present more information appropriately:: # content of conftest.py def pytest_report_header(config): - if config.option.verbose > 0: + if config.getoption('verbose') > 0: return ["info1: did you know that ...", "did you?"] which will add info only when run with "--v":: From 09d163aa3a1f01701fe2e3f073a89d49b9d0cce2 Mon Sep 17 00:00:00 2001 From: marscher Date: Tue, 7 Jun 2016 15:43:05 +0200 Subject: [PATCH 09/25] [exception handling] Fix case the current working directory (CWD) gets deleted during testing. Fixes #1235. --- AUTHORS | 1 + CHANGELOG.rst | 4 ++++ _pytest/_code/code.py | 11 +++++++---- _pytest/main.py | 8 +++++++- testing/code/test_excinfo.py | 11 +++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index b41e4c43d..d740fee5c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -69,6 +69,7 @@ Mark Abramowitz Markus Unterwaditzer Martijn Faassen Martin Prusse +Martin K. Scherer Matt Bachmann Michael Aquilina Michael Birtwell diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4783395a8..adc6993eb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,10 @@ * +* Fix exception visualization in case the current working directory (CWD) gets + deleted during testing. Fixes (`#1235`). Thanks `@bukzor` for reporting. PR by + `@marscher`. Thanks `@nicoddemus` for his help. + .. _#1580: https://github.com/pytest-dev/pytest/issues/1580 .. _@graingert: https://github.com/graingert diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 8995cc1f7..c6f811c85 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -2,7 +2,6 @@ import sys from inspect import CO_VARARGS, CO_VARKEYWORDS import py - builtin_repr = repr reprlib = py.builtin._tryimport('repr', 'reprlib') @@ -35,12 +34,16 @@ class Code(object): def path(self): """ return a path object pointing to source code (note that it might not point to an actually existing file). """ - p = py.path.local(self.raw.co_filename) - # maybe don't try this checking - if not p.check(): + try: + p = py.path.local(self.raw.co_filename) + # maybe don't try this checking + if not p.check(): + raise OSError("py.path check failed.") + except OSError: # XXX maybe try harder like the weird logic # in the standard lib [linecache.updatecache] does? p = self.raw.co_filename + return p @property diff --git a/_pytest/main.py b/_pytest/main.py index 8654d7af6..f608a7ecd 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -403,7 +403,13 @@ class Node(object): else: style = "long" - return excinfo.getrepr(funcargs=True, + try: + os.getcwd() + abspath = False + except OSError: + abspath = True + + return excinfo.getrepr(funcargs=True, abspath=abspath, showlocals=self.config.option.showlocals, style=style, tbfilter=tbfilter) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 47ad50b06..ba047f2a9 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -925,3 +925,14 @@ def test_repr_traceback_with_unicode(style, encoding): repr_traceback = formatter.repr_traceback(e_info) assert repr_traceback is not None + +def test_cwd_deleted(testdir): + testdir.makepyfile(""" + def test(tmpdir): + tmpdir.chdir() + tmpdir.remove() + assert False + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 1 failed in *']) + assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str() From accd962c9f88dbd5b2b0eef6efe7bf6fe5444b29 Mon Sep 17 00:00:00 2001 From: Guyzmo Date: Sat, 11 Jun 2016 18:04:49 +0200 Subject: [PATCH 10/25] Fixed issue shadowing error when missing argument on teardown_method When the method argument is missing on teardown_method, the traceback is 100% internal to pytest, which with default options get pruned. Then that traceback is empty, leading to a new exception as a traceback shall not be empty. This PR fixes that issue by pushing back the last stack on the traceback, when the stacktrace is empty after pruning. Then the output is still pruned, but gives meaningful information with the item where it failed on the stack. * fixes issue #1604 Signed-off-by: Guyzmo --- AUTHORS | 1 + CHANGELOG.rst | 4 +++- _pytest/main.py | 3 +++ testing/test_runner.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index d740fee5c..28cce969d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -91,3 +91,4 @@ Thomas Grainger Tom Viner Trevor Bekolay Wouter van Ackooy +Bernard Pratz diff --git a/CHANGELOG.rst b/CHANGELOG.rst index adc6993eb..1a8dc9433 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,8 @@ * Text documents without any doctests no longer appear as "skipped". Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_). -* +* Fix internal error issue when ``method`` argument is missing for + ``teardown_method()``. Fixes (`#1605`_). * @@ -15,6 +16,7 @@ `@marscher`. Thanks `@nicoddemus` for his help. .. _#1580: https://github.com/pytest-dev/pytest/issues/1580 +.. _#1605: https://github.com/pytest-dev/pytest/issues/1605 .. _@graingert: https://github.com/graingert diff --git a/_pytest/main.py b/_pytest/main.py index f608a7ecd..4a6c08775 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -392,7 +392,10 @@ class Node(object): if self.config.option.fulltrace: style="long" else: + tb = _pytest._code.Traceback([excinfo.traceback[-1]]) self._prunetraceback(excinfo) + if len(excinfo.traceback) == 0: + excinfo.traceback = tb tbfilter = False # prunetraceback already does it if style == "auto": style = "long" diff --git a/testing/test_runner.py b/testing/test_runner.py index 4421c5d0d..377801132 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -228,6 +228,39 @@ class BaseFunctionalTests: assert reps[5].nodeid.endswith("test_func") assert reps[5].failed + def test_exact_teardown_issue1206(self, testdir): + rec = testdir.inline_runsource(""" + import pytest + + class TestClass: + def teardown_method(self): + pass + + def test_method(self): + assert True + """) + reps = rec.getreports("pytest_runtest_logreport") + print (reps) + assert len(reps) == 3 + # + assert reps[0].nodeid.endswith("test_method") + assert reps[0].passed + assert reps[0].when == 'setup' + # + assert reps[1].nodeid.endswith("test_method") + assert reps[1].passed + assert reps[1].when == 'call' + # + assert reps[2].nodeid.endswith("test_method") + assert reps[2].failed + assert reps[2].when == "teardown" + assert reps[2].longrepr.reprcrash.message in ( + # python3 error + 'TypeError: teardown_method() takes 1 positional argument but 2 were given', + # python2 error + 'TypeError: teardown_method() takes exactly 1 argument (2 given)' + ) + def test_failure_in_setup_function_ignores_custom_repr(self, testdir): testdir.makepyfile(conftest=""" import pytest From e2e6e317118d6d15ccdf6f6708a48c08d85cbc25 Mon Sep 17 00:00:00 2001 From: taschini Date: Wed, 8 Jun 2016 15:17:55 +0200 Subject: [PATCH 11/25] Ensure that a module within a namespace package can be found by --pyargs. --- _pytest/main.py | 47 +++++++++------------ testing/acceptance_test.py | 83 +++++++++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 35 deletions(-) diff --git a/_pytest/main.py b/_pytest/main.py index 4a6c08775..40df13238 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -1,7 +1,5 @@ """ core implementation of testing process: init, session, runtest loop. """ -import imp import os -import re import sys import _pytest @@ -25,8 +23,6 @@ EXIT_INTERNALERROR = 3 EXIT_USAGEERROR = 4 EXIT_NOTESTSCOLLECTED = 5 -name_re = re.compile("^[a-zA-Z_]\w*$") - def pytest_addoption(parser): parser.addini("norecursedirs", "directory patterns to avoid for recursion", type="args", default=['.*', 'CVS', '_darcs', '{arch}', '*.egg']) @@ -658,36 +654,29 @@ class Session(FSCollector): return True def _tryconvertpyarg(self, x): - mod = None - path = [os.path.abspath('.')] + sys.path - for name in x.split('.'): - # ignore anything that's not a proper name here - # else something like --pyargs will mess up '.' - # since imp.find_module will actually sometimes work for it - # but it's supposed to be considered a filesystem path - # not a package - if name_re.match(name) is None: - return x - try: - fd, mod, type_ = imp.find_module(name, path) - except ImportError: - return x - else: - if fd is not None: - fd.close() + """Convert a dotted module name to path. - if type_[2] != imp.PKG_DIRECTORY: - path = [os.path.dirname(mod)] - else: - path = [mod] - return mod + """ + import pkgutil + try: + loader = pkgutil.find_loader(x) + except ImportError: + return x + if loader is None: + return x + try: + path = loader.get_filename() + except: + path = loader.modules[x][0].co_filename + if loader.is_package(x): + path = os.path.dirname(path) + return path def _parsearg(self, arg): """ return (fspath, names) tuple after checking the file exists. """ - arg = str(arg) - if self.config.option.pyargs: - arg = self._tryconvertpyarg(arg) parts = str(arg).split("::") + if self.config.option.pyargs: + parts[0] = self._tryconvertpyarg(parts[0]) relpath = parts[0].replace("/", os.sep) path = self.config.invocation_dir.join(relpath, abs=True) if not path.check(): diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 4e9645037..c66a1740f 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys import _pytest._code @@ -514,11 +515,20 @@ class TestInvocationVariants: result = testdir.runpytest("--pyargs", "tpkg.test_hello") assert result.ret != 0 - # FIXME: It would be more natural to match NOT - # "ERROR*file*or*package*not*found*". - result.stdout.fnmatch_lines([ - "*collected 0 items*" - ]) + + # Depending on whether the process running the test is the + # same as the process parsing the command-line arguments, the + # type of failure can be different: + if result.stderr.str() == '': + # Different processes + result.stdout.fnmatch_lines([ + "collected*0*items*/*1*errors" + ]) + else: + # Same process + result.stderr.fnmatch_lines([ + "ERROR:*file*or*package*not*found:*tpkg.test_hello" + ]) def test_cmdline_python_package(self, testdir, monkeypatch): monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False) @@ -557,6 +567,68 @@ class TestInvocationVariants: "*not*found*test_hello*", ]) + def test_cmdline_python_namespace_package(self, testdir, monkeypatch): + """ + test --pyargs option with namespace packages (#1567) + """ + monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False) + + search_path = [] + for dirname in "hello", "world": + d = testdir.mkdir(dirname) + search_path.append(d) + ns = d.mkdir("ns_pkg") + ns.join("__init__.py").write( + "__import__('pkg_resources').declare_namespace(__name__)") + lib = ns.mkdir(dirname) + lib.ensure("__init__.py") + lib.join("test_{0}.py".format(dirname)). \ + write("def test_{0}(): pass\n" + "def test_other():pass".format(dirname)) + + # The structure of the test directory is now: + # . + # ├── hello + # │   └── ns_pkg + # │   ├── __init__.py + # │   └── hello + # │   ├── __init__.py + # │   └── test_hello.py + # └── world + # └── ns_pkg + # ├── __init__.py + # └── world + # ├── __init__.py + # └── test_world.py + + def join_pythonpath(*dirs): + cur = py.std.os.environ.get('PYTHONPATH') + if cur: + dirs += (cur,) + return ':'.join(str(p) for p in dirs) + monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path)) + for p in search_path: + monkeypatch.syspath_prepend(p) + + # mixed module and filenames: + result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "world/ns_pkg") + assert result.ret == 0 + result.stdout.fnmatch_lines([ + "*test_hello.py::test_hello*PASSED", + "*test_hello.py::test_other*PASSED", + "*test_world.py::test_world*PASSED", + "*test_world.py::test_other*PASSED", + "*4 passed*" + ]) + + # specify tests within a module + result = testdir.runpytest("--pyargs", "-v", "ns_pkg.world.test_world::test_other") + assert result.ret == 0 + result.stdout.fnmatch_lines([ + "*test_world.py::test_other*PASSED", + "*1 passed*" + ]) + def test_cmdline_python_package_not_exists(self, testdir): result = testdir.runpytest("--pyargs", "tpkgwhatv") assert result.ret @@ -697,4 +769,3 @@ class TestDurationWithFixture: * setup *test_1* * call *test_1* """) - From 4d9e293b4d32fe20b7c5a5ef37ca190481bbfaf3 Mon Sep 17 00:00:00 2001 From: taschini Date: Thu, 9 Jun 2016 08:22:11 +0200 Subject: [PATCH 12/25] Incorporated feedback (#1597). Fixed problem caused in a test on Windows by file left open by PyPy and not immediately garbage collected. --- _pytest/main.py | 5 ++++- testing/acceptance_test.py | 28 +++++++++------------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/_pytest/main.py b/_pytest/main.py index 40df13238..a4bb7205c 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -664,9 +664,12 @@ class Session(FSCollector): return x if loader is None: return x + # This method is sometimes invoked when AssertionRewritingHook, which + # does not define a get_filename method, is already in place: try: path = loader.get_filename() - except: + except AttributeError: + # Retrieve path from AssertionRewritingHook: path = loader.modules[x][0].co_filename if loader.is_package(x): path = os.path.dirname(path) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index c66a1740f..1b14d12a5 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import os import sys import _pytest._code @@ -513,22 +514,12 @@ class TestInvocationVariants: path = testdir.mkpydir("tpkg") path.join("test_hello.py").write('raise ImportError') - result = testdir.runpytest("--pyargs", "tpkg.test_hello") + result = testdir.runpytest_subprocess("--pyargs", "tpkg.test_hello") assert result.ret != 0 - # Depending on whether the process running the test is the - # same as the process parsing the command-line arguments, the - # type of failure can be different: - if result.stderr.str() == '': - # Different processes - result.stdout.fnmatch_lines([ - "collected*0*items*/*1*errors" - ]) - else: - # Same process - result.stderr.fnmatch_lines([ - "ERROR:*file*or*package*not*found:*tpkg.test_hello" - ]) + result.stdout.fnmatch_lines([ + "collected*0*items*/*1*errors" + ]) def test_cmdline_python_package(self, testdir, monkeypatch): monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False) @@ -549,7 +540,7 @@ class TestInvocationVariants: def join_pythonpath(what): cur = py.std.os.environ.get('PYTHONPATH') if cur: - return str(what) + ':' + cur + return str(what) + os.pathsep + cur return what empty_package = testdir.mkpydir("empty_package") monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package)) @@ -560,11 +551,10 @@ class TestInvocationVariants: ]) monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir)) - path.join('test_hello.py').remove() - result = testdir.runpytest("--pyargs", "tpkg.test_hello") + result = testdir.runpytest("--pyargs", "tpkg.test_missing") assert result.ret != 0 result.stderr.fnmatch_lines([ - "*not*found*test_hello*", + "*not*found*test_missing*", ]) def test_cmdline_python_namespace_package(self, testdir, monkeypatch): @@ -605,7 +595,7 @@ class TestInvocationVariants: cur = py.std.os.environ.get('PYTHONPATH') if cur: dirs += (cur,) - return ':'.join(str(p) for p in dirs) + return os.pathsep.join(str(p) for p in dirs) monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path)) for p in search_path: monkeypatch.syspath_prepend(p) From 1218392413791345440c3e1d8d0bff0fb880442c Mon Sep 17 00:00:00 2001 From: taschini Date: Tue, 14 Jun 2016 07:16:20 +0200 Subject: [PATCH 13/25] Added taschini to AUTHORS and #1597 to CHANGELOG.rst. --- AUTHORS | 1 + CHANGELOG.rst | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 28cce969d..9962325d2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -87,6 +87,7 @@ Russel Winder Ryan Wooden Samuele Pedroni Simon Gomizelj +Stefano Taschini Thomas Grainger Tom Viner Trevor Bekolay diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1a8dc9433..2ab8834b7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,16 +9,22 @@ * Fix internal error issue when ``method`` argument is missing for ``teardown_method()``. Fixes (`#1605`_). -* - * Fix exception visualization in case the current working directory (CWD) gets deleted during testing. Fixes (`#1235`). Thanks `@bukzor` for reporting. PR by `@marscher`. Thanks `@nicoddemus` for his help. -.. _#1580: https://github.com/pytest-dev/pytest/issues/1580 +* Ensure that a module within a namespace package can be found when it + is specified on the command line together with the ``--pyargs`` + option. Thanks to `@taschini`_ for the PR (`#1597`_). + +* + +.. _#1580: https://github.com/pytest-dev/pytest/pull/1580 .. _#1605: https://github.com/pytest-dev/pytest/issues/1605 +.. _#1597: https://github.com/pytest-dev/pytest/pull/1597 .. _@graingert: https://github.com/graingert +.. _@taschini: https://github.com/taschini 2.9.2 From 2a3cbdf4d15b236913295233154a8945b247cdcc Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 15 Jun 2016 18:26:10 -0700 Subject: [PATCH 14/25] Cut a dead test helper function This appears to have been unused since commit 320835d "split out pytest-xdist related reporting to the plugin" in July 2010. It's the only caller outside of _pytest/runner.py of the `call_and_report` helper function there, so cutting it out makes that more of a pure helper function and makes it slightly easier to understand the code in _pytest/runner.py . --- testing/test_terminal.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index b43d6b379..be5749c96 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -8,16 +8,11 @@ import _pytest._pluggy as pluggy import _pytest._code import py import pytest -from _pytest import runner from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt from _pytest.terminal import build_summary_stats_line, _plugin_nameversions -def basic_run_report(item): - runner.call_and_report(item, "setup", log=False) - return runner.call_and_report(item, "call", log=False) - DistInfo = collections.namedtuple('DistInfo', ['project_name', 'version']) From ab8b2e75a30d34799311bb2b0f85881842c410b9 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 15 Jun 2016 18:10:08 -0700 Subject: [PATCH 15/25] Simplify default pytest_runtestloop The inner function and the explanatory comment it makes necessary can all be removed if we switch to an if/else rather than try/except for this condition. Perhaps this bit comes from my fondness for C, but I think I would find this style clearer and easier to understand even if it weren't for the Python 2 quirk that makes the other style require us to add an unnecessary-looking function abstraction. In any case, given that the alternative does require that abstraction this is definitely simpler. --- AUTHORS | 1 + _pytest/main.py | 11 +---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 28cce969d..f47961bfe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -47,6 +47,7 @@ Floris Bruynooghe Gabriel Reis Georgy Dyuldin Graham Horler +Greg Price Grig Gheorghiu Guido Wesdorp Harald Armin Massa diff --git a/_pytest/main.py b/_pytest/main.py index 4a6c08775..d8d6dc12e 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -136,17 +136,8 @@ def pytest_runtestloop(session): if session.config.option.collectonly: return True - def getnextitem(i): - # this is a function to avoid python2 - # keeping sys.exc_info set when calling into a test - # python2 keeps sys.exc_info till the frame is left - try: - return session.items[i+1] - except IndexError: - return None - for i, item in enumerate(session.items): - nextitem = getnextitem(i) + nextitem = session.items[i+1] if i+1 < len(session.items) else None item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) if session.shouldstop: raise session.Interrupted(session.shouldstop) From e9d798914091faa9b85ffc17b9bc756a08074d38 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 20 Jun 2016 22:30:36 +0200 Subject: [PATCH 16/25] Ignore hidden files (.testmondata) in test_pytest_collect_file `test_pytest_collect_file` fails if you run the tests using `--testmon`, because pytest-testmon will put its DB there as `.testmondata`. --- testing/test_collection.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 749c5b7ce..cd0a5cc19 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -150,7 +150,9 @@ class TestCollectPluginHookRelay: wascalled = [] class Plugin: def pytest_collect_file(self, path, parent): - wascalled.append(path) + if not path.basename.startswith("."): + # Ignore hidden files, e.g. .testmondata. + wascalled.append(path) testdir.makefile(".abc", "xyz") pytest.main([testdir.tmpdir], plugins=[Plugin()]) assert len(wascalled) == 1 From e53e45b55c71f92967af9c55c800d7818b5ac073 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 21 Jun 2016 08:36:02 +0200 Subject: [PATCH 17/25] tests: getexecutable: call `--version` on all Pythons This should prevent errors from pyenv: pyenv: python2.6: command not found The `python2.6' command exists in these Python versions: 2.6.9 While the pyenv wrapper explicitly returns 127, I think it is better to just check for non-zero?! --- _pytest/pytester.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/_pytest/pytester.py b/_pytest/pytester.py index faed7f581..697f5218f 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -123,15 +123,18 @@ def getexecutable(name, cache={}): except KeyError: executable = py.path.local.sysfind(name) if executable: + import subprocess + popen = subprocess.Popen([str(executable), "--version"], + universal_newlines=True, stderr=subprocess.PIPE) + out, err = popen.communicate() if name == "jython": - import subprocess - popen = subprocess.Popen([str(executable), "--version"], - universal_newlines=True, stderr=subprocess.PIPE) - out, err = popen.communicate() if not err or "2.5" not in err: executable = None if "2.5.2" in err: executable = None # http://bugs.jython.org/issue1790 + elif popen.returncode != 0: + # Handle pyenv's 127. + executable = None cache[name] = executable return executable From 5d8d1db4df253cdbeefdf1aec269d1df4df257c2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 21 Jun 2016 09:40:22 +0200 Subject: [PATCH 18/25] Update repository transfer instructions --- CONTRIBUTING.rst | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5a00c191b..7733b5f5a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -127,27 +127,18 @@ transferred to the ``pytest-dev`` organisation. Here's a rundown of how a repository transfer usually proceeds (using a repository named ``joedoe/pytest-xyz`` as example): -* One of the ``pytest-dev`` administrators creates: - - - ``pytest-xyz-admin`` team, with full administration rights to - ``pytest-dev/pytest-xyz``. - - ``pytest-xyz-developers`` team, with write access to - ``pytest-dev/pytest-xyz``. - -* ``joedoe`` is invited to the ``pytest-xyz-admin`` team; - -* After accepting the invitation, ``joedoe`` transfers the repository from its - original location to ``pytest-dev/pytest-xyz`` (A nice feature is that GitHub handles URL redirection from - the old to the new location automatically). - -* ``joedoe`` is free to add any other collaborators to the - ``pytest-xyz-admin`` or ``pytest-xyz-developers`` team as desired. +* ``joedoe`` transfers repository ownership to ``pytest-dev`` administrator ``calvin``. +* ``calvin`` creates ``pytest-xyz-admin`` and ``pytest-xyz-developers`` teams, inviting ``joedoe`` to both as **maintainer**. +* ``calvin`` transfers repository to ``pytest-dev`` and configures team access: + + - ``pytest-xyz-admin`` **admin** access; + - ``pytest-xyz-developers`` **write** access; The ``pytest-dev/Contributors`` team has write access to all projects, and every project administrator is in it. We recommend that each plugin has at least three people who have the right to release to PyPI. -Repository owners can be assured that no ``pytest-dev`` administrator will ever make +Repository owners can rest assured that no ``pytest-dev`` administrator will ever make releases of your repository or take ownership in any way, except in rare cases where someone becomes unresponsive after months of contact attempts. As stated, the objective is to share maintenance and avoid "plugin-abandon". From 5854a71ecee28736b10c31c14c93394dfc8805ca Mon Sep 17 00:00:00 2001 From: Tom Viner Date: Mon, 20 Jun 2016 17:20:24 +0200 Subject: [PATCH 19/25] Issue 460: getfuncargvalue fixture w. params err --- AUTHORS | 1 + CHANGELOG.rst | 11 +++- _pytest/python.py | 19 +++++++ testing/python/fixture.py | 105 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 12ac94b72..3e079b522 100644 --- a/AUTHORS +++ b/AUTHORS @@ -89,6 +89,7 @@ Ryan Wooden Samuele Pedroni Simon Gomizelj Stefano Taschini +Stefan Farmbauer Thomas Grainger Tom Viner Trevor Bekolay diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2ab8834b7..0a71de6b9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,7 @@ * Text documents without any doctests no longer appear as "skipped". Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_). -* Fix internal error issue when ``method`` argument is missing for +* Fix internal error issue when ``method`` argument is missing for ``teardown_method()``. Fixes (`#1605`_). * Fix exception visualization in case the current working directory (CWD) gets @@ -17,14 +17,21 @@ is specified on the command line together with the ``--pyargs`` option. Thanks to `@taschini`_ for the PR (`#1597`_). -* +* Raise helpful failure message, when requesting parametrized fixture at runtime, + e.g. with ``request.getfuncargvalue``. BACKWARD INCOMPAT: Previously these params + were simply never defined. So a fixture decorated like ``@pytest.fixture(params=[0, 1, 2])`` + only ran once. Now a failure is raised. Fixes (`#460`_). Thanks to + `@nikratio`_ for bug report, `@RedBeardCode`_ and `@tomviner`_ for PR. .. _#1580: https://github.com/pytest-dev/pytest/pull/1580 .. _#1605: https://github.com/pytest-dev/pytest/issues/1605 .. _#1597: https://github.com/pytest-dev/pytest/pull/1597 +.. _#460: https://github.com/pytest-dev/pytest/pull/460 .. _@graingert: https://github.com/graingert .. _@taschini: https://github.com/taschini +.. _@nikratio: https://github.com/nikratio +.. _@RedBeardCode: https://github.com/RedBeardCode 2.9.2 diff --git a/_pytest/python.py b/_pytest/python.py index 21d78aea3..7246c253c 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1651,6 +1651,25 @@ class FixtureRequest(FuncargnamesCompatAttr): except (AttributeError, ValueError): param = NOTSET param_index = 0 + if fixturedef.params is not None: + frame = inspect.stack()[3] + frameinfo = inspect.getframeinfo(frame[0]) + source_path = frameinfo.filename + source_lineno = frameinfo.lineno + source_path = py.path.local(source_path) + if source_path.relto(funcitem.config.rootdir): + source_path = source_path.relto(funcitem.config.rootdir) + msg = ( + "The requested fixture has no parameter defined for the " + "current test.\n\nRequested fixture '{0}' defined in:\n{1}" + "\n\nRequested here:\n{2}:{3}".format( + fixturedef.argname, + getlocation(fixturedef.func, funcitem.config.rootdir), + source_path, + source_lineno, + ) + ) + pytest.fail(msg) else: # indices might not be set if old-style metafunc.addcall() was used param_index = funcitem.callspec.indices.get(argname, 0) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 25e312098..d6e3250d0 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -2723,3 +2723,108 @@ class TestContextManagerFixtureFuncs: *def arg1* """) +class TestParameterizedSubRequest: + def test_call_from_fixture(self, testdir): + testfile = testdir.makepyfile(""" + import pytest + + @pytest.fixture(params=[0, 1, 2]) + def fix_with_param(request): + return request.param + + @pytest.fixture + def get_named_fixture(request): + return request.getfuncargvalue('fix_with_param') + + def test_foo(request, get_named_fixture): + pass + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + E*Failed: The requested fixture has no parameter defined for the current test. + E* + E*Requested fixture 'fix_with_param' defined in: + E*{0}:4 + E*Requested here: + E*{1}:9 + *1 error* + """.format(testfile.basename, testfile.basename)) + + def test_call_from_test(self, testdir): + testfile = testdir.makepyfile(""" + import pytest + + @pytest.fixture(params=[0, 1, 2]) + def fix_with_param(request): + return request.param + + def test_foo(request): + request.getfuncargvalue('fix_with_param') + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + E*Failed: The requested fixture has no parameter defined for the current test. + E* + E*Requested fixture 'fix_with_param' defined in: + E*{0}:4 + E*Requested here: + E*{1}:8 + *1 failed* + """.format(testfile.basename, testfile.basename)) + + def test_external_fixture(self, testdir): + conffile = testdir.makeconftest(""" + import pytest + + @pytest.fixture(params=[0, 1, 2]) + def fix_with_param(request): + return request.param + """) + + testfile = testdir.makepyfile(""" + def test_foo(request): + request.getfuncargvalue('fix_with_param') + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + E*Failed: The requested fixture has no parameter defined for the current test. + E* + E*Requested fixture 'fix_with_param' defined in: + E*{0}:4 + E*Requested here: + E*{1}:2 + *1 failed* + """.format(conffile.basename, testfile.basename)) + + def test_non_relative_path(self, testdir): + tests_dir = testdir.mkdir('tests') + fixdir = testdir.mkdir('fixtures') + fixfile = fixdir.join("fix.py") + fixfile.write(_pytest._code.Source(""" + import pytest + + @pytest.fixture(params=[0, 1, 2]) + def fix_with_param(request): + return request.param + """)) + + testfile = tests_dir.join("test_foos.py") + testfile.write(_pytest._code.Source(""" + from fix import fix_with_param + + def test_foo(request): + request.getfuncargvalue('fix_with_param') + """)) + + tests_dir.chdir() + testdir.syspathinsert(fixdir) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + E*Failed: The requested fixture has no parameter defined for the current test. + E* + E*Requested fixture 'fix_with_param' defined in: + E*{0}:5 + E*Requested here: + E*{1}:5 + *1 failed* + """.format(fixfile.strpath, testfile.basename)) From 2b5c2f3ed58e4c3bf9185318a003cbc39dde16e6 Mon Sep 17 00:00:00 2001 From: Oliver Bestwalter Date: Tue, 21 Jun 2016 14:57:48 +0200 Subject: [PATCH 20/25] help the user in the rare case this assertion actually fails --- _pytest/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/python.py b/_pytest/python.py index 21d78aea3..ddfb90b93 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1987,7 +1987,7 @@ class FixtureManager: # fixture attribute continue else: - assert not name.startswith(self._argprefix) + assert not name.startswith(self._argprefix), name fixturedef = FixtureDef(self, nodeid, name, obj, marker.scope, marker.params, yieldctx=marker.yieldctx, From 09389d2b20cc7c664e9bd68be61d9181866e2652 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 22 Jun 2016 09:13:01 +0200 Subject: [PATCH 21/25] CONTRIBUTING.rst: spelling fixes [ci skip] --- CONTRIBUTING.rst | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5a00c191b..3daa4f334 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -48,7 +48,7 @@ to fix the bug yet. Fix bugs -------- -Look through the GitHub issues for bugs. Here is sample filter you can use: +Look through the GitHub issues for bugs. Here is a filter you can use: https://github.com/pytest-dev/pytest/labels/bug :ref:`Talk ` to developers to find out how you can fix specific bugs. @@ -60,8 +60,7 @@ Don't forget to check the issue trackers of your favourite plugins, too! Implement features ------------------ -Look through the GitHub issues for enhancements. Here is sample filter you -can use: +Look through the GitHub issues for enhancements. Here is a filter you can use: https://github.com/pytest-dev/pytest/labels/enhancement :ref:`Talk ` to developers to find out how you can implement specific @@ -70,16 +69,15 @@ features. Write documentation ------------------- -pytest could always use more documentation. What exactly is needed? +Pytest could always use more documentation. What exactly is needed? * More complementary documentation. Have you perhaps found something unclear? * Documentation translations. We currently have only English. * Docstrings. There can never be too many of them. * Blog posts, articles and such -- they're all very appreciated. -You can also edit documentation files directly in the Github web interface -without needing to make a fork and local copy. This can be convenient for -small fixes. +You can also edit documentation files directly in the GitHub web interface, +without using a local copy. This can be convenient for small fixes. .. _submitplugin: @@ -95,13 +93,14 @@ in repositories living under the ``pytest-dev`` organisations: - `pytest-dev on Bitbucket `_ All pytest-dev Contributors team members have write access to all contained -repositories. pytest core and plugins are generally developed +repositories. Pytest core and plugins are generally developed using `pull requests`_ to respective repositories. The objectives of the ``pytest-dev`` organisation are: * Having a central location for popular pytest plugins -* Sharing some of the maintenance responsibility (in case a maintainer no longer whishes to maintain a plugin) +* Sharing some of the maintenance responsibility (in case a maintainer no + longer wishes to maintain a plugin) You can submit your plugin by subscribing to the `pytest-dev mail list `_ and writing a @@ -159,15 +158,11 @@ As stated, the objective is to share maintenance and avoid "plugin-abandon". Preparing Pull Requests on GitHub --------------------------------- -There's an excellent tutorial on how Pull Requests work in the -`GitHub Help Center `_ - - .. note:: What is a "pull request"? It informs project's core developers about the changes you want to review and merge. Pull requests are stored on `GitHub servers `_. - Once you send pull request, we can discuss it's potential modifications and + Once you send a pull request, we can discuss its potential modifications and even add more commits to it later on. There's an excellent tutorial on how Pull Requests work in the @@ -216,19 +211,19 @@ but here is a simple overview: This command will run tests via the "tox" tool against Python 2.7 and 3.5 and also perform "lint" coding-style checks. ``runtox.py`` is a thin wrapper around ``tox`` which installs from a development package - index where newer (not yet released to pypi) versions of dependencies + index where newer (not yet released to PyPI) versions of dependencies (especially ``py``) might be present. #. You can now edit your local working copy. You can now make the changes you want and run the tests again as necessary. - To run tests on py27 and pass options to pytest (e.g. enter pdb on failure) - to pytest you can do:: + To run tests on Python 2.7 and pass options to pytest (e.g. enter pdb on + failure) to pytest you can do:: $ python3 runtox.py -e py27 -- --pdb - or to only run tests in a particular test module on py35:: + Or to only run tests in a particular test module on Python 3.5:: $ python3 runtox.py -e py35 -- testing/test_config.py @@ -237,9 +232,9 @@ but here is a simple overview: $ git commit -a -m "" $ git push -u - Make sure you add a CHANGELOG message, and add yourself to AUTHORS. If you - are unsure about either of these steps, submit your pull request and we'll - help you fix it up. + Make sure you add a message to ``CHANGELOG.rst`` and add yourself to + ``AUTHORS``. If you are unsure about either of these steps, submit your + pull request and we'll help you fix it up. #. Finally, submit a pull request through the GitHub website using this data:: @@ -248,6 +243,6 @@ but here is a simple overview: base-fork: pytest-dev/pytest base: master # if it's a bugfix - base: feature # if it's a feature + base: features # if it's a feature From 98adf204b2fbdbeb696b47c4bf6abecad80da1d0 Mon Sep 17 00:00:00 2001 From: Tom Viner Date: Tue, 21 Jun 2016 16:48:29 +0200 Subject: [PATCH 22/25] issue 1553: Include terminal newlines in diffs --- CHANGELOG.rst | 8 ++++++++ _pytest/assertion/util.py | 5 +++-- testing/test_assertion.py | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0a71de6b9..cb6bd625a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -23,15 +23,23 @@ only ran once. Now a failure is raised. Fixes (`#460`_). Thanks to `@nikratio`_ for bug report, `@RedBeardCode`_ and `@tomviner`_ for PR. +* Create correct diff for strings ending with newlines. Fixes (`#1553`_). + Thanks `@Vogtinator`_ for reporting. Thanks to `@RedBeardCode`_ and + `@tomviner`_ for PR. + +* + .. _#1580: https://github.com/pytest-dev/pytest/pull/1580 .. _#1605: https://github.com/pytest-dev/pytest/issues/1605 .. _#1597: https://github.com/pytest-dev/pytest/pull/1597 .. _#460: https://github.com/pytest-dev/pytest/pull/460 +.. _#1553: https://github.com/pytest-dev/pytest/issues/1553 .. _@graingert: https://github.com/graingert .. _@taschini: https://github.com/taschini .. _@nikratio: https://github.com/nikratio .. _@RedBeardCode: https://github.com/RedBeardCode +.. _@Vogtinator: https://github.com/Vogtinator 2.9.2 diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index f2f23efea..8bf425caf 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -225,9 +225,10 @@ def _diff_text(left, right, verbose=False): 'characters in diff, use -v to show') % i] left = left[:-i] right = right[:-i] + keepends = True explanation += [line.strip('\n') - for line in ndiff(left.splitlines(), - right.splitlines())] + for line in ndiff(left.splitlines(keepends), + right.splitlines(keepends))] return explanation diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 347278e19..dfa1b9420 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -428,7 +428,7 @@ def test_assert_compare_truncate_longmessage(monkeypatch, testdir): "*- 3", "*- 5", "*- 7", - "*truncated (191 more lines)*use*-vv*", + "*truncated (193 more lines)*use*-vv*", ]) @@ -626,3 +626,17 @@ def test_set_with_unsortable_elements(): + repr(3) """).strip() assert '\n'.join(expl) == dedent + +def test_diff_newline_at_end(monkeypatch, testdir): + testdir.makepyfile(r""" + def test_diff(): + assert 'asdf' == 'asdf\n' + """) + + result = testdir.runpytest() + result.stdout.fnmatch_lines(r""" + *assert 'asdf' == 'asdf\n' + * - asdf + * + asdf + * ? + + """) From ec25d398a5ad238b677f6d2c139a1a0a3d32456c Mon Sep 17 00:00:00 2001 From: Tom Viner Date: Wed, 22 Jun 2016 10:06:14 +0200 Subject: [PATCH 23/25] fix changelog links --- CHANGELOG.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0a71de6b9..9f5450d46 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,8 +10,8 @@ ``teardown_method()``. Fixes (`#1605`_). * Fix exception visualization in case the current working directory (CWD) gets - deleted during testing. Fixes (`#1235`). Thanks `@bukzor` for reporting. PR by - `@marscher`. Thanks `@nicoddemus` for his help. + deleted during testing. Fixes (`#1235`). Thanks `@bukzor`_ for reporting. PR by + `@marscher`. Thanks `@nicoddemus`_ for his help. * Ensure that a module within a namespace package can be found when it is specified on the command line together with the ``--pyargs`` @@ -192,7 +192,7 @@ Thanks `@biern`_ for the PR. * Fix `traceback style docs`_ to describe all of the available options - (auto/long/short/line/native/no), with `auto` being the default since v2.6. + (auto/long/short/line/native/no), with ``auto`` being the default since v2.6. Thanks `@hackebrot`_ for the PR. * Fix (`#1422`_): junit record_xml_property doesn't allow multiple records From 7d87a1b127249d6051066a5ff0197a10c5505224 Mon Sep 17 00:00:00 2001 From: Oliver Bestwalter Date: Tue, 21 Jun 2016 20:10:22 +0200 Subject: [PATCH 24/25] Add test for failing assertion Should contain function name that caused the failure (see pull request #1631). --- testing/python/fixture.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index d6e3250d0..85de0c318 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -490,6 +490,20 @@ class TestRequestBasic: print(ss.stack) assert teardownlist == [1] + def test_mark_as_fixture_with_prefix_and_decorator_fails(self, testdir): + testdir.makeconftest(""" + import pytest + + @pytest.fixture + def pytest_funcarg__marked_with_prefix_and_decorator(): + pass + """) + result = testdir.runpytest_subprocess() + assert result.ret != 0 + result.stdout.fnmatch_lines([ + "*AssertionError:*pytest_funcarg__marked_with_prefix_and_decorator*" + ]) + def test_request_addfinalizer_failing_setup(self, testdir): testdir.makepyfile(""" import pytest From be1dabd6a9ac65a0badb93edaedb1da4ef700c4d Mon Sep 17 00:00:00 2001 From: HEAD KANGAROO Date: Wed, 22 Jun 2016 13:43:11 +0100 Subject: [PATCH 25/25] cache.rst: Fix wrong command used The "Inspecting Cache content" section was showing --cache-clear command, but should actually be using --cache-show command. Also; update AUTHORS --- AUTHORS | 1 + doc/en/cache.rst | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 3e079b522..c35e66587 100644 --- a/AUTHORS +++ b/AUTHORS @@ -22,6 +22,7 @@ Bruno Oliveira Cal Leeming Carl Friedrich Bolz Charles Cloud +Charnjit SiNGH (CCSJ) Chris Lamb Christian Theunert Christian Tismer diff --git a/doc/en/cache.rst b/doc/en/cache.rst index de03204de..860b13753 100644 --- a/doc/en/cache.rst +++ b/doc/en/cache.rst @@ -222,9 +222,9 @@ Inspecting Cache content ------------------------------- You can always peek at the content of the cache using the -``--cache-clear`` command line option:: +``--cache-show`` command line option:: - $ py.test --cache-clear + $ py.test --cache-show ======= test session starts ======== platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: