Imported Upstream version 6.0.2 upstream/6.0.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 18 Mar 2021 02:33:40 +0000 (11:33 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 18 Mar 2021 02:33:40 +0000 (11:33 +0900)
17 files changed:
doc/en/announce/index.rst
doc/en/announce/release-6.0.2.rst [new file with mode: 0644]
doc/en/changelog.rst
doc/en/deprecations.rst
doc/en/getting-started.rst
doc/en/reference.rst
scripts/release.minor.rst
scripts/release.patch.rst
src/_pytest/_code/code.py
src/_pytest/capture.py
src/_pytest/logging.py
src/_pytest/nodes.py
src/_pytest/python.py
testing/code/test_excinfo.py
testing/logging/test_fixture.py
testing/python/metafunc.py
testing/test_capture.py

index 49d7a0465c87d4f6a22610675e3868348fab76ac..e011aaa8160ffa7ad89db119a91ef603b39f7189 100644 (file)
@@ -6,6 +6,7 @@ Release announcements
    :maxdepth: 2
 
 
+   release-6.0.2
    release-6.0.1
    release-6.0.0
    release-6.0.0rc1
diff --git a/doc/en/announce/release-6.0.2.rst b/doc/en/announce/release-6.0.2.rst
new file mode 100644 (file)
index 0000000..16eabc5
--- /dev/null
@@ -0,0 +1,19 @@
+pytest-6.0.2
+=======================================
+
+pytest 6.0.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/stable/changelog.html.
+
+Thanks to all of the contributors to this release:
+
+* Bruno Oliveira
+* Ran Benita
+
+
+Happy testing,
+The pytest Development Team
index 7516b53f4ca789a6dac9fdcefc29c9643a985742..feb113e2135ed216368dbde1418cea2e66411e93 100644 (file)
@@ -28,6 +28,25 @@ with advance notice in the **Deprecations** section of releases.
 
 .. towncrier release notes start
 
+pytest 6.0.2 (2020-09-04)
+=========================
+
+Bug Fixes
+---------
+
+- `#7148 <https://github.com/pytest-dev/pytest/issues/7148>`_: Fixed ``--log-cli`` potentially causing unrelated ``print`` output to be swallowed.
+
+
+- `#7672 <https://github.com/pytest-dev/pytest/issues/7672>`_: Fixed log-capturing level restored incorrectly if ``caplog.set_level`` is called more than once.
+
+
+- `#7686 <https://github.com/pytest-dev/pytest/issues/7686>`_: Fixed `NotSetType.token` being used as the parameter ID when the parametrization list is empty.
+  Regressed in pytest 6.0.0.
+
+
+- `#7707 <https://github.com/pytest-dev/pytest/issues/7707>`_: Fix internal error when handling some exceptions that contain multiple lines or the style uses multiple lines (``--tb=line`` for example).
+
+
 pytest 6.0.1 (2020-07-30)
 =========================
 
@@ -212,9 +231,9 @@ Breaking Changes
 
 
 - `#7224 <https://github.com/pytest-dev/pytest/issues/7224>`_: The `item.catch_log_handler` and `item.catch_log_handlers` attributes, set by the
-  logging plugin and never meant to be public , are no longer available.
+  logging plugin and never meant to be public, are no longer available.
 
-  The deprecated ``--no-print-logs`` option is removed. Use ``--show-capture`` instead.
+  The deprecated ``--no-print-logs`` option and ``log_print`` ini option are removed. Use ``--show-capture`` instead.
 
 
 - `#7226 <https://github.com/pytest-dev/pytest/issues/7226>`_: Removed the unused ``args`` parameter from ``pytest.Function.__init__``.
index a2bed18625683f314f5e1ebd87df82a85d4229fa..3334b5d5fe458fef070197d6e86456677399cb37 100644 (file)
@@ -51,9 +51,10 @@ a public API and may break in the future.
 .. versionremoved:: 6.0
 
 
-Option ``--no-print-logs`` is removed. If you used ``--no-print-logs``, please use ``--show-capture`` instead.
+The ``--no-print-logs`` option and ``log_print`` ini setting are removed. If
+you used them, please use ``--show-capture`` instead.
 
-``--show-capture`` command-line option was added in ``pytest 3.5.0`` and allows to specify how to
+A ``--show-capture`` command-line option was added in ``pytest 3.5.0`` which allows to specify how to
 display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default).
 
 
