From 8641c0fd40116f204010bc9a55edc56ff9528d6a Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 18 Mar 2021 11:33:39 +0900 Subject: [PATCH] Imported Upstream version 6.0.1 --- doc/en/announce/index.rst | 1 + doc/en/announce/release-6.0.1.rst | 21 +++++++++++++++++++ doc/en/changelog.rst | 20 ++++++++++++++++++ doc/en/getting-started.rst | 2 +- doc/en/tmpdir.rst | 9 ++++++-- src/_pytest/helpconfig.py | 9 +++++--- src/_pytest/logging.py | 4 ++++ src/_pytest/mark/structures.py | 14 ++++++------- src/_pytest/reports.py | 5 +++-- testing/logging/test_fixture.py | 35 +++++++++++++++++++++++++++++-- testing/test_helpconfig.py | 35 +++++++++++++++++++++++++++++++ testing/test_runner.py | 27 ++++++++++++++++++++++++ 12 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 doc/en/announce/release-6.0.1.rst diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 7d176aa..49d7a04 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.0.1 release-6.0.0 release-6.0.0rc1 release-5.4.3 diff --git a/doc/en/announce/release-6.0.1.rst b/doc/en/announce/release-6.0.1.rst new file mode 100644 index 0000000..33fdbed --- /dev/null +++ b/doc/en/announce/release-6.0.1.rst @@ -0,0 +1,21 @@ +pytest-6.0.1 +======================================= + +pytest 6.0.1 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: + +* Bruno Oliveira +* Mattreex +* Ran Benita +* hp310780 + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 2ad8de2..7516b53 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,26 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.0.1 (2020-07-30) +========================= + +Bug Fixes +--------- + +- `#7394 `_: Passing an empty ``help`` value to ``Parser.add_option`` is now accepted instead of crashing when running ``pytest --help``. + Passing ``None`` raises a more informative ``TypeError``. + + +- `#7558 `_: Fix pylint ``not-callable`` lint on ``pytest.mark.parametrize()`` and the other builtin marks: + ``skip``, ``skipif``, ``xfail``, ``usefixtures``, ``filterwarnings``. + + +- `#7559 `_: Fix regression in plugins using ``TestReport.longreprtext`` (such as ``pytest-html``) when ``TestReport.longrepr`` is not a string. + + +- `#7569 `_: Fix logging capture handler's level not reset on teardown after a call to ``caplog.set_level()``. + + pytest 6.0.0 (2020-07-28) ========================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index a2f6daa..c160191 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.0.0 + pytest 6.0.1 .. _`simpletest`: diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index a3749d8..5f882b1 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -192,8 +192,13 @@ You can override the default temporary directory setting like this: pytest --basetemp=mydir -When distributing tests on the local machine, ``pytest`` takes care to -configure a basetemp directory for the sub processes such that all temporary +.. warning:: + + The contents of ``mydir`` will be completely removed, so make sure to use a directory + for that purpose only. + +When distributing tests on the local machine using ``pytest-xdist``, care is taken to +automatically configure a basetemp directory for the sub processes such that all temporary data lands below a single per-test run basetemp directory. .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 2495285..f3623b8 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -170,6 +170,8 @@ def showhelp(config: Config) -> None: help, type, default = config._parser._inidict[name] if type is None: type = "string" + if help is None: + raise TypeError("help argument cannot be None for {}".format(name)) spec = "{} ({}):".format(name, type) tw.write(" %s" % spec) spec_len = len(spec) @@ -191,9 +193,10 @@ def showhelp(config: Config) -> None: tw.write(" " * (indent_len - spec_len - 2)) wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) - tw.line(wrapped[0]) - for line in wrapped[1:]: - tw.line(indent + line) + if wrapped: + tw.line(wrapped[0]) + for line in wrapped[1:]: + tw.line(indent + line) tw.line() tw.line("environment variables:") diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 11031f2..0ee9457 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -345,6 +345,7 @@ class LogCaptureFixture: """Creates a new funcarg.""" self._item = item # dict of log name -> log level + self._initial_handler_level = None # type: Optional[int] self._initial_logger_levels = {} # type: Dict[Optional[str], int] def _finalize(self) -> None: @@ -353,6 +354,8 @@ class LogCaptureFixture: This restores the log levels changed by :meth:`set_level`. """ # restore log levels + if self._initial_handler_level is not None: + self.handler.setLevel(self._initial_handler_level) for logger_name, level in self._initial_logger_levels.items(): logger = logging.getLogger(logger_name) logger.setLevel(level) @@ -434,6 +437,7 @@ 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 self.handler.setLevel(level) @contextmanager diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 5edeecd..6567822 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -4,7 +4,6 @@ import typing import warnings from typing import Any from typing import Callable -from typing import cast from typing import Iterable from typing import List from typing import Mapping @@ -473,14 +472,13 @@ class MarkGenerator: # See TYPE_CHECKING above. if TYPE_CHECKING: - # Using casts instead of type comments intentionally - issue #7473. # TODO(py36): Change to builtin annotation syntax. - skip = cast(_SkipMarkDecorator, None) - skipif = cast(_SkipifMarkDecorator, None) - xfail = cast(_XfailMarkDecorator, None) - parametrize = cast(_ParametrizeMarkDecorator, None) - usefixtures = cast(_UsefixturesMarkDecorator, None) - filterwarnings = cast(_FilterwarningsMarkDecorator, None) + skip = _SkipMarkDecorator(Mark("skip", (), {})) + skipif = _SkipifMarkDecorator(Mark("skipif", (), {})) + xfail = _XfailMarkDecorator(Mark("xfail", (), {})) + parametrize = _ParametrizeMarkDecorator(Mark("parametrize", (), {})) + usefixtures = _UsefixturesMarkDecorator(Mark("usefixtures", (), {})) + filterwarnings = _FilterwarningsMarkDecorator(Mark("filterwarnings", (), {})) def __getattr__(self, name: str) -> MarkDecorator: if name[0] == "_": diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 186c53e..cbd9ae1 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -82,9 +82,10 @@ class BaseReport: longrepr.toterminal(out) else: try: - out.line(longrepr) + s = str(longrepr) except UnicodeEncodeError: - out.line("") + s = "" + out.line(s) def get_sections(self, prefix: str) -> Iterator[Tuple[str, str]]: for name, content in self.sections: diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index da53033..6e5e9c2 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -2,6 +2,7 @@ import logging import pytest from _pytest.logging import caplog_records_key +from _pytest.pytester import Testdir logger = logging.getLogger(__name__) sublogger = logging.getLogger(__name__ + ".baz") @@ -27,8 +28,11 @@ def test_change_level(caplog): assert "CRITICAL" in caplog.text -def test_change_level_undo(testdir): - """Ensure that 'set_level' is undone after the end of the test""" +def test_change_level_undo(testdir: Testdir) -> None: + """Ensure that 'set_level' is undone after the end of the test. + + Tests the logging output themselves (affacted both by logger and handler levels). + """ testdir.makepyfile( """ import logging @@ -50,6 +54,33 @@ def test_change_level_undo(testdir): result.stdout.no_fnmatch_line("*log from test2*") +def test_change_level_undos_handler_level(testdir: Testdir) -> None: + """Ensure that 'set_level' is undone after the end of the test (handler). + + Issue #7569. Tests the handler level specifically. + """ + testdir.makepyfile( + """ + import logging + + def test1(caplog): + assert caplog.handler.level == 0 + caplog.set_level(41) + assert caplog.handler.level == 41 + + def test2(caplog): + assert caplog.handler.level == 0 + + def test3(caplog): + assert caplog.handler.level == 0 + caplog.set_level(43) + assert caplog.handler.level == 43 + """ + ) + result = testdir.runpytest() + result.assert_outcomes(passed=3) + + def test_with_statement(caplog): with caplog.at_level(logging.INFO): logger.debug("handler DEBUG level") diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 24590dd..a33273a 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -38,6 +38,41 @@ def test_help(testdir): ) +def test_none_help_param_raises_exception(testdir): + """Tests a None help param raises a TypeError. + """ + testdir.makeconftest( + """ + def pytest_addoption(parser): + parser.addini("test_ini", None, default=True, type="bool") + """ + ) + result = testdir.runpytest("--help") + result.stderr.fnmatch_lines( + ["*TypeError: help argument cannot be None for test_ini*"] + ) + + +def test_empty_help_param(testdir): + """Tests an empty help param is displayed correctly. + """ + testdir.makeconftest( + """ + def pytest_addoption(parser): + parser.addini("test_ini", "", default=True, type="bool") + """ + ) + result = testdir.runpytest("--help") + assert result.ret == 0 + lines = [ + " required_plugins (args):", + " plugins that must be present for pytest to run*", + " test_ini (bool):*", + "environment variables:", + ] + result.stdout.fnmatch_lines(lines, consecutive=True) + + def test_hookvalidation_unknown(testdir): testdir.makeconftest( """ diff --git a/testing/test_runner.py b/testing/test_runner.py index def3f91..b207ccc 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -951,6 +951,33 @@ class TestReportContents: rep = reports[1] assert rep.longreprtext == "" + def test_longreprtext_skip(self, testdir) -> None: + """TestReport.longreprtext can handle non-str ``longrepr`` attributes (#7559)""" + reports = testdir.runitem( + """ + import pytest + def test_func(): + pytest.skip() + """ + ) + _, call_rep, _ = reports + assert isinstance(call_rep.longrepr, tuple) + assert "Skipped" in call_rep.longreprtext + + def test_longreprtext_collect_skip(self, testdir) -> None: + """CollectReport.longreprtext can handle non-str ``longrepr`` attributes (#7559)""" + testdir.makepyfile( + """ + import pytest + pytest.skip(allow_module_level=True) + """ + ) + rec = testdir.inline_run() + calls = rec.getcalls("pytest_collectreport") + _, call = calls + assert isinstance(call.report.longrepr, tuple) + assert "Skipped" in call.report.longreprtext + def test_longreprtext_failure(self, testdir) -> None: reports = testdir.runitem( """ -- 2.34.1