From c61ff31ffa183b531d51787e231f197b039e2780 Mon Sep 17 00:00:00 2001 From: iwanb Date: Sun, 23 Sep 2018 13:05:55 +0200 Subject: [PATCH 01/27] Fix #3539: reload module with assertion rewrite import hook --- AUTHORS | 1 + changelog/3539.bugfix.rst | 1 + src/_pytest/assertion/rewrite.py | 20 +++++++-------- testing/test_assertrewrite.py | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 changelog/3539.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 2d59a1b0f..a65623820 100644 --- a/AUTHORS +++ b/AUTHORS @@ -93,6 +93,7 @@ Hui Wang (coldnight) Ian Bicking Ian Lesperance Ionuț Turturică +Iwan Briquemont Jaap Broekhuizen Jan Balster Janne Vanhala diff --git a/changelog/3539.bugfix.rst b/changelog/3539.bugfix.rst new file mode 100644 index 000000000..d0741cda9 --- /dev/null +++ b/changelog/3539.bugfix.rst @@ -0,0 +1 @@ +Fix reload on assertion rewritten modules. diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index be8c6dc4d..7a11c4ec1 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -269,17 +269,17 @@ class AssertionRewritingHook(object): ) def load_module(self, name): - # If there is an existing module object named 'fullname' in - # sys.modules, the loader must use that existing module. (Otherwise, - # the reload() builtin will not work correctly.) - if name in sys.modules: - return sys.modules[name] - co, pyc = self.modules.pop(name) - # I wish I could just call imp.load_compiled here, but __file__ has to - # be set properly. In Python 3.2+, this all would be handled correctly - # by load_compiled. - mod = sys.modules[name] = imp.new_module(name) + if name in sys.modules: + # If there is an existing module object named 'fullname' in + # sys.modules, the loader must use that existing module. (Otherwise, + # the reload() builtin will not work correctly.) + mod = sys.modules[name] + else: + # I wish I could just call imp.load_compiled here, but __file__ has to + # be set properly. In Python 3.2+, this all would be handled correctly + # by load_compiled. + mod = sys.modules[name] = imp.new_module(name) try: mod.__file__ = co.co_filename # Normally, this attribute is 3.2+. diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index a2cd8e81c..5153fc741 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1050,6 +1050,48 @@ class TestAssertionRewriteHookDetails(object): result = testdir.runpytest("-s") result.stdout.fnmatch_lines(["* 1 passed*"]) + def test_reload_reloads(self, testdir): + """Reloading a module after change picks up the change.""" + testdir.tmpdir.join("file.py").write( + textwrap.dedent( + """ + def reloaded(): + return False + + def rewrite_self(): + with open(__file__, 'w') as self: + self.write('def reloaded(): return True') + """ + ) + ) + testdir.tmpdir.join("pytest.ini").write( + textwrap.dedent( + """ + [pytest] + python_files = *.py + """ + ) + ) + + testdir.makepyfile( + test_fun=""" + import sys + try: + from imp import reload + except ImportError: + pass + + def test_loader(): + import file + assert not file.reloaded() + file.rewrite_self() + reload(file) + assert file.reloaded() + """ + ) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(["* 1 passed*"]) + def test_get_data_support(self, testdir): """Implement optional PEP302 api (#808). """ From 4474beeb8294a8f8759e967484b7a932ca4c22fb Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 23 Sep 2018 18:07:28 -0700 Subject: [PATCH 02/27] Typo fix [ci skip] --- doc/en/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 4da786101..e5521bba5 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -255,7 +255,7 @@ Pytest supports the use of ``breakpoint()`` with the following behaviours: - When ``breakpoint()`` is called and ``PYTHONBREAKPOINT`` is set to the default value, pytest will use the custom internal PDB trace UI instead of the system default ``Pdb``. - When tests are complete, the system will default back to the system ``Pdb`` trace UI. - - If ``--pdb`` is called on execution of pytest, the custom internal Pdb trace UI is used on ``bothbreakpoint()`` and failed tests/unhandled exceptions. + - If ``--pdb`` is called on execution of pytest, the custom internal Pdb trace UI is used on both ``breakpoint()`` and failed tests/unhandled exceptions. - If ``--pdbcls`` is used, the custom class debugger will be executed when a test fails (as expected within existing behaviour), but also when ``breakpoint()`` is called from within a test, the custom class debugger will be instantiated. .. _durations: From 9899b8f1fbb415c709e9238d5295951a49c3462a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 23 Sep 2018 22:42:09 -0300 Subject: [PATCH 03/27] Add an example on how to update config.warn calls As commented in https://github.com/pytest-dev/pytest-cov/pull/230#pullrequestreview-157958838 --- doc/en/deprecations.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index ee6c99193..a9d153206 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -22,14 +22,24 @@ Below is a complete list of all pytest features which are considered deprecated. Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning system for its own warnings, so those two functions are now deprecated. -``Config.warn`` should be replaced by calls to the standard ``warnings.warn``. +``Config.warn`` should be replaced by calls to the standard ``warnings.warn``, example: + +.. code-block:: python + + config.warn("C1", "some warning") + +Becomes: + +.. code-block:: python + + warnings.warn(pytest.PytestWarning("some warning")) ``Node.warn`` now supports two signatures: -* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. +* ``node.warn(PytestWarning("some message"))``: is now the **recommended** way to call this function. The warning instance must be a PytestWarning or subclass. -* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to the warning instance form above. +* ``node.warn("CI", "some message")``: this code/message form is now **deprecated** and should be converted to the warning instance form above. ``pytest_namespace`` From 2b762337bdf6d1f6152d33108600a29cef07d3a4 Mon Sep 17 00:00:00 2001 From: Harry Percival Date: Tue, 25 Sep 2018 06:49:50 +0100 Subject: [PATCH 04/27] add documentation of register_assert_rewrite wip --- doc/en/assert.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index e0e9b9305..76cd66e41 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -264,8 +264,12 @@ Advanced assertion introspection Reporting details about a failing assertion is achieved by rewriting assert statements before they are run. Rewritten assert statements put introspection information into the assertion failure message. ``pytest`` only rewrites test -modules directly discovered by its test collection process, so asserts in -supporting modules which are not themselves test modules will not be rewritten. +modules directly discovered by its test collection process, so **asserts in +supporting modules which are not themselves test modules will not be rewritten**. + +You can manually enable assertion rewriting for an imported module by calling +`register_assert_rewrite` +before you import it. .. note:: From 97748b66057dd9ec34b28abe557a453fdfbfb685 Mon Sep 17 00:00:00 2001 From: Harry Percival Date: Tue, 25 Sep 2018 06:55:28 +0100 Subject: [PATCH 05/27] mention conftest.py as a good place to do it. --- doc/en/assert.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index 76cd66e41..1fc5917d6 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -269,7 +269,7 @@ supporting modules which are not themselves test modules will not be rewritten** You can manually enable assertion rewriting for an imported module by calling `register_assert_rewrite` -before you import it. +before you import it (a good place to do that is in ``conftest.py``). .. note:: From 11442f2ad7a9619645322704213f225b335e4114 Mon Sep 17 00:00:00 2001 From: Harry Percival Date: Tue, 25 Sep 2018 06:57:33 +0100 Subject: [PATCH 06/27] fix rst syntax thing --- doc/en/assert.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index 1fc5917d6..ff11be21a 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -268,7 +268,7 @@ modules directly discovered by its test collection process, so **asserts in supporting modules which are not themselves test modules will not be rewritten**. You can manually enable assertion rewriting for an imported module by calling -`register_assert_rewrite` +`register_assert_rewrite`_ before you import it (a good place to do that is in ``conftest.py``). .. note:: From 19ec300b2a05ccce0346417f1252040306ed7451 Mon Sep 17 00:00:00 2001 From: Harry Percival Date: Tue, 25 Sep 2018 06:58:47 +0100 Subject: [PATCH 07/27] fix rst syntax again --- doc/en/assert.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index ff11be21a..60edbf2a3 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -268,7 +268,7 @@ modules directly discovered by its test collection process, so **asserts in supporting modules which are not themselves test modules will not be rewritten**. You can manually enable assertion rewriting for an imported module by calling -`register_assert_rewrite`_ +`register_assert_rewrite `_ before you import it (a good place to do that is in ``conftest.py``). .. note:: From b03bad5dbbddd851d0a164da56efe4d870a085bb Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 25 Sep 2018 08:12:55 -0300 Subject: [PATCH 08/27] Fix linting --- doc/en/assert.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index 60edbf2a3..d3deeb503 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -267,7 +267,7 @@ information into the assertion failure message. ``pytest`` only rewrites test modules directly discovered by its test collection process, so **asserts in supporting modules which are not themselves test modules will not be rewritten**. -You can manually enable assertion rewriting for an imported module by calling +You can manually enable assertion rewriting for an imported module by calling `register_assert_rewrite `_ before you import it (a good place to do that is in ``conftest.py``). From 1636522563b115de385674b08f66d89cf6b29e92 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 25 Sep 2018 17:38:22 -0300 Subject: [PATCH 09/27] Document that item parameter of pytest_warning_captured hook is deprecated Our policy is to not deprecate features during bugfix releases, but in this case I believe it makes sense as we are only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get the word out that hook implementers should not use this parameter at all. Fix #4036 --- changelog/4036.removal.rst | 9 +++++++++ src/_pytest/hookspec.py | 3 +++ 2 files changed, 12 insertions(+) create mode 100644 changelog/4036.removal.rst diff --git a/changelog/4036.removal.rst b/changelog/4036.removal.rst new file mode 100644 index 000000000..a124028c4 --- /dev/null +++ b/changelog/4036.removal.rst @@ -0,0 +1,9 @@ +The ``item`` parameter of ``pytest_warning_captured`` hook is now documented as deprecated. We realized only after +the ``3.8`` release that this parameter is incompatible with ``pytest-xdist``. + +Our policy is to not deprecate features during bugfix releases, but in this case we believe it makes sense as we are +only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get +the word out that hook implementers should not use this parameter at all. + +In a future release ``item`` will always be ``None`` and will emit a proper warning when a hook implementation +makes use of it. diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 1a9326149..533806964 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -562,6 +562,9 @@ def pytest_warning_captured(warning_message, when, item): * ``"runtest"``: during test execution. :param pytest.Item|None item: + **DEPRECATED**: This parameter is incompatible with ``pytest-xdist``, and will always receive ``None`` + in a future release. + The item being executed if ``when`` is ``"runtest"``, otherwise ``None``. """ From f9ab81a4931f71740aecfd14e97b3187c7a1c37c Mon Sep 17 00:00:00 2001 From: Denis Otkidach Date: Wed, 26 Sep 2018 11:55:39 +0300 Subject: [PATCH 10/27] Exclude empty reports for passed tests --- src/_pytest/terminal.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 49a9a33fb..125eb97c4 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -745,9 +745,10 @@ class TerminalReporter(object): return self.write_sep("=", "PASSES") for rep in reports: - msg = self._getfailureheadline(rep) - self.write_sep("_", msg) - self._outrep_summary(rep) + if rep.sections: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg) + self._outrep_summary(rep) def print_teardown_sections(self, rep): showcapture = self.config.option.showcapture From 6be2136f20c076b98b63233a701493c79acd7c6c Mon Sep 17 00:00:00 2001 From: Zac-HD Date: Wed, 26 Sep 2018 22:02:14 +1000 Subject: [PATCH 11/27] Ensure user_properties is a list --- AUTHORS | 5 +++-- changelog/4034.bugfix.rst | 3 +++ src/_pytest/reports.py | 7 ++++++- 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 changelog/4034.bugfix.rst diff --git a/AUTHORS b/AUTHORS index a65623820..2be74441a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ Ahn Ki-Wook Alan Velasco Alexander Johnson Alexei Kozlenok +Allan Feldman Anatoly Bubenkoff Anders Hovmöller Andras Tim @@ -179,6 +180,7 @@ Raphael Pierzina Raquel Alegre Ravi Chandra Roberto Polli +Roland Puntaier Romain Dorgueil Roman Bolshakov Ronny Pfannschmidt @@ -223,6 +225,5 @@ Wim Glenn Wouter van Ackooy Xuan Luong Xuecong Liao +Zac Hatfield-Dodds Zoltán Máté -Roland Puntaier -Allan Feldman diff --git a/changelog/4034.bugfix.rst b/changelog/4034.bugfix.rst new file mode 100644 index 000000000..44044fb29 --- /dev/null +++ b/changelog/4034.bugfix.rst @@ -0,0 +1,3 @@ +The ``.user_properties`` attribute of ``TestReport`` objects is a list +of (name, value) tuples, but could sometimes be instantiated as a tuple +of tuples. It is now always a list. diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 33b906702..4d4ab2f17 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -110,7 +110,7 @@ class TestReport(BaseReport): when, sections=(), duration=0, - user_properties=(), + user_properties=None, **extra ): #: normalized collection node id @@ -136,6 +136,11 @@ class TestReport(BaseReport): #: user properties is a list of tuples (name, value) that holds user #: defined properties of the test + if user_properties is None: + user_properties = [] + elif not isinstance(user_properties, list): + # This used to be a tuple; so cast for backwards-compatibility + user_properties = list(user_properties) self.user_properties = user_properties #: list of pairs ``(str, str)`` of extra information which needs to From a089a9577ea850f0bcf822cf2c666490a62200d0 Mon Sep 17 00:00:00 2001 From: Zac-HD Date: Wed, 26 Sep 2018 22:40:11 +1000 Subject: [PATCH 12/27] Succinct definition of user_properties --- src/_pytest/reports.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 4d4ab2f17..a57c9fa80 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -136,12 +136,7 @@ class TestReport(BaseReport): #: user properties is a list of tuples (name, value) that holds user #: defined properties of the test - if user_properties is None: - user_properties = [] - elif not isinstance(user_properties, list): - # This used to be a tuple; so cast for backwards-compatibility - user_properties = list(user_properties) - self.user_properties = user_properties + self.user_properties = list(user_properties or []) #: list of pairs ``(str, str)`` of extra information which needs to #: marshallable. Used by pytest to add captured text From 808df48ee832c55840a58d81de8a52d7e1eb7ece Mon Sep 17 00:00:00 2001 From: Denis Otkidach Date: Wed, 26 Sep 2018 16:44:00 +0300 Subject: [PATCH 13/27] Test for excluding empty reports for passed tests --- testing/test_terminal.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index cca704c4c..7651f3ab3 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -681,14 +681,22 @@ def test_pass_reporting_on_fail(testdir): def test_pass_output_reporting(testdir): testdir.makepyfile( """ - def test_pass_output(): + def test_pass_has_output(): print("Four score and seven years ago...") + def test_pass_no_output(): + pass """ ) result = testdir.runpytest() - assert "Four score and seven years ago..." not in result.stdout.str() + s = result.stdout.str() + assert "test_pass_has_output" not in s + assert "Four score and seven years ago..." not in s + assert "test_pass_no_output" not in s result = testdir.runpytest("-rP") - result.stdout.fnmatch_lines(["Four score and seven years ago..."]) + result.stdout.fnmatch_lines( + ["*test_pass_has_output*", "Four score and seven years ago..."] + ) + assert "test_pass_no_output" not in result.stdout.str() def test_color_yes(testdir): From ba5630e0f8fd35a3a8e98b5dee9572832198bc82 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 26 Sep 2018 10:49:14 -0300 Subject: [PATCH 14/27] Simplify test_pytest_plugins_in_non_top_level_conftest_deprecated --- testing/deprecated_test.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index fbaca4e30..974450044 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -178,21 +178,12 @@ def test_pytest_catchlog_deprecated(testdir, plugin): def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir): from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST - subdirectory = testdir.tmpdir.join("subdirectory") - subdirectory.mkdir() - # create the inner conftest with makeconftest and then move it to the subdirectory - testdir.makeconftest( - """ + testdir.makepyfile( + **{ + "subdirectory/conftest.py": """ pytest_plugins=['capture'] """ - ) - testdir.tmpdir.join("conftest.py").move(subdirectory.join("conftest.py")) - # make the top level conftest - testdir.makeconftest( - """ - import warnings - warnings.filterwarnings('always', category=DeprecationWarning) - """ + } ) testdir.makepyfile( """ From 32c5a113e23e31f06b32dbed9ad1a71e7d5d870c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 26 Sep 2018 12:00:49 -0300 Subject: [PATCH 15/27] Do not issue non-top-level conftest warning when --pyargs is used Fix #4039 --- changelog/4039.bugfix.rst | 2 ++ src/_pytest/config/__init__.py | 7 ++++++- testing/deprecated_test.py | 30 +++++++++++++++++++++++++++++- testing/test_conftest.py | 1 + 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 changelog/4039.bugfix.rst diff --git a/changelog/4039.bugfix.rst b/changelog/4039.bugfix.rst new file mode 100644 index 000000000..867b29498 --- /dev/null +++ b/changelog/4039.bugfix.rst @@ -0,0 +1,2 @@ +No longer issue warnings about using ``pytest_plugins`` in non-top-level directories when using ``--pyargs``: the +current ``--pyargs`` mechanism is not reliable and might give false negatives. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index bc45d65a9..0f8d2d5f3 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -351,6 +351,7 @@ class PytestPluginManager(PluginManager): else None ) self._noconftest = namespace.noconftest + self._using_pyargs = namespace.pyargs testpaths = namespace.file_or_dir foundanchor = False for path in testpaths: @@ -416,7 +417,11 @@ class PytestPluginManager(PluginManager): _ensure_removed_sysmodule(conftestpath.purebasename) try: mod = conftestpath.pyimport() - if hasattr(mod, "pytest_plugins") and self._configured: + if ( + hasattr(mod, "pytest_plugins") + and self._configured + and not self._using_pyargs + ): from _pytest.deprecated import ( PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST ) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 974450044..64b4a0124 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -191,7 +191,7 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir): pass """ ) - res = testdir.runpytest_subprocess() + res = testdir.runpytest() assert res.ret == 0 msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] res.stdout.fnmatch_lines( @@ -201,6 +201,34 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir): ) +@pytest.mark.parametrize("use_pyargs", [True, False]) +def test_pytest_plugins_in_non_top_level_conftest_deprecated_pyargs( + testdir, use_pyargs +): + """When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)""" + from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST + + files = { + "src/pkg/__init__.py": "", + "src/pkg/conftest.py": "", + "src/pkg/test_root.py": "def test(): pass", + "src/pkg/sub/__init__.py": "", + "src/pkg/sub/conftest.py": "pytest_plugins=['capture']", + "src/pkg/sub/test_bar.py": "def test(): pass", + } + testdir.makepyfile(**files) + testdir.syspathinsert(testdir.tmpdir.join("src")) + + args = ("--pyargs", "pkg") if use_pyargs else () + res = testdir.runpytest(*args) + assert res.ret == 0 + msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] + if use_pyargs: + assert msg not in res.stdout.str() + else: + res.stdout.fnmatch_lines("*{msg}*".format(msg=msg)) + + def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_top_level_conftest( testdir ): diff --git a/testing/test_conftest.py b/testing/test_conftest.py index f3b5bac38..07da5d5ee 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -30,6 +30,7 @@ def conftest_setinitial(conftest, args, confcutdir=None): self.file_or_dir = args self.confcutdir = str(confcutdir) self.noconftest = False + self.pyargs = False conftest._set_initial_conftests(Namespace()) From 44d29d887efb2993f7ffdd6b5615562223f44c01 Mon Sep 17 00:00:00 2001 From: Denis Otkidach Date: Wed, 26 Sep 2018 19:02:35 +0300 Subject: [PATCH 16/27] Changelog entry on excluding empty reports for passed tests --- changelog/4040.trivial.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4040.trivial.rst diff --git a/changelog/4040.trivial.rst b/changelog/4040.trivial.rst new file mode 100644 index 000000000..d63541100 --- /dev/null +++ b/changelog/4040.trivial.rst @@ -0,0 +1 @@ +Exclude empty reports for passed tests when ``-rP`` option is used. From 783019a8e627844c58848390320609e9204e06a4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 27 Sep 2018 08:08:03 -0300 Subject: [PATCH 17/27] Rename 4040.trivial.rst to 4040.bugfix.rst --- changelog/{4040.trivial.rst => 4040.bugfix.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{4040.trivial.rst => 4040.bugfix.rst} (100%) diff --git a/changelog/4040.trivial.rst b/changelog/4040.bugfix.rst similarity index 100% rename from changelog/4040.trivial.rst rename to changelog/4040.bugfix.rst From c2179c31279cc9fd78138f4714edaadb1421b5e8 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 30 Sep 2018 18:34:56 -0700 Subject: [PATCH 18/27] Improve error messaging when invalid syntax is passed to the -m option --- changelog/4051.trivial | 1 + src/_pytest/mark/legacy.py | 5 ++++- testing/test_mark.py | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelog/4051.trivial diff --git a/changelog/4051.trivial b/changelog/4051.trivial new file mode 100644 index 000000000..401f3b1a8 --- /dev/null +++ b/changelog/4051.trivial @@ -0,0 +1 @@ +Imrpove Error messaging when Invalid Python Syntax is passed through the ``-m`` option to limit what marks to run against. diff --git a/src/_pytest/mark/legacy.py b/src/_pytest/mark/legacy.py index ab016a035..0d0d852ce 100644 --- a/src/_pytest/mark/legacy.py +++ b/src/_pytest/mark/legacy.py @@ -66,7 +66,10 @@ python_keywords_allowed_list = ["or", "and", "not"] def matchmark(colitem, markexpr): """Tries to match on any marker names, attached to the given colitem.""" - return eval(markexpr, {}, MarkMapping.from_item(colitem)) + try: + return eval(markexpr, {}, MarkMapping.from_item(colitem)) + except SyntaxError as e: + raise SyntaxError(str(e) + "\nMarker expression must be valid Python!") def matchkeyword(colitem, keywordexpr): diff --git a/testing/test_mark.py b/testing/test_mark.py index ddeff2789..06bbe65f4 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -786,6 +786,18 @@ class TestFunctional(object): deselected_tests = dlist[0].items assert len(deselected_tests) == 2 + def test_invalid_m_option(self, testdir): + testdir.makepyfile( + """ + def test_a(): + pass + """ + ) + result = testdir.runpytest("-m bogus/") + result.stdout.fnmatch_lines( + ["INTERNALERROR> Marker expression must be valid Python!"] + ) + def test_keywords_at_node_level(self, testdir): testdir.makepyfile( """ From 7a6d16c1eb6b374386138a5e7f8017ef7ce2ff86 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 30 Sep 2018 18:45:49 -0700 Subject: [PATCH 19/27] Adding .rst to end of changelog fragment filename. --- changelog/{4051.trivial => 4051.trivial.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{4051.trivial => 4051.trivial.rst} (100%) diff --git a/changelog/4051.trivial b/changelog/4051.trivial.rst similarity index 100% rename from changelog/4051.trivial rename to changelog/4051.trivial.rst From 732cc2687d3a28209ed696b3fb5d0ee220c13dd0 Mon Sep 17 00:00:00 2001 From: Jose Carlos Menezes Date: Mon, 1 Oct 2018 14:31:28 -0300 Subject: [PATCH 20/27] Add possible values for fixture scope to docs --- doc/en/fixture.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 8ea13c7f4..e48e055fe 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -171,6 +171,7 @@ to cause the decorated ``smtp_connection`` fixture function to only be invoked once per test *module* (the default is to invoke once per test *function*). Multiple test functions in a test module will thus each receive the same ``smtp_connection`` fixture instance, thus saving time. +Possible values for ``scope`` are: ``function``, ``class``, ``module``, ``package`` or ``session``. The next example puts the fixture function into a separate ``conftest.py`` file so that tests from multiple test modules in the directory can From 4dc73bda45b3cbf1bc42429b29943c1557db5a6d Mon Sep 17 00:00:00 2001 From: Jose Carlos Menezes Date: Mon, 1 Oct 2018 14:41:30 -0300 Subject: [PATCH 21/27] Update changelog --- changelog/3928.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3928.doc.rst diff --git a/changelog/3928.doc.rst b/changelog/3928.doc.rst new file mode 100644 index 000000000..d61b110ae --- /dev/null +++ b/changelog/3928.doc.rst @@ -0,0 +1 @@ +Add possible values for fixture scope to docs. From d24a7e6c5a415ece9ad377d13b20daefc944fdd4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 29 Sep 2018 19:55:04 -0300 Subject: [PATCH 22/27] Issue warning if Monkeypatch.setenv/delenv receive non-strings in Python 2 Fixes the bug described in: https://github.com/tox-dev/tox/pull/1025#discussion_r221273830 Which is more evident when using `unicode_literals`. --- changelog/4056.bugfix.rst | 4 ++++ src/_pytest/monkeypatch.py | 14 ++++++++++++++ testing/test_monkeypatch.py | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 changelog/4056.bugfix.rst diff --git a/changelog/4056.bugfix.rst b/changelog/4056.bugfix.rst new file mode 100644 index 000000000..f018a4776 --- /dev/null +++ b/changelog/4056.bugfix.rst @@ -0,0 +1,4 @@ +``MonkeyPatch.setenv`` and ``MonkeyPatch.delenv`` issue a warning if the environment variable name is not ``str`` on Python 2. + +In Python 2, adding ``unicode`` keys to ``os.environ`` causes problems with ``subprocess`` (and possible other modules), +making this a subtle bug specially susceptible when used with ``from __future__ import unicode_literals``. diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 67279003f..60feb0802 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -4,9 +4,12 @@ from __future__ import absolute_import, division, print_function import os import sys import re +import warnings from contextlib import contextmanager import six + +import pytest from _pytest.fixtures import fixture RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$") @@ -209,6 +212,15 @@ class MonkeyPatch(object): self._setitem.append((dic, name, dic.get(name, notset))) del dic[name] + def _warn_if_env_name_is_not_str(self, name): + """On Python 2, warn if the given environment variable name is not a native str (#4056)""" + if six.PY2 and not isinstance(name, str): + warnings.warn( + pytest.PytestWarning( + "Environment variable name {!r} should be str".format(name) + ) + ) + def setenv(self, name, value, prepend=None): """ Set environment variable ``name`` to ``value``. If ``prepend`` is a character, read the current environment variable value @@ -216,6 +228,7 @@ class MonkeyPatch(object): value = str(value) if prepend and name in os.environ: value = value + prepend + os.environ[name] + self._warn_if_env_name_is_not_str(name) self.setitem(os.environ, name, value) def delenv(self, name, raising=True): @@ -225,6 +238,7 @@ class MonkeyPatch(object): If ``raising`` is set to False, no exception will be raised if the environment variable is missing. """ + self._warn_if_env_name_is_not_str(name) self.delitem(os.environ, name, raising=raising) def syspath_prepend(self, path): diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index adf10e4d0..f0f63023d 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -3,6 +3,8 @@ import os import sys import textwrap +import six + import pytest from _pytest.monkeypatch import MonkeyPatch @@ -192,6 +194,30 @@ def test_delenv(): del os.environ[name] +@pytest.mark.skipif(six.PY3, reason="Python 2 only test") +class TestEnvironKeysWarning(object): + """ + os.environ needs keys to be native strings, otherwise it will cause problems with other modules (notably + subprocess). We only test this behavior on Python 2, because Python 3 raises an error right away. + """ + + VAR_NAME = u"PYTEST_INTERNAL_MY_VAR" + + def test_setenv_unicode_key(self, monkeypatch): + with pytest.warns( + pytest.PytestWarning, + match="Environment variable name {!r} should be str".format(self.VAR_NAME), + ): + monkeypatch.setenv(self.VAR_NAME, "2") + + def test_delenv_unicode_key(self, monkeypatch): + with pytest.warns( + pytest.PytestWarning, + match="Environment variable name {!r} should be str".format(self.VAR_NAME), + ): + monkeypatch.delenv(self.VAR_NAME, raising=False) + + def test_setenv_prepend(): import os From 20d0f0e56bfa87440920cc4809d1505d975e130f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 1 Oct 2018 17:12:05 -0300 Subject: [PATCH 23/27] Improve changelog for #4051 --- changelog/4051.bugfix.rst | 1 + changelog/4051.trivial.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelog/4051.bugfix.rst delete mode 100644 changelog/4051.trivial.rst diff --git a/changelog/4051.bugfix.rst b/changelog/4051.bugfix.rst new file mode 100644 index 000000000..abea99233 --- /dev/null +++ b/changelog/4051.bugfix.rst @@ -0,0 +1 @@ +Improve error message when an invalid Python expression is passed to the ``-m`` option. diff --git a/changelog/4051.trivial.rst b/changelog/4051.trivial.rst deleted file mode 100644 index 401f3b1a8..000000000 --- a/changelog/4051.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Imrpove Error messaging when Invalid Python Syntax is passed through the ``-m`` option to limit what marks to run against. From bc009a8582b4787c409528bf9c0d1bc6792d35ce Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Sep 2018 13:06:48 -0300 Subject: [PATCH 24/27] Fix test to comply with pypy 6.0 --- testing/code/test_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 14f06acd0..66e880e05 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -129,7 +129,7 @@ def test_source_strip_multiline(): def test_syntaxerror_rerepresentation(): ex = pytest.raises(SyntaxError, _pytest._code.compile, "xyz xyz") assert ex.value.lineno == 1 - assert ex.value.offset in (4, 7) # XXX pypy/jython versus cpython? + assert ex.value.offset in (4, 5, 7) # XXX pypy/jython versus cpython? assert ex.value.text.strip(), "x x" From 9d971d33bec34715715bf5c5c269bc73fca00c36 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 1 Oct 2018 18:45:08 -0300 Subject: [PATCH 25/27] Hide internal pytest.warns traceback --- src/_pytest/recwarn.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 0eee4c841..7a0586697 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -212,6 +212,8 @@ class WarningsChecker(WarningsRecorder): def __exit__(self, *exc_info): super(WarningsChecker, self).__exit__(*exc_info) + __tracebackhide__ = True + # only check if we're not currently handling an exception if all(a is None for a in exc_info): if self.expected_warning is not None: From 1a323fbd3c52db051ae32e2b27275717b891b322 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 1 Oct 2018 18:48:41 -0300 Subject: [PATCH 26/27] Show a warning when non-str is given to Monkeypatch.setenv --- src/_pytest/monkeypatch.py | 10 +++++++++- testing/acceptance_test.py | 4 ++-- testing/test_cacheprovider.py | 14 +++++++------- testing/test_junitxml.py | 2 +- testing/test_monkeypatch.py | 27 ++++++++++++++++++++------- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 60feb0802..4bd6b607d 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -225,7 +225,15 @@ class MonkeyPatch(object): """ Set environment variable ``name`` to ``value``. If ``prepend`` is a character, read the current environment variable value and prepend the ``value`` adjoined with the ``prepend`` character.""" - value = str(value) + if not isinstance(value, str): + warnings.warn( + pytest.PytestWarning( + "Environment variable value {!r} should be str, converted to str implicitly".format( + value + ) + ) + ) + value = str(value) if prepend and name in os.environ: value = value + prepend + os.environ[name] self._warn_if_env_name_is_not_str(name) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 8a9585be2..332af27b5 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -577,7 +577,7 @@ class TestInvocationVariants(object): return what empty_package = testdir.mkpydir("empty_package") - monkeypatch.setenv("PYTHONPATH", join_pythonpath(empty_package)) + monkeypatch.setenv("PYTHONPATH", str(join_pythonpath(empty_package))) # the path which is not a package raises a warning on pypy; # no idea why only pypy and not normal python warn about it here with warnings.catch_warnings(): @@ -586,7 +586,7 @@ class TestInvocationVariants(object): assert result.ret == 0 result.stdout.fnmatch_lines(["*2 passed*"]) - monkeypatch.setenv("PYTHONPATH", join_pythonpath(testdir)) + monkeypatch.setenv("PYTHONPATH", str(join_pythonpath(testdir))) result = testdir.runpytest("--pyargs", "tpkg.test_missing", syspathinsert=True) assert result.ret != 0 result.stderr.fnmatch_lines(["*not*found*test_missing*"]) diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 5d73dc846..2444d8bc1 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -215,7 +215,7 @@ def test_cache_show(testdir): class TestLastFailed(object): def test_lastfailed_usecase(self, testdir, monkeypatch): - monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) + monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1") p = testdir.makepyfile( """ def test_1(): @@ -301,7 +301,7 @@ class TestLastFailed(object): assert "test_a.py" not in result.stdout.str() def test_lastfailed_difference_invocations(self, testdir, monkeypatch): - monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) + monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1") testdir.makepyfile( test_a="""\ def test_a1(): @@ -335,7 +335,7 @@ class TestLastFailed(object): result.stdout.fnmatch_lines(["*1 failed*1 desel*"]) def test_lastfailed_usecase_splice(self, testdir, monkeypatch): - monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) + monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1") testdir.makepyfile( """\ def test_1(): @@ -474,8 +474,8 @@ class TestLastFailed(object): ) def rlf(fail_import, fail_run): - monkeypatch.setenv("FAILIMPORT", fail_import) - monkeypatch.setenv("FAILTEST", fail_run) + monkeypatch.setenv("FAILIMPORT", str(fail_import)) + monkeypatch.setenv("FAILTEST", str(fail_run)) testdir.runpytest("-q") config = testdir.parseconfigure() @@ -519,8 +519,8 @@ class TestLastFailed(object): ) def rlf(fail_import, fail_run, args=()): - monkeypatch.setenv("FAILIMPORT", fail_import) - monkeypatch.setenv("FAILTEST", fail_run) + monkeypatch.setenv("FAILIMPORT", str(fail_import)) + monkeypatch.setenv("FAILTEST", str(fail_run)) result = testdir.runpytest("-q", "--lf", *args) config = testdir.parseconfigure() diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 3928548a8..2304c4a50 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -850,7 +850,7 @@ def test_logxml_path_expansion(tmpdir, monkeypatch): assert xml_tilde.logfile == home_tilde # this is here for when $HOME is not set correct - monkeypatch.setenv("HOME", tmpdir) + monkeypatch.setenv("HOME", str(tmpdir)) home_var = os.path.normpath(os.path.expandvars("$HOME/test.xml")) xml_var = LogXML("$HOME%stest.xml" % tmpdir.sep, None) diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index f0f63023d..853c0192f 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -165,7 +165,8 @@ def test_delitem(): def test_setenv(): monkeypatch = MonkeyPatch() - monkeypatch.setenv("XYZ123", 2) + with pytest.warns(pytest.PytestWarning): + monkeypatch.setenv("XYZ123", 2) import os assert os.environ["XYZ123"] == "2" @@ -194,15 +195,16 @@ def test_delenv(): del os.environ[name] -@pytest.mark.skipif(six.PY3, reason="Python 2 only test") -class TestEnvironKeysWarning(object): +class TestEnvironWarnings(object): """ - os.environ needs keys to be native strings, otherwise it will cause problems with other modules (notably - subprocess). We only test this behavior on Python 2, because Python 3 raises an error right away. + os.environ keys and values should be native strings, otherwise it will cause problems with other modules (notably + subprocess). On Python 2 os.environ accepts anything without complaining, while Python 3 does the right thing + and raises an error. """ VAR_NAME = u"PYTEST_INTERNAL_MY_VAR" + @pytest.mark.skipif(six.PY3, reason="Python 2 only test") def test_setenv_unicode_key(self, monkeypatch): with pytest.warns( pytest.PytestWarning, @@ -210,6 +212,7 @@ class TestEnvironKeysWarning(object): ): monkeypatch.setenv(self.VAR_NAME, "2") + @pytest.mark.skipif(six.PY3, reason="Python 2 only test") def test_delenv_unicode_key(self, monkeypatch): with pytest.warns( pytest.PytestWarning, @@ -217,14 +220,24 @@ class TestEnvironKeysWarning(object): ): monkeypatch.delenv(self.VAR_NAME, raising=False) + def test_setenv_non_str_warning(self, monkeypatch): + value = 2 + msg = ( + "Environment variable value {!r} should be str, converted to str implicitly" + ) + with pytest.warns(pytest.PytestWarning, match=msg.format(value)): + monkeypatch.setenv(str(self.VAR_NAME), value) + def test_setenv_prepend(): import os monkeypatch = MonkeyPatch() - monkeypatch.setenv("XYZ123", 2, prepend="-") + with pytest.warns(pytest.PytestWarning): + monkeypatch.setenv("XYZ123", 2, prepend="-") assert os.environ["XYZ123"] == "2" - monkeypatch.setenv("XYZ123", 3, prepend="-") + with pytest.warns(pytest.PytestWarning): + monkeypatch.setenv("XYZ123", 3, prepend="-") assert os.environ["XYZ123"] == "3-2" monkeypatch.undo() assert "XYZ123" not in os.environ From d3673c7429f00ac7dfa91180194d43fe9dd99b5c Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 2 Oct 2018 07:46:02 -0700 Subject: [PATCH 27/27] Preparing release version 3.8.2 --- CHANGELOG.rst | 52 +++++++++++++++++++++++++++++++ changelog/3539.bugfix.rst | 1 - changelog/3928.doc.rst | 1 - changelog/4034.bugfix.rst | 3 -- changelog/4036.removal.rst | 9 ------ changelog/4039.bugfix.rst | 2 -- changelog/4040.bugfix.rst | 1 - changelog/4051.bugfix.rst | 1 - changelog/4056.bugfix.rst | 4 --- doc/en/announce/index.rst | 1 + doc/en/announce/release-3.8.2.rst | 28 +++++++++++++++++ doc/en/example/markers.rst | 16 +++++----- doc/en/example/nonpython.rst | 2 +- doc/en/example/parametrize.rst | 7 ++--- doc/en/example/simple.rst | 2 +- doc/en/fixture.rst | 6 ++-- 16 files changed, 97 insertions(+), 39 deletions(-) delete mode 100644 changelog/3539.bugfix.rst delete mode 100644 changelog/3928.doc.rst delete mode 100644 changelog/4034.bugfix.rst delete mode 100644 changelog/4036.removal.rst delete mode 100644 changelog/4039.bugfix.rst delete mode 100644 changelog/4040.bugfix.rst delete mode 100644 changelog/4051.bugfix.rst delete mode 100644 changelog/4056.bugfix.rst create mode 100644 doc/en/announce/release-3.8.2.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d68a160ab..26bc28af1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,58 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 3.8.2 (2018-10-02) +========================= + +Deprecations and Removals +------------------------- + +- `#4036 `_: The ``item`` parameter of ``pytest_warning_captured`` hook is now documented as deprecated. We realized only after + the ``3.8`` release that this parameter is incompatible with ``pytest-xdist``. + + Our policy is to not deprecate features during bugfix releases, but in this case we believe it makes sense as we are + only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get + the word out that hook implementers should not use this parameter at all. + + In a future release ``item`` will always be ``None`` and will emit a proper warning when a hook implementation + makes use of it. + + + +Bug Fixes +--------- + +- `#3539 `_: Fix reload on assertion rewritten modules. + + +- `#4034 `_: The ``.user_properties`` attribute of ``TestReport`` objects is a list + of (name, value) tuples, but could sometimes be instantiated as a tuple + of tuples. It is now always a list. + + +- `#4039 `_: No longer issue warnings about using ``pytest_plugins`` in non-top-level directories when using ``--pyargs``: the + current ``--pyargs`` mechanism is not reliable and might give false negatives. + + +- `#4040 `_: Exclude empty reports for passed tests when ``-rP`` option is used. + + +- `#4051 `_: Improve error message when an invalid Python expression is passed to the ``-m`` option. + + +- `#4056 `_: ``MonkeyPatch.setenv`` and ``MonkeyPatch.delenv`` issue a warning if the environment variable name is not ``str`` on Python 2. + + In Python 2, adding ``unicode`` keys to ``os.environ`` causes problems with ``subprocess`` (and possible other modules), + making this a subtle bug specially susceptible when used with ``from __future__ import unicode_literals``. + + + +Improved Documentation +---------------------- + +- `#3928 `_: Add possible values for fixture scope to docs. + + pytest 3.8.1 (2018-09-22) ========================= diff --git a/changelog/3539.bugfix.rst b/changelog/3539.bugfix.rst deleted file mode 100644 index d0741cda9..000000000 --- a/changelog/3539.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix reload on assertion rewritten modules. diff --git a/changelog/3928.doc.rst b/changelog/3928.doc.rst deleted file mode 100644 index d61b110ae..000000000 --- a/changelog/3928.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Add possible values for fixture scope to docs. diff --git a/changelog/4034.bugfix.rst b/changelog/4034.bugfix.rst deleted file mode 100644 index 44044fb29..000000000 --- a/changelog/4034.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``.user_properties`` attribute of ``TestReport`` objects is a list -of (name, value) tuples, but could sometimes be instantiated as a tuple -of tuples. It is now always a list. diff --git a/changelog/4036.removal.rst b/changelog/4036.removal.rst deleted file mode 100644 index a124028c4..000000000 --- a/changelog/4036.removal.rst +++ /dev/null @@ -1,9 +0,0 @@ -The ``item`` parameter of ``pytest_warning_captured`` hook is now documented as deprecated. We realized only after -the ``3.8`` release that this parameter is incompatible with ``pytest-xdist``. - -Our policy is to not deprecate features during bugfix releases, but in this case we believe it makes sense as we are -only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get -the word out that hook implementers should not use this parameter at all. - -In a future release ``item`` will always be ``None`` and will emit a proper warning when a hook implementation -makes use of it. diff --git a/changelog/4039.bugfix.rst b/changelog/4039.bugfix.rst deleted file mode 100644 index 867b29498..000000000 --- a/changelog/4039.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -No longer issue warnings about using ``pytest_plugins`` in non-top-level directories when using ``--pyargs``: the -current ``--pyargs`` mechanism is not reliable and might give false negatives. diff --git a/changelog/4040.bugfix.rst b/changelog/4040.bugfix.rst deleted file mode 100644 index d63541100..000000000 --- a/changelog/4040.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Exclude empty reports for passed tests when ``-rP`` option is used. diff --git a/changelog/4051.bugfix.rst b/changelog/4051.bugfix.rst deleted file mode 100644 index abea99233..000000000 --- a/changelog/4051.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Improve error message when an invalid Python expression is passed to the ``-m`` option. diff --git a/changelog/4056.bugfix.rst b/changelog/4056.bugfix.rst deleted file mode 100644 index f018a4776..000000000 --- a/changelog/4056.bugfix.rst +++ /dev/null @@ -1,4 +0,0 @@ -``MonkeyPatch.setenv`` and ``MonkeyPatch.delenv`` issue a warning if the environment variable name is not ``str`` on Python 2. - -In Python 2, adding ``unicode`` keys to ``os.environ`` causes problems with ``subprocess`` (and possible other modules), -making this a subtle bug specially susceptible when used with ``from __future__ import unicode_literals``. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index cdedd3ca7..e6c712120 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-3.8.2 release-3.8.1 release-3.8.0 release-3.7.4 diff --git a/doc/en/announce/release-3.8.2.rst b/doc/en/announce/release-3.8.2.rst new file mode 100644 index 000000000..124c33aa4 --- /dev/null +++ b/doc/en/announce/release-3.8.2.rst @@ -0,0 +1,28 @@ +pytest-3.8.2 +======================================= + +pytest 3.8.2 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/latest/changelog.html. + +Thanks to all who contributed to this release, among them: + +* Ankit Goel +* Anthony Sottile +* Bruno Oliveira +* Daniel Hahler +* Denis Otkidach +* Harry Percival +* Jeffrey Rackauckas +* Jose Carlos Menezes +* Ronny Pfannschmidt +* Zac-HD +* iwanb + + +Happy testing, +The pytest Development Team diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index cb6368a64..a2cb8a676 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``:: $ pytest -v -m webtest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 3 deselected @@ -44,7 +44,7 @@ Or the inverse, running all tests except the webtest ones:: $ pytest -v -m "not webtest" =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 1 deselected @@ -64,7 +64,7 @@ tests based on their module, class, method, or function name:: $ pytest -v test_server.py::TestClass::test_method =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 1 item @@ -77,7 +77,7 @@ You can also select on the class:: $ pytest -v test_server.py::TestClass =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 1 item @@ -90,7 +90,7 @@ Or select multiple nodes:: $ pytest -v test_server.py::TestClass test_server.py::test_send_http =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 2 items @@ -128,7 +128,7 @@ select tests based on their names:: $ pytest -v -k http # running with the above defined example module =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 3 deselected @@ -141,7 +141,7 @@ And you can also run all tests except the ones that match the keyword:: $ pytest -k "not send_http" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 1 deselected @@ -156,7 +156,7 @@ Or to select "http" and "quick" tests:: $ pytest -k "http or quick" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 2 deselected diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index bda15065a..a266b5bfe 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode:: nonpython $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collecting ... collected 2 items diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 5af269bba..7dca5510a 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -411,11 +411,10 @@ is to be run with different sets of arguments for its three arguments: Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize):: . $ pytest -rs -q multipython.py - ...ssssssssssssssssssssssss [100%] + ...sss...sssssssss...sss... [100%] ========================= short test summary info ========================== - SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found - SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found - 3 passed, 24 skipped in 0.12 seconds + SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found + 12 passed, 15 skipped in 0.12 seconds Indirect parametrization of optional implementations/imports -------------------------------------------------------------------- diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index c6e6c428e..8c3975c60 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -357,7 +357,7 @@ which will add info only when run with "--v":: $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache info1: did you know that ... did you? diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index e48e055fe..d90850d09 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -727,7 +727,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:: $ pytest test_fixture_marks.py -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 3 items @@ -770,7 +770,7 @@ Here we declare an ``app`` fixture which receives the previously defined $ pytest -v test_appsetup.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 2 items @@ -839,7 +839,7 @@ Let's run the tests in verbose mode and with looking at the print-output:: $ pytest -v -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 8 items