index c160191b65484b211a211e1e43ce8e38716b85a7..caca6f511de6e60109add9d31f6b3fb1e5204b6d 100644 (file)
@@ -28,7 +28,7 @@ Install ``pytest``
 .. code-block:: bash
 
     $ pytest --version
-    pytest 6.0.1
+    pytest 6.0.2
 
 .. _`simpletest`:
 
index 775dd556a8a04954f427605a502ab1dd27253376..7f99cdc90fdd7babdbed210a6067798efffddfc2 100644 (file)
@@ -783,12 +783,19 @@ ExceptionInfo
     :members:
 
 
-pytest.ExitCode
-~~~~~~~~~~~~~~~
+ExitCode
+~~~~~~~~
 
 .. autoclass:: _pytest.config.ExitCode
     :members:
 
+File
+~~~~
+
+.. autoclass:: _pytest.nodes.File()
+    :members:
+    :show-inheritance:
+
 
 FixtureDef
 ~~~~~~~~~~
@@ -1466,20 +1473,6 @@ passed multiple times. The expected format is ``name=value``. For example::
     For more information, see :ref:`logging`.
 
 
-.. confval:: log_print
-
-
-
-    If set to ``False``, will disable displaying captured logging messages for failed tests.
-
-    .. code-block:: ini
-
-        [pytest]
-        log_print = False
-
-    For more information, see :ref:`logging`.
-
-
 .. confval:: markers
 
     When the ``--strict-markers`` or ``--strict`` command-line arguments are used,
index f71f9b1b64ce890f9f17583ac84fa0674dc35414..76e447f0c6d9a832b27f81a4db2d1c3a34de2426 100644 (file)
@@ -3,23 +3,20 @@ pytest-{version}
 
 The pytest team is proud to announce the {version} release!
 
-pytest is a mature Python testing tool with more than a 2000 tests
-against itself, passing on many different interpreters and platforms.
+This release contains new features, improvements, bug fixes, and breaking changes, so users
+are encouraged to take a look at the CHANGELOG carefully:
 
-This release contains a number of bug fixes and improvements, so users are encouraged
-to take a look at the CHANGELOG:
-
-    https://docs.pytest.org/en/latest/changelog.html
+    https://docs.pytest.org/en/stable/changelog.html
 
 For complete documentation, please visit:
 
-    https://docs.pytest.org/en/latest/
+    https://docs.pytest.org/en/stable/
 
 As usual, you can upgrade from PyPI via:
 
     pip install -U pytest
 
-Thanks to all who contributed to this release, among them:
+Thanks to all of the contributors to this release:
 
 {contributors}
 
index b1ad2dbd775a9eb4773f83aaaf1047e2db92a2cf..59fbe50ce0e51c2c7335919b48991d6e013f0519 100644 (file)
@@ -7,9 +7,9 @@ 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.
+The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
 
-Thanks to all who contributed to this release, among them:
+Thanks to all of the contributors to this release:
 
 {contributors}
 
index 219ebb68ff5e66eba834759b2d004a88927f8ac7..14a4681ab49f4e4d6253d5217d3adbd94e72af59 100644 (file)
@@ -1038,25 +1038,21 @@ class ReprEntry(TerminalRepr):
         # such as ">   assert 0"
         fail_marker = "{}   ".format(FormattedExcinfo.fail_marker)
         indent_size = len(fail_marker)
-        indents = []
-        source_lines = []
-        failure_lines = []
-        seeing_failures = False
-        for line in self.lines:
-            is_source_line = not line.startswith(fail_marker)
-            if is_source_line:
-                assert not seeing_failures, (
-                    "Unexpected failure lines between source lines:\n"
-                    + "\n".join(self.lines)
-                )
+        indents = []  # type: List[str]
+        source_lines = []  # type: List[str]
+        failure_lines = []  # type: List[str]
+        for index, line in enumerate(self.lines):
+            is_failure_line = line.startswith(fail_marker)
+            if is_failure_line:
+                # from this point on all lines are considered part of the failure
+                failure_lines.extend(self.lines[index:])
+                break
+            else:
                 if self.style == "value":
                     source_lines.append(line)
                 else:
                     indents.append(line[:indent_size])
                     source_lines.append(line[indent_size:])
