From f4d3ec6370f5855637d13853bad920e34e914fc0 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Fri, 30 Nov 2018 23:04:59 +0000 Subject: [PATCH 01/12] Logging done late enough might happen when capture already stopped. --- src/_pytest/capture.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index ec72ae3ec..376b4f87b 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -117,7 +117,10 @@ class CaptureManager(object): self._global_capturing = None def resume_global_capture(self): - self._global_capturing.resume_capturing() + # During teardown of the python process, and on rare occasions, capture + # attributes can be `None` while trying to resume global capture. + if self._global_capturing is not None: + self._global_capturing.resume_capturing() def suspend_global_capture(self, in_=False): cap = getattr(self, "_global_capturing", None) From f792cc420c302109011bb47c0f5103c468d957ac Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Sat, 1 Dec 2018 19:11:02 +0000 Subject: [PATCH 02/12] Add changelog entry --- changelog/4487.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4487.bugfix.rst diff --git a/changelog/4487.bugfix.rst b/changelog/4487.bugfix.rst new file mode 100644 index 000000000..3ba8d4731 --- /dev/null +++ b/changelog/4487.bugfix.rst @@ -0,0 +1 @@ +During teardown of the python process, and on rare occasions, capture attributes can be ``None`` while trying to resume global capture. From 517b8bc69e3c1c0c0e94d697900648fff1e440b0 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 3 Dec 2018 11:52:33 +0000 Subject: [PATCH 03/12] Stop using the deprecated `\.warn()` log handler method --- testing/test_capture.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/testing/test_capture.py b/testing/test_capture.py index 47aba70d4..c113f0304 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -302,14 +302,14 @@ class TestLoggingInteraction(object): """\ import logging def setup_function(function): - logging.warn("hello1") + logging.warning("hello1") def test_logging(): - logging.warn("hello2") + logging.warning("hello2") assert 0 def teardown_function(function): - logging.warn("hello3") + logging.warning("hello3") assert 0 """ ) @@ -328,14 +328,14 @@ class TestLoggingInteraction(object): """\ import logging def setup_module(function): - logging.warn("hello1") + logging.warning("hello1") def test_logging(): - logging.warn("hello2") + logging.warning("hello2") assert 0 def teardown_module(function): - logging.warn("hello3") + logging.warning("hello3") assert 0 """ ) @@ -354,7 +354,7 @@ class TestLoggingInteraction(object): """\ import logging logging.basicConfig() - logging.warn("hello435") + logging.warning("hello435") """ ) # make sure that logging is still captured in tests @@ -375,7 +375,7 @@ class TestLoggingInteraction(object): """\ def test_hello(): import logging - logging.warn("hello433") + logging.warning("hello433") assert 0 """ ) @@ -1300,13 +1300,13 @@ def test_capturing_and_logging_fundamentals(testdir, method): Capture=capture.%s) cap.start_capturing() - logging.warn("hello1") + logging.warning("hello1") outerr = cap.readouterr() print("suspend, captured %%s" %%(outerr,)) - logging.warn("hello2") + logging.warning("hello2") cap.pop_outerr_to_orig() - logging.warn("hello3") + logging.warning("hello3") outerr = cap.readouterr() print("suspend2, captured %%s" %% (outerr,)) From 14024c7fc13a1bb60c7971e418caec919a5276ee Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 3 Dec 2018 12:12:26 +0000 Subject: [PATCH 04/12] Test case for #4500 bug and respective fix #4487 --- testing/test_capture.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/testing/test_capture.py b/testing/test_capture.py index c113f0304..2e1912f2e 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -385,6 +385,35 @@ class TestLoggingInteraction(object): assert "something" not in result.stderr.str() assert "operation on closed file" not in result.stderr.str() + def test_logging_after_cap_stopped(self, testdir): + testdir.makeconftest( + """\ + import pytest + import logging + + log = logging.getLogger(__name__) + + @pytest.fixture + def log_on_teardown(): + yield + log.warning('Logging on teardown') + """ + ) + # make sure that logging is still captured in tests + p = testdir.makepyfile( + """\ + def test_hello(log_on_teardown): + import logging + logging.warning("hello433") + assert 1 + raise KeyboardInterrupt() + """ + ) + result = testdir.runpytest_subprocess(p, "--log-cli-level", "info") + assert result.ret != 0 + result.stdout.fnmatch_lines(["*WARNING*hello433*", "*WARNING*Logging on teardown*"]) + assert "AttributeError: 'NoneType' object has no attribute 'resume_capturing'" not in result.stderr.str() + class TestCaptureFixture(object): @pytest.mark.parametrize("opt", [[], ["-s"]]) From c1bdb07b2f2e905e6ec6258fb3638663bb2805e8 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 3 Dec 2018 12:15:04 +0000 Subject: [PATCH 05/12] Fix change log entry --- changelog/4487.bugfix.rst | 1 - changelog/4500.bugfix.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog/4487.bugfix.rst create mode 100644 changelog/4500.bugfix.rst diff --git a/changelog/4487.bugfix.rst b/changelog/4487.bugfix.rst deleted file mode 100644 index 3ba8d4731..000000000 --- a/changelog/4487.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -During teardown of the python process, and on rare occasions, capture attributes can be ``None`` while trying to resume global capture. diff --git a/changelog/4500.bugfix.rst b/changelog/4500.bugfix.rst new file mode 100644 index 000000000..b84b6b117 --- /dev/null +++ b/changelog/4500.bugfix.rst @@ -0,0 +1 @@ +When a fixture yields and a log call is made after the test runs, and, if the test is interrupted, capture attributes are ``None``. From 6da8befc74b9671123e861bcb343179fd5aa5edf Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 3 Dec 2018 12:58:22 +0000 Subject: [PATCH 06/12] Black suggestions --- testing/test_capture.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/testing/test_capture.py b/testing/test_capture.py index 2e1912f2e..d44b58ee0 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -411,8 +411,13 @@ class TestLoggingInteraction(object): ) result = testdir.runpytest_subprocess(p, "--log-cli-level", "info") assert result.ret != 0 - result.stdout.fnmatch_lines(["*WARNING*hello433*", "*WARNING*Logging on teardown*"]) - assert "AttributeError: 'NoneType' object has no attribute 'resume_capturing'" not in result.stderr.str() + result.stdout.fnmatch_lines( + ["*WARNING*hello433*", "*WARNING*Logging on teardown*"] + ) + assert ( + "AttributeError: 'NoneType' object has no attribute 'resume_capturing'" + not in result.stderr.str() + ) class TestCaptureFixture(object): From ae92b8c62444ec7ff9132ed560b8ae502757cd51 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 3 Dec 2018 15:30:45 -0800 Subject: [PATCH 07/12] Upgrade sphinx doc requirements --- doc/en/requirements.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index b9fdcb8f6..80c57422f 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -1,5 +1,3 @@ -pygments-pytest>=1.0.4 -# pinning sphinx to 1.4.* due to search issues with rtd: -# https://github.com/rtfd/readthedocs-sphinx-ext/issues/25 -sphinx ==1.4.* +pygments-pytest>=1.1.0 +sphinx>=1.8.2 sphinxcontrib-trio From db32949c50e41a93a6f3a5f480438f2b7c44bc52 Mon Sep 17 00:00:00 2001 From: Yash Todi Date: Tue, 4 Dec 2018 19:33:13 +0530 Subject: [PATCH 08/12] Rectified typo --- doc/en/mark.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/mark.rst b/doc/en/mark.rst index 5d1cd00f4..d5c5c5735 100644 --- a/doc/en/mark.rst +++ b/doc/en/mark.rst @@ -156,4 +156,4 @@ More details can be found in the `original PR Date: Wed, 5 Dec 2018 11:25:52 +0200 Subject: [PATCH 09/12] Document doctest fixture pitfalls --- doc/en/doctest.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index 52edd4cf2..125ed3aa7 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -154,6 +154,9 @@ which can then be used in your doctests directly:: """ pass +Note that like the normal ``conftest.py``, the fixtures are discovered in the directory tree conftest is in. +Meaning that if you put your doctest with your source code, the relevant conftest.py needs to be in the same directory tree. +Fixtures will not be discovered in a sibling directory tree! Output format ------------- From b7a64d67294832792157f23fa41acea4a0d3fde2 Mon Sep 17 00:00:00 2001 From: Tomer Keren Date: Wed, 5 Dec 2018 11:29:32 +0200 Subject: [PATCH 10/12] Create changelog file for 1495 --- changelog/1495.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/1495.doc.rst diff --git a/changelog/1495.doc.rst b/changelog/1495.doc.rst new file mode 100644 index 000000000..ab7231333 --- /dev/null +++ b/changelog/1495.doc.rst @@ -0,0 +1 @@ +Document common doctest fixture directory tree structure pitfalls From f3babf13ea819b36ae691279ef296927e5c56469 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Dec 2018 14:44:45 +0100 Subject: [PATCH 11/12] Ensure that PYTEST_ADDOPTS are valid by themselves Fixes https://github.com/pytest-dev/pytest/issues/4265. --- changelog/4265.bugfix.rst | 1 + src/_pytest/config/__init__.py | 13 +++++++++++-- testing/test_config.py | 27 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 changelog/4265.bugfix.rst diff --git a/changelog/4265.bugfix.rst b/changelog/4265.bugfix.rst new file mode 100644 index 000000000..7b40737c3 --- /dev/null +++ b/changelog/4265.bugfix.rst @@ -0,0 +1 @@ +Validate arguments from the ``PYTEST_ADDOPTS`` environment variable and the ``addopts`` ini option separately. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 562b50c38..f70c91590 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -784,12 +784,21 @@ class Config(object): for name in _iter_rewritable_modules(package_files): hook.mark_rewrite(name) + def _validate_args(self, args): + """Validate known args.""" + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + return args + def _preparse(self, args, addopts=True): if addopts: - args[:] = shlex.split(os.environ.get("PYTEST_ADDOPTS", "")) + args + env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + if len(env_addopts): + args[:] = self._validate_args(shlex.split(env_addopts)) + args self._initini(args) if addopts: - args[:] = self.getini("addopts") + args + args[:] = self._validate_args(self.getini("addopts")) + args self._checkversion() self._consider_importhook(args) self.pluginmanager.consider_preparse(args) diff --git a/testing/test_config.py b/testing/test_config.py index 605d28aa0..b7c68ea4d 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1082,6 +1082,33 @@ class TestOverrideIniArgs(object): config._preparse([], addopts=True) assert config._override_ini == ["cache_dir=%s" % cache_dir] + def test_addopts_from_env_not_concatenated(self, monkeypatch): + """PYTEST_ADDOPTS should not take values from normal args (#4265).""" + from _pytest.config import get_config + + monkeypatch.setenv("PYTEST_ADDOPTS", "-o") + config = get_config() + with pytest.raises(SystemExit) as excinfo: + config._preparse(["cache_dir=ignored"], addopts=True) + assert excinfo.value.args[0] == _pytest.main.EXIT_USAGEERROR + + def test_addopts_from_ini_not_concatenated(self, testdir): + """addopts from ini should not take values from normal args (#4265).""" + testdir.makeini( + """ + [pytest] + addopts=-o + """ + ) + result = testdir.runpytest("cache_dir=ignored") + result.stderr.fnmatch_lines( + [ + "%s: error: argument -o/--override-ini: expected one argument" + % (testdir.request.config._parser.optparser.prog,) + ] + ) + assert result.ret == _pytest.main.EXIT_USAGEERROR + def test_override_ini_does_not_contain_paths(self): """Check that -o no longer swallows all options after it (#3103)""" from _pytest.config import get_config From 5ecbb02ace74d0bcb49226c59060f04df9dc2b31 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 10 Dec 2018 06:26:23 +0100 Subject: [PATCH 12/12] tests: fix test_raises_exception_looks_iterable Started to fail on py37-xdist between https://travis-ci.org/pytest-dev/pytest/jobs/465498973 and https://travis-ci.org/pytest-dev/pytest/jobs/465792343, but could not find a diff in versions (from the tox header), and both commits failed locally. --- testing/python/raises.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/python/raises.py b/testing/python/raises.py index 6ca19c677..912d34673 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -190,7 +190,8 @@ class TestRaises(object): pass with pytest.raises( - Failed, match="DID NOT RAISE " + Failed, + match=r"DID NOT RAISE ", ): pytest.raises(ClassLooksIterableException, lambda: None)