:maxdepth: 2
+ release-6.0.2
release-6.0.1
release-6.0.0
release-6.0.0rc1
--- /dev/null
+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
.. 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)
=========================
- `#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__``.
.. 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).
.. code-block:: bash
$ pytest --version
- pytest 6.0.1
+ pytest 6.0.2
.. _`simpletest`:
:members:
-pytest.ExitCode
-~~~~~~~~~~~~~~~
+ExitCode
+~~~~~~~~
.. autoclass:: _pytest.config.ExitCode
:members:
+File
+~~~~
+
+.. autoclass:: _pytest.nodes.File()
+ :members:
+ :show-inheritance:
+
FixtureDef
~~~~~~~~~~
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,
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}
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}
# 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)
self._in_suspended = True
def resume_capturing(self) -> None:
- self._state = "resumed"
+ self._state = "started"
if self.out:
self.out.resume()
if self.err:
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()
@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]:
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."""
# 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
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):
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):
import queue
import sys
import textwrap
+from typing import Any
+from typing import Dict
from typing import Tuple
from typing import Union
@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)
def test1(caplog):
assert caplog.handler.level == 0
+ caplog.set_level(9999)
caplog.set_level(41)
assert caplog.handler.level == 41
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
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(
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)
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):
"""