From 1b5149793659cd234fc91a9ac00c2be78b62e00d Mon Sep 17 00:00:00 2001 From: Ankit Goel Date: Thu, 18 Oct 2018 02:36:41 +0000 Subject: [PATCH 01/17] [WIP] Update warning stacklevel when wrapping warnings.warn --- src/_pytest/recwarn.py | 2 ++ testing/test_recwarn.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 62c9158fb..5adc42d26 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -156,6 +156,8 @@ class WarningsRecorder(warnings.catch_warnings): if six.PY2: def warn(*args, **kwargs): + kwargs.setdefault("stacklevel", 1) + kwargs["stacklevel"] += 1 return self._saved_warn(*args, **kwargs) warnings.warn, self._saved_warn = warn, warnings.warn diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 3ae543248..e1d44f174 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -6,6 +6,12 @@ import pytest from _pytest.recwarn import WarningsRecorder +def test_recwarn_stacklevel(recwarn): + warnings.warn("hello") + warn = recwarn.pop() + assert warn.filename == __file__ + + def test_recwarn_functional(testdir): testdir.makepyfile( """ From cdd0e18ca8c82a2cb6e61ee2f2893782fe29756b Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 18 Oct 2018 10:40:47 -0700 Subject: [PATCH 02/17] Emulate resetting the warnings registry for python 2.x --- changelog/4192.bugfix.rst | 1 + src/_pytest/recwarn.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 changelog/4192.bugfix.rst diff --git a/changelog/4192.bugfix.rst b/changelog/4192.bugfix.rst new file mode 100644 index 000000000..86ed95360 --- /dev/null +++ b/changelog/4192.bugfix.rst @@ -0,0 +1 @@ +Fix filename reported by ``warnings.warn`` when using ``recwarn`` under python2. diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 5adc42d26..8738fe0b8 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -158,7 +158,18 @@ class WarningsRecorder(warnings.catch_warnings): def warn(*args, **kwargs): kwargs.setdefault("stacklevel", 1) kwargs["stacklevel"] += 1 - return self._saved_warn(*args, **kwargs) + + # emulate resetting the warn registry + f_globals = sys._getframe(kwargs["stacklevel"] - 1).f_globals + if "__warningregistry__" in f_globals: + orig = f_globals["__warningregistry__"] + f_globals["__warningregistry__"] = None + try: + return self._saved_warn(*args, **kwargs) + finally: + f_globals["__warningregistry__"] = orig + else: + return self._saved_warn(*args, **kwargs) warnings.warn, self._saved_warn = warn, warnings.warn return self From ac5704290f8f8f0507ba7aa927e944fc62dea69a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 18 Oct 2018 20:05:41 -0300 Subject: [PATCH 03/17] Improve error message when it is not possible to determine a function's signature Fix #4026 --- changelog/4026.bugfix.rst | 1 + src/_pytest/compat.py | 12 ++++++++++-- testing/test_collection.py | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 changelog/4026.bugfix.rst diff --git a/changelog/4026.bugfix.rst b/changelog/4026.bugfix.rst new file mode 100644 index 000000000..ce7bd572b --- /dev/null +++ b/changelog/4026.bugfix.rst @@ -0,0 +1 @@ +Improve error message when it is not possible to determine a function's signature. diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 0cf0e41c2..78bf1bc04 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -13,7 +13,7 @@ from contextlib import contextmanager import py import _pytest -from _pytest.outcomes import TEST_OUTCOME +from _pytest.outcomes import TEST_OUTCOME, fail from six import text_type import six @@ -131,9 +131,17 @@ def getfuncargnames(function, is_method=False, cls=None): # ordered mapping of parameter names to Parameter instances. This # creates a tuple of the names of the parameters that don't have # defaults. + try: + parameters = signature(function).parameters + except (ValueError, TypeError) as e: + fail( + "Could not determine arguments of {!r}: {}".format(function, e), + pytrace=False, + ) + arg_names = tuple( p.name - for p in signature(function).parameters.values() + for p in parameters.values() if ( p.kind is Parameter.POSITIONAL_OR_KEYWORD or p.kind is Parameter.KEYWORD_ONLY diff --git a/testing/test_collection.py b/testing/test_collection.py index fb3860f99..9cd085778 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -952,3 +952,23 @@ def test_collect_init_tests(testdir): "*", ] ) + + +def test_collect_invalid_signature_message(testdir): + """Check that we issue a proper message when we can't determine the signature of a test + function (#4026). + """ + testdir.makepyfile( + """ + import pytest + + class TestCase: + @pytest.fixture + def fix(): + pass + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines( + ["Could not determine arguments of *.fix *: invalid method signature"] + ) From 18035211f5cf9c95cc331ee37156f4c04707c4a0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 18 Oct 2018 19:42:25 -0300 Subject: [PATCH 04/17] Use safe_str() to format warning message about unicode in Python 2 Fix #3691 --- changelog/3691.bugfix.rst | 2 ++ src/_pytest/warnings.py | 2 +- testing/test_warnings.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 changelog/3691.bugfix.rst diff --git a/changelog/3691.bugfix.rst b/changelog/3691.bugfix.rst new file mode 100644 index 000000000..f30dd67a1 --- /dev/null +++ b/changelog/3691.bugfix.rst @@ -0,0 +1,2 @@ +Python 2: safely format warning message about passing unicode strings to ``warnings.warn``, which may cause +surprising ``MemoryError`` exception when monkey patching ``warnings.warn`` itself. diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 5574eee8e..e900ff067 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -123,7 +123,7 @@ def warning_record_to_str(warning_message): if unicode_warning: warnings.warn( "Warning is using unicode non convertible to ascii, " - "converting to a safe representation:\n %s" % msg, + "converting to a safe representation:\n {!r}".format(compat.safe_str(msg)), UnicodeWarning, ) return msg diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 7825f2167..10eb5ea33 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -3,6 +3,8 @@ from __future__ import unicode_literals import sys +import six + import pytest @@ -562,3 +564,30 @@ class TestDeprecationWarningsByDefault: monkeypatch.setenv(str("PYTHONWARNINGS"), str("once::UserWarning")) result = testdir.runpytest_subprocess() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() + + +@pytest.mark.skipif(six.PY3, reason="Python 2 only issue") +def test_infinite_loop_warning_against_unicode_usage_py2(testdir): + """ + We need to be careful when raising the warning about unicode usage with "warnings.warn" + because it might be overwritten by users and this itself causes another warning (#3691). + """ + testdir.makepyfile( + """ + # -*- coding: utf8 -*- + from __future__ import unicode_literals + import warnings + import pytest + + def _custom_showwarning(message, *a, **b): + return "WARNING: {}".format(message) + + warnings.formatwarning = _custom_showwarning + + @pytest.mark.filterwarnings("default") + def test_custom_warning_formatter(): + warnings.warn("¥") + """ + ) + result = testdir.runpytest_subprocess() + result.stdout.fnmatch_lines(["*1 passed, * warnings in*"]) From 864d7fef30f477a0980c9b8f9c283ca4becd4b7b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 18 Oct 2018 21:57:11 -0300 Subject: [PATCH 05/17] Add back some {posargs} placeholders for pytest invocations in tox.ini Those were removed by accident in a previous commits it seems --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 86b3b9458..dbfd4eef5 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ envlist = [testenv] commands = - {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof + {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof {posargs} coverage: coverage combine coverage: coverage report passenv = USER USERNAME COVERAGE_* TRAVIS @@ -41,7 +41,7 @@ deps = py27: mock nose commands = - pytest -n auto --runpytest=subprocess + pytest -n auto --runpytest=subprocess {posargs} [testenv:linting] @@ -58,7 +58,7 @@ deps = hypothesis>=3.56 {env:_PYTEST_TOX_EXTRA_DEP:} commands = - {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto + {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto {posargs} [testenv:py36-xdist] # NOTE: copied from above due to https://github.com/tox-dev/tox/issues/706. From 11ff14be1f8cd8c5304517cd516e767d4de183a9 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 18 Oct 2018 20:11:35 -0700 Subject: [PATCH 06/17] Upgrade pre-commit/pre-commit-hooks to 2.0.0 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5cc56230..c3003ad43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: additional_dependencies: [black==18.9b0] language_version: python3 - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v1.4.0-1 + rev: v2.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer From 0fc1801d0d37cbe2a752bb5415f195792ebdb77f Mon Sep 17 00:00:00 2001 From: ykantor <8383983+ykantor@users.noreply.github.com> Date: Fri, 19 Oct 2018 05:54:29 +0200 Subject: [PATCH 07/17] Update tmpdir.rst --- doc/en/tmpdir.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index d8cd8b705..284f786bc 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -11,11 +11,11 @@ The ``tmp_path`` fixture .. versionadded:: 3.9 -You can use the ``tmpdir`` fixture which will +You can use the ``tmp_path`` fixture which will provide a temporary directory unique to the test invocation, created in the `base temporary directory`_. -``tmpdir`` is a ``pathlib/pathlib2.Path`` object. Here is an example test usage: +``tmp_path`` is a ``pathlib/pathlib2.Path`` object. Here is an example test usage: .. code-block:: python @@ -72,7 +72,7 @@ The ``tmp_path_factory`` fixture The ``tmp_path_facotry`` is a session-scoped fixture which can be used to create arbitrary temporary directories from any other fixture or test. -its intended to replace ``tmpdir_factory`` and returns :class:`pathlib.Path` instances. +It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances. The 'tmpdir' fixture From dde147641ed2a7e81654146280be364cf09c05a4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 19 Oct 2018 09:33:51 -0300 Subject: [PATCH 08/17] Fix linting --- doc/en/tmpdir.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index 284f786bc..ca26bb5c8 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -72,7 +72,7 @@ The ``tmp_path_factory`` fixture The ``tmp_path_facotry`` is a session-scoped fixture which can be used to create arbitrary temporary directories from any other fixture or test. -It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances. +It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances. The 'tmpdir' fixture From 51d358a7a987980153c3e20a98c87ddf6a11dc6e Mon Sep 17 00:00:00 2001 From: Vincent Barbaresi Date: Fri, 19 Oct 2018 19:22:04 +0200 Subject: [PATCH 09/17] fix typo in tmpdir documentation --- doc/en/tmpdir.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index ca26bb5c8..21bdcdd6a 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -69,7 +69,7 @@ The ``tmp_path_factory`` fixture .. versionadded:: 3.9 -The ``tmp_path_facotry`` is a session-scoped fixture which can be used +The ``tmp_path_factory`` is a session-scoped fixture which can be used to create arbitrary temporary directories from any other fixture or test. It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances. From db106d60855fa7536eadbe1d0a38bb65abb6511f Mon Sep 17 00:00:00 2001 From: Vincent Barbaresi Date: Fri, 19 Oct 2018 18:59:23 +0200 Subject: [PATCH 10/17] Fix logging usage in hooks pytest_sessionstart/finish #3340 --- changelog/3340.bugfix.rst | 1 + src/_pytest/logging.py | 23 ++++++++++++++++++++ testing/logging/test_reporting.py | 36 +++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 changelog/3340.bugfix.rst diff --git a/changelog/3340.bugfix.rst b/changelog/3340.bugfix.rst new file mode 100644 index 000000000..72b889d01 --- /dev/null +++ b/changelog/3340.bugfix.rst @@ -0,0 +1 @@ +Fix logging messages not shown in hooks ``pytest_sessionstart()`` and ``pytest_sessionfinish()``. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index ee6795250..8244c8030 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -497,6 +497,29 @@ class LoggingPlugin(object): with self._runtest_for(None, "finish"): yield + @pytest.hookimpl(hookwrapper=True, tryfirst=True) + def pytest_sessionfinish(self): + with self.live_logs_context(): + if self.log_cli_handler: + self.log_cli_handler.set_when("sessionfinish") + if self.log_file_handler is not None: + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield + else: + yield + + @pytest.hookimpl(hookwrapper=True, tryfirst=True) + def pytest_sessionstart(self): + self._setup_cli_logging() + with self.live_logs_context(): + if self.log_cli_handler: + self.log_cli_handler.set_when("sessionstart") + if self.log_file_handler is not None: + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield + else: + yield + @pytest.hookimpl(hookwrapper=True) def pytest_runtestloop(self, session): """Runs all collected test items.""" diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index 498b4c5bd..5863e0115 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -966,3 +966,39 @@ def test_collection_logging_to_file(testdir): assert "Normal message" in contents assert "debug message in test_simple" not in contents assert "info message in test_simple" in contents + + +def test_log_in_hooks(testdir): + log_file = testdir.tmpdir.join("pytest.log").strpath + + testdir.makeini( + """ + [pytest] + log_file={} + log_file_level = INFO + log_cli=true + """.format( + log_file + ) + ) + testdir.makeconftest( + """ + import logging + + def pytest_runtestloop(session): + logging.info('runtestloop') + + def pytest_sessionstart(session): + logging.info('sessionstart') + + def pytest_sessionfinish(session, exitstatus): + logging.info('sessionfinish') + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*sessionstart*", "*runtestloop*", "*sessionfinish*"]) + with open(log_file) as rfh: + contents = rfh.read() + assert "sessionstart" in contents + assert "runtestloop" in contents + assert "sessionfinish" in contents From 02ae7d85318e9e2a02ab292e37123000d694308a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 20 Oct 2018 09:44:42 -0300 Subject: [PATCH 11/17] Improve error message when a recursive dependency between fixtures is detected Fix #2909 --- changelog/2909.bugfix.rst | 1 + src/_pytest/fixtures.py | 15 ++++++++++----- .../test_detect_recursive_dependency_error.py | 15 +++++++++++++++ testing/python/fixture.py | 7 +++++++ 4 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 changelog/2909.bugfix.rst create mode 100644 testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py diff --git a/changelog/2909.bugfix.rst b/changelog/2909.bugfix.rst new file mode 100644 index 000000000..534d17490 --- /dev/null +++ b/changelog/2909.bugfix.rst @@ -0,0 +1 @@ +Improve error message when a recursive dependency between fixtures is detected. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 29eda351f..cfc187bc2 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -762,14 +762,19 @@ class FixtureLookupError(LookupError): if msg is None: fm = self.request._fixturemanager - available = [] + available = set() parentid = self.request._pyfuncitem.parent.nodeid for name, fixturedefs in fm._arg2fixturedefs.items(): faclist = list(fm._matchfactories(fixturedefs, parentid)) - if faclist and name not in available: - available.append(name) - msg = "fixture %r not found" % (self.argname,) - msg += "\n available fixtures: %s" % (", ".join(sorted(available)),) + if faclist: + available.add(name) + if self.argname in available: + msg = " recursive dependency involving fixture '{}' detected".format( + self.argname + ) + else: + msg = "fixture '{}' not found".format(self.argname) + msg += "\n available fixtures: {}".format(", ".join(sorted(available))) msg += "\n use 'pytest --fixtures [testpath]' for help on them." return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py b/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py new file mode 100644 index 000000000..d1efcbb33 --- /dev/null +++ b/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py @@ -0,0 +1,15 @@ +import pytest + + +@pytest.fixture +def fix1(fix2): + return 1 + + +@pytest.fixture +def fix2(fix1): + return 1 + + +def test(fix1): + pass diff --git a/testing/python/fixture.py b/testing/python/fixture.py index f21f7a861..02304ac32 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -60,6 +60,13 @@ class TestFillFixtures(object): """ ) + def test_detect_recursive_dependency_error(self, testdir): + testdir.copy_example() + result = testdir.runpytest() + result.stdout.fnmatch_lines( + ["*recursive dependency involving fixture 'fix1' detected*"] + ) + def test_funcarg_basic(self, testdir): testdir.copy_example() item = testdir.getitem(Path("test_funcarg_basic.py")) From 6b126997e117bb7085a7b3999c1d18cc262de793 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 20 Oct 2018 11:28:39 -0300 Subject: [PATCH 12/17] Remove pytest.config example from skipping at module level docs --- doc/en/skipping.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index dabc8a90f..f705422d8 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -58,18 +58,20 @@ by calling the ``pytest.skip(reason)`` function: if not valid_config(): pytest.skip("unsupported configuration") +The imperative method is useful when it is not possible to evaluate the skip condition +during import time. + It is also possible to skip the whole module using ``pytest.skip(reason, allow_module_level=True)`` at the module level: .. code-block:: python + import sys import pytest - if not pytest.config.getoption("--custom-flag"): - pytest.skip("--custom-flag is missing, skipping tests", allow_module_level=True) + if not sys.platform.startswith("win"): + pytest.skip("skipping windows-only tests", allow_module_level=True) -The imperative method is useful when it is not possible to evaluate the skip condition -during import time. **Reference**: :ref:`pytest.mark.skip ref` From cb39bd065165c6bafb78e30f56b0fca82df5a74d Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 22 Oct 2018 08:49:31 -0700 Subject: [PATCH 13/17] Fixes for flake8 master --- doc/en/example/py2py3/test_py2.py | 5 ++--- doc/en/example/py2py3/test_py3.py | 2 +- src/_pytest/assertion/rewrite.py | 3 +-- src/_pytest/pathlib.py | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/en/example/py2py3/test_py2.py b/doc/en/example/py2py3/test_py2.py index 664acf178..1f665086e 100644 --- a/doc/en/example/py2py3/test_py2.py +++ b/doc/en/example/py2py3/test_py2.py @@ -1,6 +1,5 @@ - def test_exception_syntax(): try: - 0/0 + 0 / 0 except ZeroDivisionError, e: - pass + assert e diff --git a/doc/en/example/py2py3/test_py3.py b/doc/en/example/py2py3/test_py3.py index baf0ffbd8..d95702a53 100644 --- a/doc/en/example/py2py3/test_py3.py +++ b/doc/en/example/py2py3/test_py3.py @@ -2,4 +2,4 @@ def test_exception_syntax(): try: 0 / 0 except ZeroDivisionError as e: - pass + assert e diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 5e76563d9..9b3daf9b4 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -706,10 +706,9 @@ class AssertionRewriter(ast.NodeVisitor): setattr(node, name, new) elif ( isinstance(field, ast.AST) - and # Don't recurse into expressions as they can't contain # asserts. - not isinstance(field, ast.expr) + and not isinstance(field, ast.expr) ): nodes.append(field) diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 081fce904..bf46cbf70 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -245,8 +245,8 @@ def make_numbered_dir_with_cleanup(root, prefix, keep, lock_timeout): p = make_numbered_dir(root, prefix) lock_path = create_cleanup_lock(p) register_cleanup_lock_removal(lock_path) - except Exception as e: - pass + except Exception as exc: + e = exc else: consider_lock_dead_if_created_before = p.stat().st_mtime - lock_timeout cleanup_numbered_dir( From 771e860011c24f5582d31ebf0631d38bee60c770 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 22 Oct 2018 21:47:42 +0000 Subject: [PATCH 14/17] Preparing release version 3.9.2 --- CHANGELOG.rst | 31 +++++++++++++++++++++++++++++++ changelog/2909.bugfix.rst | 1 - changelog/3340.bugfix.rst | 1 - changelog/3533.bugfix.rst | 1 - changelog/3691.bugfix.rst | 2 -- changelog/4026.bugfix.rst | 1 - changelog/4177.bugfix.rst | 1 - changelog/4179.bugfix.rst | 1 - changelog/4192.bugfix.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-3.9.2.rst | 23 +++++++++++++++++++++++ doc/en/example/markers.rst | 16 ++++++++-------- doc/en/example/nonpython.rst | 2 +- doc/en/example/simple.rst | 2 +- doc/en/fixture.rst | 6 +++--- doc/en/writing_plugins.rst | 12 ++++++------ 16 files changed, 74 insertions(+), 28 deletions(-) delete mode 100644 changelog/2909.bugfix.rst delete mode 100644 changelog/3340.bugfix.rst delete mode 100644 changelog/3533.bugfix.rst delete mode 100644 changelog/3691.bugfix.rst delete mode 100644 changelog/4026.bugfix.rst delete mode 100644 changelog/4177.bugfix.rst delete mode 100644 changelog/4179.bugfix.rst delete mode 100644 changelog/4192.bugfix.rst create mode 100644 doc/en/announce/release-3.9.2.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 14881e373..9b4c6355d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,37 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 3.9.2 (2018-10-22) +========================= + +Bug Fixes +--------- + +- `#2909 `_: Improve error message when a recursive dependency between fixtures is detected. + + +- `#3340 `_: Fix logging messages not shown in hooks ``pytest_sessionstart()`` and ``pytest_sessionfinish()``. + + +- `#3533 `_: Fix unescaped XML raw objects in JUnit report for skipped tests + + +- `#3691 `_: Python 2: safely format warning message about passing unicode strings to ``warnings.warn``, which may cause + surprising ``MemoryError`` exception when monkey patching ``warnings.warn`` itself. + + +- `#4026 `_: Improve error message when it is not possible to determine a function's signature. + + +- `#4177 `_: Pin ``setuptools>=40.0`` to support ``py_modules`` in ``setup.cfg`` + + +- `#4179 `_: Restore the tmpdir behaviour of symlinking the current test run. + + +- `#4192 `_: Fix filename reported by ``warnings.warn`` when using ``recwarn`` under python2. + + pytest 3.9.1 (2018-10-16) ========================= diff --git a/changelog/2909.bugfix.rst b/changelog/2909.bugfix.rst deleted file mode 100644 index 534d17490..000000000 --- a/changelog/2909.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Improve error message when a recursive dependency between fixtures is detected. diff --git a/changelog/3340.bugfix.rst b/changelog/3340.bugfix.rst deleted file mode 100644 index 72b889d01..000000000 --- a/changelog/3340.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix logging messages not shown in hooks ``pytest_sessionstart()`` and ``pytest_sessionfinish()``. diff --git a/changelog/3533.bugfix.rst b/changelog/3533.bugfix.rst deleted file mode 100644 index 89f136458..000000000 --- a/changelog/3533.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix unescaped XML raw objects in JUnit report for skipped tests diff --git a/changelog/3691.bugfix.rst b/changelog/3691.bugfix.rst deleted file mode 100644 index f30dd67a1..000000000 --- a/changelog/3691.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Python 2: safely format warning message about passing unicode strings to ``warnings.warn``, which may cause -surprising ``MemoryError`` exception when monkey patching ``warnings.warn`` itself. diff --git a/changelog/4026.bugfix.rst b/changelog/4026.bugfix.rst deleted file mode 100644 index ce7bd572b..000000000 --- a/changelog/4026.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Improve error message when it is not possible to determine a function's signature. diff --git a/changelog/4177.bugfix.rst b/changelog/4177.bugfix.rst deleted file mode 100644 index b26ad4bad..000000000 --- a/changelog/4177.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Pin ``setuptools>=40.0`` to support ``py_modules`` in ``setup.cfg`` diff --git a/changelog/4179.bugfix.rst b/changelog/4179.bugfix.rst deleted file mode 100644 index 6f7467f50..000000000 --- a/changelog/4179.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Restore the tmpdir behaviour of symlinking the current test run. diff --git a/changelog/4192.bugfix.rst b/changelog/4192.bugfix.rst deleted file mode 100644 index 86ed95360..000000000 --- a/changelog/4192.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix filename reported by ``warnings.warn`` when using ``recwarn`` under python2. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index a692eee15..a39be6b17 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-3.9.2 release-3.9.1 release-3.9.0 release-3.8.2 diff --git a/doc/en/announce/release-3.9.2.rst b/doc/en/announce/release-3.9.2.rst new file mode 100644 index 000000000..1440831cb --- /dev/null +++ b/doc/en/announce/release-3.9.2.rst @@ -0,0 +1,23 @@ +pytest-3.9.2 +======================================= + +pytest 3.9.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 +* Ronny Pfannschmidt +* Vincent Barbaresi +* ykantor + + +Happy testing, +The pytest Development Team diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index a2cb8a676..cb6368a64 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 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 a266b5bfe..bda15065a 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collecting ... collected 2 items diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index df83ec97e..b16070287 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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache info1: did you know that ... did you? diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index fa111afa4..65664c0b2 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -732,7 +732,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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 3 items @@ -775,7 +775,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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 2 items @@ -844,7 +844,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/python + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 8 items diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 2cb1caefb..8ac316578 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -420,17 +420,17 @@ additionally it is possible to copy examples for a example folder before running ============================= warnings summary ============================= $REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time testdir.copy_example("test_example.py") - $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead + $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:329: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead return getattr(object, name, default) - $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead + $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:329: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead return getattr(object, name, default) - $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead + $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:329: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead return getattr(object, name, default) - $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead + $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:329: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead return getattr(object, name, default) - $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead + $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:329: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead return getattr(object, name, default) - $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead + $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:329: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead return getattr(object, name, default) -- Docs: https://docs.pytest.org/en/latest/warnings.html From fadac0ffc0102f8ba536f3848623f67c81bb11a2 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 22 Oct 2018 13:57:01 +0200 Subject: [PATCH 15/17] Fix "Plugin already registered" error with symlinks Fixes https://github.com/pytest-dev/pytest/issues/4174. --- changelog/4174.bugfix.rst | 1 + src/_pytest/config/__init__.py | 2 +- testing/test_conftest.py | 8 +++++++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 changelog/4174.bugfix.rst diff --git a/changelog/4174.bugfix.rst b/changelog/4174.bugfix.rst new file mode 100644 index 000000000..5e263c23a --- /dev/null +++ b/changelog/4174.bugfix.rst @@ -0,0 +1 @@ +Fix "ValueError: Plugin already registered" with conftest plugins via symlink. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 88cbf14ba..47e5c2945 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -391,7 +391,7 @@ class PytestPluginManager(PluginManager): # and allow users to opt into looking into the rootdir parent # directories instead of requiring to specify confcutdir clist = [] - for parent in directory.parts(): + for parent in directory.realpath().parts(): if self._confcutdir and self._confcutdir.relto(parent): continue conftestpath = parent.join("conftest.py") diff --git a/testing/test_conftest.py b/testing/test_conftest.py index a2df0ae37..44126613e 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -192,8 +192,10 @@ def test_conftest_confcutdir(testdir): ) def test_conftest_symlink(testdir): """Ensure that conftest.py is used for resolved symlinks.""" - realtests = testdir.tmpdir.mkdir("real").mkdir("app").mkdir("tests") + real = testdir.tmpdir.mkdir("real") + realtests = real.mkdir("app").mkdir("tests") testdir.tmpdir.join("symlinktests").mksymlinkto(realtests) + testdir.tmpdir.join("symlink").mksymlinkto(real) testdir.makepyfile( **{ "real/app/tests/test_foo.py": "def test1(fixture): pass", @@ -220,6 +222,10 @@ def test_conftest_symlink(testdir): ) assert result.ret == EXIT_OK + # Should not cause "ValueError: Plugin already registered" (#4174). + result = testdir.runpytest("-vs", "symlink") + assert result.ret == EXIT_OK + realtests.ensure("__init__.py") result = testdir.runpytest("-vs", "symlinktests/test_foo.py::test1") result.stdout.fnmatch_lines( From 0dd85157ea75860610bbd13c6bdda92dcb57ed73 Mon Sep 17 00:00:00 2001 From: Andreas Profous Date: Tue, 23 Oct 2018 21:44:30 +0200 Subject: [PATCH 16/17] Add reference to ``empty_parameter_set_mark`` parametrize doc --- changelog/3851.doc.rst | 1 + doc/en/parametrize.rst | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 changelog/3851.doc.rst diff --git a/changelog/3851.doc.rst b/changelog/3851.doc.rst new file mode 100644 index 000000000..ec46126f1 --- /dev/null +++ b/changelog/3851.doc.rst @@ -0,0 +1 @@ +Add reference to ``empty_parameter_set_mark`` ini option in documentation of ``@pytest.mark.parametrize`` diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 693cf1913..90ce4ffc6 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -114,6 +114,10 @@ Let's run this:: The one parameter set which caused a failure previously now shows up as an "xfailed (expected to fail)" test. +In case the values provided to ``parametrize`` result in an empty list - for +example, if they're dynamically generated by some function - the behaviour of +pytest is defined by the :confval:`empty_parameter_set_mark` option. + To get all combinations of multiple parametrized arguments you can stack ``parametrize`` decorators:: From 28dbffdaf26519c79c86df67347ef09c31abf6ae Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 23 Oct 2018 08:03:45 -0300 Subject: [PATCH 17/17] Attempt to fix macosx build setup This has been failing as of 2018-10-23 while installing gcc with this message: ==> Installing numpy dependency: gcc ==> Downloading https://homebrew.bintray.com/bottles/gcc-8.2.0.high_sierra.bottl ######################################################################## 100.0% ==> Pouring gcc-8.2.0.high_sierra.bottle.1.tar.gz Error: The `brew link` step did not complete successfully The formula built, but is not symlinked into /usr/local Could not symlink include/c++ Target /usr/local/include/c++ already exists. You may want to remove it: rm '/usr/local/include/c++' To force the link and overwrite all conflicting files: brew link --overwrite gcc To list all files that would be deleted: brew link --overwrite --dry-run gcc Possible conflicting files are: /usr/local/include/c++ -> /usr/local/Caskroom/oclint/0.13.1,17.4.0/oclint-0.13.1/include/c++ --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 00abca0b2..49c94b47e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,11 @@ jobs: env: TOXENV=py37 before_install: - brew update + # remove c++ include files because upgrading python as of 2018-10-23, also + # attempts to upgrade gcc, and it fails because the include files already + # exist. removing the include files is one of the solutions recommended by brew + # this workaround might not be necessary in the future + - rm '/usr/local/include/c++' - brew upgrade python - brew unlink python - brew link python