-            else:
-                seeing_failures = True
-                failure_lines.append(line)
 
         tw._write_source(source_lines, indents)
 
index f538b67eceb31b93fb213455cc9fc3b5039f1685..a20d14fe7e330635e684a6e0c3a900e3e28f2cea 100644 (file)
@@ -537,7 +537,7 @@ class MultiCapture:
             self._in_suspended = True
 
     def resume_capturing(self) -> None:
-        self._state = "resumed"
+        self._state = "started"
         if self.out:
             self.out.resume()
         if self.err:
@@ -558,6 +558,10 @@ class MultiCapture:
         if self.in_:
             self.in_.done()
 
+    def is_started(self) -> bool:
+        """Whether actively capturing -- not suspended or stopped."""
+        return self._state == "started"
+
     def readouterr(self) -> CaptureResult:
         if self.out:
             out = self.out.snap()
@@ -697,11 +701,19 @@ class CaptureManager:
     @contextlib.contextmanager
     def global_and_fixture_disabled(self) -> Generator[None, None, None]:
         """Context manager to temporarily disable global and current fixture capturing."""
-        self.suspend()
+        do_fixture = self._capture_fixture and self._capture_fixture._is_started()
+        if do_fixture:
+            self.suspend_fixture()
+        do_global = self._global_capturing and self._global_capturing.is_started()
+        if do_global:
+            self.suspend_global_capture()
         try:
             yield
         finally:
-            self.resume()
+            if do_global:
+                self.resume_global_capture()
+            if do_fixture:
+                self.resume_fixture()
 
     @contextlib.contextmanager
     def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
@@ -810,6 +822,12 @@ class CaptureFixture:
         if self._capture is not None:
             self._capture.resume_capturing()
 
+    def _is_started(self) -> bool:
+        """Whether actively capturing -- not disabled or closed."""
+        if self._capture is not None:
+            return self._capture.is_started()
+        return False
+
     @contextlib.contextmanager
     def disabled(self) -> Generator[None, None, None]:
         """Temporarily disables capture while inside the 'with' block."""
index 0ee9457ea72a26cd94da2b0be6b2d231f8f53952..6c422647dbc0ec1b1ee4676be4e4fa009454f312 100644 (file)
@@ -437,7 +437,8 @@ class LogCaptureFixture:
         # save the original log-level to restore it during teardown
         self._initial_logger_levels.setdefault(logger, logger_obj.level)
         logger_obj.setLevel(level)
-        self._initial_handler_level = self.handler.level
+        if self._initial_handler_level is None:
+            self._initial_handler_level = self.handler.level
         self.handler.setLevel(level)
 
     @contextmanager
index 560548aea64b0f1c148c9284686df9e094f166ca..91cf7e5ac3541a5f3f1b5fda0aaf65f7c78dcddb 100644 (file)
@@ -604,7 +604,10 @@ class FSCollector(Collector):
 
 
 class File(FSCollector):
-    """ base class for collecting tests from a file. """
+    """Base class for collecting tests from a file.
+
+    :ref:`non-python tests`.
+    """
 
 
 class Item(Node):
index aa81714868384bdf22f8e5da237537700d568983..28c2dc849c74bb566612576bacc50cf98e3a7b5c 100644 (file)
@@ -1254,6 +1254,9 @@ def _idval(
         return str(val)
     elif isinstance(val, REGEX_TYPE):
         return ascii_escaped(val.pattern)
+    elif val is NOTSET:
+        # Fallback to default. Note that NOTSET is an enum.Enum.
+        pass
     elif isinstance(val, enum.Enum):
         return str(val)
     elif isinstance(getattr(val, "__name__", None), str):
index 78b55e26e01a8503d282864ff8a1bbbfb92125d6..8eab319ead8e5671a452f33337e9b2663f7d82c1 100644 (file)
@@ -4,6 +4,8 @@ import os
 import queue
 import sys
 import textwrap
+from typing import Any
+from typing import Dict
 from typing import Tuple
 from typing import Union
 
@@ -1045,28 +1047,34 @@ raise ValueError()
     @pytest.mark.parametrize(
         "reproptions",
         [
-            {
-                "style": style,
-                "showlocals": showlocals,
-                "funcargs": funcargs,
-                "tbfilter": tbfilter,
-            }
-            for style in ("long", "short", "no")
+            pytest.param(
+                {
+                    "style": style,
+                    "showlocals": showlocals,
+                    "funcargs": funcargs,
+                    "tbfilter": tbfilter,
+                },
+                id="style={},showlocals={},funcargs={},tbfilter={}".format(
+                    style, showlocals, funcargs, tbfilter
+                ),
+            )
+            for style in ["long", "short", "line", "no", "native", "value", "auto"]
             for showlocals in (True, False)
             for tbfilter in (True, False)
             for funcargs in (True, False)
         ],
     )
-    def test_format_excinfo(self, importasmod, reproptions):
-        mod = importasmod(
-            """
-            def g(x):
-                raise ValueError(x)
-            def f():
-                g(3)
-        """
-        )
-        excinfo = pytest.raises(ValueError, mod.f)
+    def test_format_excinfo(self, reproptions: Dict[str, Any]) -> None:
+        def bar():
+            assert False, "some error"
+
+        def foo():
+            bar()
+
+        # using inline functions as opposed to importasmod so we get source code lines
+        # in the tracebacks (otherwise getinspect doesn't find the source code).
+        with pytest.raises(AssertionError) as excinfo:
+            foo()
         file = io.StringIO()
         tw = TerminalWriter(file=file)
         repr = excinfo.getrepr(**reproptions)
index 6e5e9c2b42a0af545d229fc64d934ba4701ff875..f18d7087dc570e06117f319a7406196f30b30a23 100644 (file)
@@ -65,6 +65,7 @@ def test_change_level_undos_handler_level(testdir: Testdir) -> None:
 
         def test1(caplog):
             assert caplog.handler.level == 0
+            caplog.set_level(9999)
             caplog.set_level(41)
             assert caplog.handler.level == 41
 
index 4e6cfaf91ba8b80bd8ce2198a5705030f93b3266..5d935153931f2b85a0471772af98556c1907b1c3 100644 (file)
@@ -19,6 +19,7 @@ from hypothesis import strategies
 import pytest
 from _pytest import fixtures
 from _pytest import python
+from _pytest.compat import NOTSET
 from _pytest.outcomes import fail
 from _pytest.pytester import Testdir
 from _pytest.python import _idval
@@ -363,6 +364,14 @@ class TestMetafunc:
         for val, expected in values:
             assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected
 
+    def test_notset_idval(self) -> None:
+        """Test that a NOTSET value (used by an empty parameterset) generates
+        a proper ID.
+
+        Regression test for #7686.
+        """
+        assert _idval(NOTSET, "a", 0, None, nodeid=None, config=None) == "a0"
+
     def test_idmaker_autoname(self) -> None:
         """#250"""
         result = idmaker(
index bc89501c73bf3135c88cf05a31ca14dd1c2bb526..678bc1863c101d22e82e234428cfd4e50b1a63b6 100644 (file)
@@ -16,6 +16,7 @@ from _pytest.capture import _get_multicapture
 from _pytest.capture import CaptureManager
 from _pytest.capture import MultiCapture
 from _pytest.config import ExitCode
+from _pytest.pytester import Testdir
 
 # note: py.io capture tests where copied from
 # pylib 1.4.20.dev2 (rev 13d9af95547e)
@@ -633,6 +634,34 @@ class TestCaptureFixture:
         else:
             result.stdout.no_fnmatch_line("*test_normal executed*")
 
+    def test_disabled_capture_fixture_twice(self, testdir: Testdir) -> None:
+        """Test that an inner disabled() exit doesn't undo an outer disabled().
+
+        Issue #7148.
+        """
+        testdir.makepyfile(
+            """
+            def test_disabled(capfd):
+                print('captured before')
+                with capfd.disabled():
+                    print('while capture is disabled 1')
+                    with capfd.disabled():
+                        print('while capture is disabled 2')
+                    print('while capture is disabled 1 after')
+                print('captured after')
+                assert capfd.readouterr() == ('captured before\\ncaptured after\\n', '')
+        """
+        )
+        result = testdir.runpytest_subprocess()
+        result.stdout.fnmatch_lines(
+            [
+                "*while capture is disabled 1",
+                "*while capture is disabled 2",
+                "*while capture is disabled 1 after",
+            ],
+            consecutive=True,
+        )
+
     @pytest.mark.parametrize("fixture", ["capsys", "capfd"])
     def test_fixture_use_by_other_fixtures(self, testdir, fixture):
         """