From b55351274ecd65689a5304619cb1c3391ed8d6da Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 6 Sep 2018 18:56:08 -0300 Subject: [PATCH 01/31] Amend CHANGELOG with missing #3251 --- CHANGELOG.rst | 5 ++++- changelog/3251.feture.rst | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 changelog/3251.feture.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7a0de069c56..fbe5dbb9922 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -63,7 +63,10 @@ Features more info. -- `#3784 `_: Add option to disable plugin auto-loading. +- `#3251 `_: Warnings are now captured and displayed during test collection. + + +- `#3784 `_: ``PYTEST_DISABLE_PLUGIN_AUTOLOAD`` environment variable disables plugin auto-loading when set. - `#3829 `_: Added the ``count`` option to ``console_output_style`` to enable displaying the progress as a count instead of a percentage. diff --git a/changelog/3251.feture.rst b/changelog/3251.feture.rst deleted file mode 100644 index 3ade3093dd1..00000000000 --- a/changelog/3251.feture.rst +++ /dev/null @@ -1 +0,0 @@ -Warnings are now captured and displayed during test collection. From 826adafe2ec2d4fb531f83e2645fb44847e34844 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 7 Sep 2018 09:32:36 -0700 Subject: [PATCH 02/31] Improve pre-commit detection for changelog filenames --- .pre-commit-config.yaml | 9 +++++---- changelog/3955.trivial.rst | 1 + scripts/fail | 7 ------- tox.ini | 4 ++-- 4 files changed, 8 insertions(+), 13 deletions(-) create mode 100644 changelog/3955.trivial.rst delete mode 100755 scripts/fail diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index faae8237297..f0340fb9ddb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,8 @@ repos: language: python additional_dependencies: [pygments, restructuredtext_lint] - id: changelogs-rst - name: changelog files must end in .rst - entry: ./scripts/fail - language: script - files: 'changelog/.*(?=1.11.0 commands = pre-commit run --all-files --show-diff-on-failure [testenv:py27-xdist] @@ -195,7 +195,7 @@ passenv = * deps = colorama gitpython - pre-commit + pre-commit>=1.11.0 towncrier wheel commands = python scripts/release.py {posargs} From 7537e94ddf6aa9efdbb448a256ab4a68f11df47c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 11 Sep 2018 18:51:05 +0200 Subject: [PATCH 03/31] tests: use unittest.mock with py34+ Fixes https://github.com/pytest-dev/pytest/issues/3965. Has to work around https://github.com/tox-dev/tox/issues/706. No coverage for pluggymaster builds is OK though anyway. --- .travis.yml | 4 ++-- appveyor.yml | 2 ++ testing/code/test_code.py | 10 ++++++++-- tox.ini | 22 +++++++++++++--------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1e709ba058a..6f00324edb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,12 +18,12 @@ env: - TOXENV=py27-xdist - TOXENV=py27-trial - TOXENV=py27-numpy - - TOXENV=py27-pluggymaster + - TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1 - TOXENV=py36-pexpect - TOXENV=py36-xdist - TOXENV=py36-trial - TOXENV=py36-numpy - - TOXENV=py36-pluggymaster + - TOXENV=py36-pluggymaster PYTEST_NO_COVERAGE=1 - TOXENV=py27-nobyte - TOXENV=doctesting - TOXENV=docs PYTEST_NO_COVERAGE=1 diff --git a/appveyor.yml b/appveyor.yml index 949397d3b88..7e2c16561e7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,10 +13,12 @@ environment: - TOXENV: "py27-trial" - TOXENV: "py27-numpy" - TOXENV: "py27-pluggymaster" + PYTEST_NO_COVERAGE: "1" - TOXENV: "py36-xdist" - TOXENV: "py36-trial" - TOXENV: "py36-numpy" - TOXENV: "py36-pluggymaster" + PYTEST_NO_COVERAGE: "1" - TOXENV: "py27-nobyte" - TOXENV: "doctesting" - TOXENV: "py36-freeze" diff --git a/testing/code/test_code.py b/testing/code/test_code.py index d1ae648c848..a144bc80e27 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -1,13 +1,19 @@ # coding: utf-8 from __future__ import absolute_import, division, print_function + import sys import _pytest._code import pytest -import mock -from test_excinfo import TWMock from six import text_type +from test_excinfo import TWMock + +try: + import mock +except ImportError: + import unittest.mock as mock + def test_ne(): code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec")) diff --git a/tox.ini b/tox.ini index c7ab4f7a5f5..452bdc60dd3 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,7 @@ setenv = deps = hypothesis>=3.56 nose - mock + {py27,pypy}: mock requests {env:_PYTEST_TOX_EXTRA_DEP:} @@ -38,7 +38,7 @@ deps = changedir = . deps = pytest-xdist>=1.13 - mock + py27: mock nose passenv = USER USERNAME TRAVIS commands = @@ -54,7 +54,7 @@ commands = pre-commit run --all-files --show-diff-on-failure [testenv:py27-xdist] deps = pytest-xdist>=1.13 - mock + {py27,pypy}: mock nose hypothesis>=3.56 {env:_PYTEST_TOX_EXTRA_DEP:} @@ -64,7 +64,13 @@ commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:testing} [testenv:py36-xdist] -deps = {[testenv:py27-xdist]deps} +# NOTE: copied from above due to https://github.com/tox-dev/tox/issues/706. +deps = + pytest-xdist>=1.13 + {py27,pypy}: mock + nose + hypothesis>=3.56 + {env:_PYTEST_TOX_EXTRA_DEP:} commands = {[testenv:py27-xdist]commands} [testenv:py27-pexpect] @@ -87,7 +93,7 @@ commands = {[testenv:py27-pexpect]commands} deps = pytest-xdist>=1.13 hypothesis>=3.56 - mock + py27: mock {env:_PYTEST_TOX_EXTRA_DEP:} distribute = true changedir=testing @@ -127,13 +133,11 @@ commands = {[testenv:py27-numpy]commands} setenv= {[testenv]setenv} _PYTEST_SETUP_SKIP_PLUGGY_DEP=1 -deps = - {[testenv]deps} - git+https://github.com/pytest-dev/pluggy.git@master + # NOTE: using env instead of "{[testenv]deps}", because of https://github.com/tox-dev/tox/issues/706. + _PYTEST_TOX_EXTRA_DEP=git+https://github.com/pytest-dev/pluggy.git@master [testenv:py36-pluggymaster] setenv = {[testenv:py27-pluggymaster]setenv} -deps = {[testenv:py27-pluggymaster]deps} [testenv:docs] skipsdist = True From a0ce9a444188403cbb56cad0f5cb2789cca09826 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 13 Sep 2018 15:38:36 +0200 Subject: [PATCH 04/31] remove the legacy code about im_func and generalize using fix and compat.getimfunc --- src/_pytest/python.py | 9 ++++----- src/_pytest/unittest.py | 3 ++- testing/python/metafunc.py | 8 ++------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 05165027231..1976be80cb3 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -37,6 +37,7 @@ getlocation, enum, get_default_arg_names, + getimfunc, ) from _pytest.outcomes import fail from _pytest.mark.structures import ( @@ -681,14 +682,12 @@ def collect(self): def setup(self): setup_class = _get_xunit_func(self.obj, "setup_class") if setup_class is not None: - setup_class = getattr(setup_class, "im_func", setup_class) - setup_class = getattr(setup_class, "__func__", setup_class) + setup_class = getimfunc(setup_class) setup_class(self.obj) fin_class = getattr(self.obj, "teardown_class", None) if fin_class is not None: - fin_class = getattr(fin_class, "im_func", fin_class) - fin_class = getattr(fin_class, "__func__", fin_class) + fin_class = getimfunc(fin_class) self.addfinalizer(lambda: fin_class(self.obj)) @@ -1433,7 +1432,7 @@ def _initrequest(self): @property def function(self): "underlying python 'function' object" - return getattr(self.obj, "im_func", self.obj) + return getimfunc(self.obj) def _getobj(self): name = self.name diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index a135dbd53f3..a2fd6ad5afe 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -9,6 +9,7 @@ from _pytest.config import hookimpl from _pytest.outcomes import fail, skip, xfail from _pytest.python import transfer_markers, Class, Module, Function +from _pytest.compat import getimfunc def pytest_pycollect_makeitem(collector, name, obj): @@ -53,7 +54,7 @@ def collect(self): x = getattr(self.obj, name) if not getattr(x, "__test__", True): continue - funcobj = getattr(x, "im_func", x) + funcobj = getimfunc(x) transfer_markers(funcobj, cls, module) yield TestCaseFunction(name, parent=self, callobj=funcobj) foundsomething = True diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 1f5daa9b944..7ff1e22831c 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -796,7 +796,7 @@ def test_attributes(self, testdir): p = testdir.makepyfile( """ # assumes that generate/provide runs in the same process - import sys, pytest + import sys, pytest, six def pytest_generate_tests(metafunc): metafunc.addcall(param=metafunc) @@ -815,11 +815,7 @@ class TestClass(object): def test_method(self, metafunc, pytestconfig): assert metafunc.config == pytestconfig assert metafunc.module.__name__ == __name__ - if sys.version_info > (3, 0): - unbound = TestClass.test_method - else: - unbound = TestClass.test_method.im_func - # XXX actually have an unbound test function here? + unbound = six.get_unbound_function(TestClass.test_method) assert metafunc.function == unbound assert metafunc.cls == TestClass """ From 8fe55b1d18783dce80172fb6c09a68a0861719d6 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 13 Sep 2018 15:40:45 +0200 Subject: [PATCH 05/31] add changelog for fix #3975 --- changelog/3975.trivial.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3975.trivial.rst diff --git a/changelog/3975.trivial.rst b/changelog/3975.trivial.rst new file mode 100644 index 00000000000..1013e13b983 --- /dev/null +++ b/changelog/3975.trivial.rst @@ -0,0 +1 @@ +Remove legacy code around im_func as that was python2 only From 9aa6b0903bd0f2a5ecb83cdecbe0c533f8e93938 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 15:15:12 -0300 Subject: [PATCH 06/31] .pytest_cache is now automatically ignored by Git --- changelog/3286.bugfix.rst | 1 + src/_pytest/cacheprovider.py | 9 ++++++--- testing/test_cacheprovider.py | 11 +++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 changelog/3286.bugfix.rst diff --git a/changelog/3286.bugfix.rst b/changelog/3286.bugfix.rst new file mode 100644 index 00000000000..d69e085319b --- /dev/null +++ b/changelog/3286.bugfix.rst @@ -0,0 +1 @@ +``.pytest_cache`` directory is now automatically ignored by Git. Users who would like to contribute a solution for other SCMs please consult/comment on this issue. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 791cf3a33aa..87e24894bc0 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -115,15 +115,18 @@ def set(self, key, value): else: with f: json.dump(value, f, indent=2, sort_keys=True) - self._ensure_readme() - - def _ensure_readme(self): + self._ensure_supporting_files() + def _ensure_supporting_files(self): + """Create supporting files in the cache dir that are not really part of the cache.""" if self._cachedir.is_dir(): readme_path = self._cachedir / "README.md" if not readme_path.is_file(): readme_path.write_text(README_CONTENT) + msg = u"# created by pytest automatically, do not change\n*" + self._cachedir.joinpath(".gitignore").write_text(msg, encoding="UTF-8") + class LFPlugin(object): """ Plugin which implements the --lf (run last-failing) option """ diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 6d425f95b03..5d73dc84668 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -884,3 +884,14 @@ def test_always_passes(): ) testdir.runpytest() assert self.check_readme(testdir) is True + + +def test_gitignore(testdir): + """Ensure we automatically create .gitignore file in the pytest_cache directory (#3286).""" + from _pytest.cacheprovider import Cache + + config = testdir.parseconfig() + cache = Cache.for_config(config) + cache.set("foo", "bar") + msg = "# created by pytest automatically, do not change\n*" + assert cache._cachedir.joinpath(".gitignore").read_text(encoding="UTF-8") == msg From 87ddb2dbd5c35cc1aaaa3da025f46abe408bf5cc Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 15:25:45 -0300 Subject: [PATCH 07/31] Change flaky test_request_garbage to provide more debug information This test fails *very* rarely when running in xdist. --- testing/python/fixture.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index fc3eee42b5f..33c040e3125 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import sys import textwrap import pytest @@ -488,6 +489,10 @@ def test_method(self, something): assert len(arg2fixturedefs) == 1 assert arg2fixturedefs["something"][0].argname == "something" + @pytest.mark.skipif( + hasattr(sys, "pypy_version_info"), + reason="this method of test doesn't work on pypy", + ) def test_request_garbage(self, testdir): testdir.makepyfile( """ @@ -498,33 +503,32 @@ def test_request_garbage(self, testdir): @pytest.fixture(autouse=True) def something(request): - # this method of test doesn't work on pypy - if hasattr(sys, "pypy_version_info"): - yield - else: - original = gc.get_debug() - gc.set_debug(gc.DEBUG_SAVEALL) - gc.collect() + original = gc.get_debug() + gc.set_debug(gc.DEBUG_SAVEALL) + gc.collect() - yield + yield + try: gc.collect() leaked_types = sum(1 for _ in gc.garbage if isinstance(_, PseudoFixtureDef)) + # debug leaked types if the test fails + print(leaked_types) + gc.garbage[:] = [] - try: - assert leaked_types == 0 - finally: - gc.set_debug(original) + assert leaked_types == 0 + finally: + gc.set_debug(original) def test_func(): pass """ ) - reprec = testdir.inline_run() - reprec.assertoutcome(passed=1) + result = testdir.runpytest() + result.stdout.fnmatch_lines("* 1 passed in *") def test_getfixturevalue_recursive(self, testdir): testdir.makeconftest( From fa78da3c033c82ec868ae81137fdb8ba5dba198e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 15:58:22 -0300 Subject: [PATCH 08/31] Update backward compatibility policy with new practices --- doc/en/backwards-compatibility.rst | 7 ++++--- doc/en/warnings.rst | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/en/backwards-compatibility.rst b/doc/en/backwards-compatibility.rst index 55506e7c338..5f8f6d6469b 100644 --- a/doc/en/backwards-compatibility.rst +++ b/doc/en/backwards-compatibility.rst @@ -7,9 +7,11 @@ Keeping backwards compatibility has a very high priority in the pytest project. With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around. -To communicate changes we are already issuing deprecation warnings, but they are not displayed by default. In pytest 3.0 we changed the default setting so that pytest deprecation warnings are displayed if not explicitly silenced (with ``--disable-pytest-warnings``). +To communicate changes we issue deprecation warnings using a custom warning hierarchy (see :ref:`internal-warnings`). These warnings may be suppressed using the standard means: ``-W`` command-line flag or ``filterwarnings`` ini options (see :ref:`warnings`), but we suggest to use these sparingly and temporarily, and heed the warnings when possible. -We will only remove deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we will not remove it in 4.0 but in 5.0). +We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0). + +When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn them into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed. Deprecation Roadmap @@ -17,4 +19,3 @@ Deprecation Roadmap We track deprecation and removal of features using milestones and the `deprecation `_ and `removal `_ labels on GitHub. -Following our deprecation policy, after starting issuing deprecation warnings we keep features for *at least* two minor versions before considering removal. diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 1f0c3bf97a6..9c28ecb4959 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -330,6 +330,9 @@ You can also use it as a contextmanager:: myobject.deprecated_method() + +.. _internal-warnings: + Internal pytest warnings ------------------------ @@ -363,9 +366,8 @@ defines an ``__init__`` constructor, as this prevents the class from being insta These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings. -Following our :ref:`backwards-compatibility`, deprecated features will be kept *at least* two minor releases. After that, -they will changed so they by default raise errors instead of just warnings, so users can adapt to it on their own time -if not having done so until now. In a later release the deprecated feature will be removed completely. +Please read our :ref:`backwards-compatibility` to learn how we proceed about deprecating and eventually removing +features. The following warning types ares used by pytest and are part of the public API: From cbb41f1ae24e164e9d9b2479efe7177b1a07b210 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 16:00:35 -0300 Subject: [PATCH 09/31] Ignore Sphinx's .doctrees folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1b363494dd6..f5cd0145cce 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ src/_pytest/_version.py .eggs/ doc/*/_build +doc/*/.doctrees build/ dist/ *.egg-info From 130cf7e0dbb5b0c142f2eea2f57c22d515a8f805 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 17:20:22 -0300 Subject: [PATCH 10/31] Fix rendering of the ini example for python_files Also added an example using one pattern per line --- doc/en/reference.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 52d83cf6ee3..b956b0a341b 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -1256,15 +1256,25 @@ passed multiple times. The expected format is ``name=value``. For example:: One or more Glob-style file patterns determining which python files are considered as test modules. Search for multiple glob patterns by - adding a space between patterns:: + adding a space between patterns: .. code-block:: ini [pytest] python_files = test_*.py check_*.py example_*.py - By default, pytest will consider any file matching with ``test_*.py`` - and ``*_test.py`` globs as a test module. + Or one per line: + + .. code-block:: ini + + [pytest] + python_files = + test_*.py + check_*.py + example_*.py + + By default, files matching ``test_*.py`` and ``*_test.py`` will be considered + test modules. .. confval:: python_functions From a4dd6ee3ce7bba40ddf36cf4a79b6d74770c082b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 17:31:01 -0300 Subject: [PATCH 11/31] Fix linting --- doc/en/backwards-compatibility.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/en/backwards-compatibility.rst b/doc/en/backwards-compatibility.rst index 5f8f6d6469b..3c30d040eef 100644 --- a/doc/en/backwards-compatibility.rst +++ b/doc/en/backwards-compatibility.rst @@ -18,4 +18,3 @@ Deprecation Roadmap ------------------- We track deprecation and removal of features using milestones and the `deprecation `_ and `removal `_ labels on GitHub. - From 86a14d007da61d66f09df4729a699a2c91eae644 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 14 Sep 2018 21:13:58 -0300 Subject: [PATCH 12/31] Fix scope determination with indirect parameters Fix #3941 --- changelog/3941.bugfix.rst | 1 + src/_pytest/python.py | 15 +++++--- testing/python/metafunc.py | 79 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 changelog/3941.bugfix.rst diff --git a/changelog/3941.bugfix.rst b/changelog/3941.bugfix.rst new file mode 100644 index 00000000000..1cd3e843fab --- /dev/null +++ b/changelog/3941.bugfix.rst @@ -0,0 +1 @@ +Fix bug where indirect parametrization would consider the scope of all fixtures used by the test function to determine the parametrization scope, and not only the scope of the fixtures being parametrized. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 1976be80cb3..e83ea7a2691 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1141,13 +1141,18 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect): """ from _pytest.fixtures import scopes - indirect_as_list = isinstance(indirect, (list, tuple)) - all_arguments_are_fixtures = ( - indirect is True or indirect_as_list and len(indirect) == argnames - ) + if isinstance(indirect, (list, tuple)): + all_arguments_are_fixtures = len(indirect) == len(argnames) + else: + all_arguments_are_fixtures = bool(indirect) + if all_arguments_are_fixtures: fixturedefs = arg2fixturedefs or {} - used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()] + used_scopes = [ + fixturedef[0].scope + for name, fixturedef in fixturedefs.items() + if name in argnames + ] if used_scopes: # Takes the most narrow scope from used fixtures for scope in reversed(scopes): diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 7ff1e22831c..2172c5e0c93 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -132,6 +132,52 @@ def func(x): except ValueError as ve: assert "has an unsupported scope value 'doggy'" in str(ve) + def test_find_parametrized_scope(self): + """unittest for _find_parametrized_scope (#3941)""" + from _pytest.python import _find_parametrized_scope + + @attr.s + class DummyFixtureDef(object): + scope = attr.ib() + + fixtures_defs = dict( + session_fix=[DummyFixtureDef("session")], + package_fix=[DummyFixtureDef("package")], + module_fix=[DummyFixtureDef("module")], + class_fix=[DummyFixtureDef("class")], + func_fix=[DummyFixtureDef("function")], + ) + + # use arguments to determine narrow scope; the cause of the bug is that it would look on all + # fixture defs given to the method + def find_scope(argnames, indirect): + return _find_parametrized_scope(argnames, fixtures_defs, indirect=indirect) + + assert find_scope(["func_fix"], indirect=True) == "function" + assert find_scope(["class_fix"], indirect=True) == "class" + assert find_scope(["module_fix"], indirect=True) == "module" + assert find_scope(["package_fix"], indirect=True) == "package" + assert find_scope(["session_fix"], indirect=True) == "session" + + assert find_scope(["class_fix", "func_fix"], indirect=True) == "function" + assert find_scope(["func_fix", "session_fix"], indirect=True) == "function" + assert find_scope(["session_fix", "class_fix"], indirect=True) == "class" + assert find_scope(["package_fix", "session_fix"], indirect=True) == "package" + assert find_scope(["module_fix", "session_fix"], indirect=True) == "module" + + # when indirect is False or is not for all scopes, always use function + assert find_scope(["session_fix", "module_fix"], indirect=False) == "function" + assert ( + find_scope(["session_fix", "module_fix"], indirect=["module_fix"]) + == "function" + ) + assert ( + find_scope( + ["session_fix", "module_fix"], indirect=["session_fix", "module_fix"] + ) + == "module" + ) + def test_parametrize_and_id(self): def func(x, y): pass @@ -1383,6 +1429,39 @@ def test_2(animal, echo): result = testdir.runpytest() result.stdout.fnmatch_lines(["* 3 passed *"]) + def test_parametrize_some_arguments_auto_scope(self, testdir, monkeypatch): + """Integration test for (#3941)""" + class_fix_setup = [] + monkeypatch.setattr(sys, "class_fix_setup", class_fix_setup, raising=False) + func_fix_setup = [] + monkeypatch.setattr(sys, "func_fix_setup", func_fix_setup, raising=False) + + testdir.makepyfile( + """ + import pytest + import sys + + @pytest.fixture(scope='class', autouse=True) + def class_fix(request): + sys.class_fix_setup.append(request.param) + + @pytest.fixture(autouse=True) + def func_fix(): + sys.func_fix_setup.append(True) + + @pytest.mark.parametrize('class_fix', [10, 20], indirect=True) + class Test: + def test_foo(self): + pass + def test_bar(self): + pass + """ + ) + result = testdir.runpytest_inprocess() + result.stdout.fnmatch_lines(["* 4 passed in *"]) + assert func_fix_setup == [True] * 4 + assert class_fix_setup == [10, 20] + def test_parametrize_issue634(self, testdir): testdir.makepyfile( """ From 93224f8cf9986cf81373e1e9eec0b3d4ccd8a07d Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 17 Sep 2018 12:41:57 +0200 Subject: [PATCH 13/31] tox: remove obsolete whitelist_externals --- tox.ini | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tox.ini b/tox.ini index 452bdc60dd3..b919c7e8459 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,6 @@ envlist = docs [testenv] -whitelist_externals = env commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof -ra {posargs:testing} passenv = USER USERNAME @@ -58,7 +57,6 @@ deps = nose hypothesis>=3.56 {env:_PYTEST_TOX_EXTRA_DEP:} -whitelist_externals = env passenv = USER USERNAME TRAVIS commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:testing} @@ -79,7 +77,6 @@ platform = linux|darwin deps = pexpect {env:_PYTEST_TOX_EXTRA_DEP:} -whitelist_externals = env commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra test_pdb.py test_terminal.py test_unittest.py @@ -100,7 +97,6 @@ changedir=testing setenv = {[testenv]setenv} PYTHONDONTWRITEBYTECODE=1 -whitelist_externals = env passenv = USER USERNAME TRAVIS commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:.} @@ -109,7 +105,6 @@ commands = deps = twisted {env:_PYTEST_TOX_EXTRA_DEP:} -whitelist_externals = env commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra {posargs:testing/test_unittest.py} @@ -121,7 +116,6 @@ commands = {[testenv:py27-trial]commands} deps = numpy {env:_PYTEST_TOX_EXTRA_DEP:} -whitelist_externals = env commands= {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra {posargs:testing/python/approx.py} @@ -157,7 +151,6 @@ skipsdist = True deps = PyYAML {env:_PYTEST_TOX_EXTRA_DEP:} -whitelist_externals = env commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra doc/en {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest From 739f9a4a4b0ae2efe105f3a6561088dac5096941 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 17 Sep 2018 12:40:12 +0200 Subject: [PATCH 14/31] Travis: use codecov-bash Faster to install and will retry uploads on connection errors. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f00324edb0..dd7529d20c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -94,11 +94,11 @@ after_success: - | if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then set -e - pip install codecov + pip install coverage coverage combine coverage xml --ignore-errors coverage report -m --ignore-errors - codecov --required -X gcov pycov search -f coverage.xml --flags ${TOXENV//-/ } linux + bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux" # Coveralls does not support merged reports. if [[ "$TOXENV" = py37 ]]; then From 03eaad376b390964b650c2fec5fcee00f9870d4e Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 17 Sep 2018 12:41:06 +0200 Subject: [PATCH 15/31] tox: coverage factor: combine and report --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index b919c7e8459..1c77f989d24 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,8 @@ envlist = [testenv] commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof -ra {posargs:testing} + coverage: coverage combine + coverage: coverage report passenv = USER USERNAME setenv = # configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage" From 1df6d28080343749114eda89aa06c7fda6fe1d7f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 13 Sep 2018 18:50:05 -0300 Subject: [PATCH 16/31] Fix assertion rewriter crash if cwd changes mid-testing Unfortunately we need to get a `py.path.local` object to perform the fnmatch operation, it is different from the standard `fnmatch` module because it implements its own custom logic. So we need to use `py.path` to perform the fnmatch for backward compatibility reasons. Ideally we should be able to use a "pure path" in `pathlib` terms (a path not bound to the file system), but we don't have those in pylib. Fix #3973 --- changelog/3973.bugfix.rst | 1 + src/_pytest/assertion/rewrite.py | 6 +++++- testing/test_assertrewrite.py | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelog/3973.bugfix.rst diff --git a/changelog/3973.bugfix.rst b/changelog/3973.bugfix.rst new file mode 100644 index 00000000000..29c70b8403f --- /dev/null +++ b/changelog/3973.bugfix.rst @@ -0,0 +1 @@ +Fix crash of the assertion rewriter if a test changed the current working directory without restoring it afterwards. diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 738d6339663..2b3a0ab3861 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -199,7 +199,11 @@ def _early_rewrite_bailout(self, name, state): # For matching the name it must be as if it was a filename. parts[-1] = parts[-1] + ".py" - fn_pypath = py.path.local(os.path.sep.join(parts)) + try: + fn_pypath = py.path.local(os.path.sep.join(parts)) + except EnvironmentError: + return False + for pat in self.fnpats: # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based # on the name alone because we need to match against the full path diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index aaf3e47854e..394d30a05d4 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1232,3 +1232,27 @@ def test_simple_failure(): hook.fnpats[:] = ["tests/**.py"] assert hook.find_module("file") is not None assert self.find_module_calls == ["file"] + + @pytest.mark.skipif( + sys.platform.startswith("win32"), reason="cannot remove cwd on Windows" + ) + def test_cwd_changed(self, testdir): + testdir.makepyfile( + **{ + "test_bar.py": """ + import os + import shutil + import tempfile + + d = tempfile.mkdtemp() + os.chdir(d) + shutil.rmtree(d) + """, + "test_foo.py": """ + def test(): + pass + """, + } + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines("* 1 passed in *") From 37d24692664b0dc5166f8cfb5bbb407c211c97c1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 17 Sep 2018 20:01:01 -0300 Subject: [PATCH 17/31] Use a PurePath instance to do matching against patterns in assertion rewrite This way we don't need to have real file system path, which prevents the original #3973 bug. --- src/_pytest/assertion/rewrite.py | 19 ++------- src/_pytest/compat.py | 14 +++++-- src/_pytest/paths.py | 39 +++++++++++++++++- testing/test_paths.py | 69 ++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 testing/test_paths.py diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 2b3a0ab3861..3539bd55d5f 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -16,7 +16,8 @@ import py from _pytest.assertion import util - +from _pytest.compat import PurePath, spec_from_file_location +from _pytest.paths import fnmatch_ex # pytest caches rewritten pycs in __pycache__. if hasattr(imp, "get_tag"): @@ -45,14 +46,6 @@ def ast_Call(a, b, c): return ast.Call(a, b, c, None, None) -if sys.version_info >= (3, 4): - from importlib.util import spec_from_file_location -else: - - def spec_from_file_location(*_, **__): - return None - - class AssertionRewritingHook(object): """PEP302 Import hook which rewrites asserts.""" @@ -198,18 +191,14 @@ def _early_rewrite_bailout(self, name, state): return False # For matching the name it must be as if it was a filename. - parts[-1] = parts[-1] + ".py" - try: - fn_pypath = py.path.local(os.path.sep.join(parts)) - except EnvironmentError: - return False + path = PurePath(os.path.sep.join(parts) + ".py") for pat in self.fnpats: # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based # on the name alone because we need to match against the full path if os.path.dirname(pat): return False - if fn_pypath.fnmatch(pat): + if fnmatch_ex(pat, path): return False if self._is_marked_for_rewrite(name, state): diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index ea369ccf2af..02cad24ccea 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -23,7 +23,7 @@ # Only available in Python 3.4+ or as a backport enum = None -__all__ = ["Path"] +__all__ = ["Path", "PurePath"] _PY3 = sys.version_info > (3, 0) _PY2 = not _PY3 @@ -42,9 +42,9 @@ MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError" if PY36: - from pathlib import Path + from pathlib import Path, PurePath else: - from pathlib2 import Path + from pathlib2 import Path, PurePath if _PY3: @@ -56,6 +56,14 @@ from collections import Mapping, Sequence # noqa +if sys.version_info >= (3, 4): + from importlib.util import spec_from_file_location +else: + + def spec_from_file_location(*_, **__): + return None + + def _format_args(func): return str(signature(func)) diff --git a/src/_pytest/paths.py b/src/_pytest/paths.py index 7c0dc1ec166..52b2392d0a5 100644 --- a/src/_pytest/paths.py +++ b/src/_pytest/paths.py @@ -1,5 +1,11 @@ -from .compat import Path -from os.path import expanduser, expandvars, isabs +from os.path import expanduser, expandvars, isabs, sep +from posixpath import sep as posix_sep +import fnmatch +import sys + +import six + +from .compat import Path, PurePath def resolve_from_str(input, root): @@ -11,3 +17,32 @@ def resolve_from_str(input, root): return Path(input) else: return root.joinpath(input) + + +def fnmatch_ex(pattern, path): + """FNMatcher port from py.path.common which works with PurePath() instances. + + The difference between this algorithm and PurePath.match() is that the latter matches "**" glob expressions + for each part of the path, while this algorithm uses the whole path instead. + + For example: + "tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" with this algorithm, but not with + PurePath.match(). + + This algorithm was ported to keep backward-compatibility with existing settings which assume paths match according + this logic. + """ + path = PurePath(path) + iswin32 = sys.platform.startswith("win") + + if iswin32 and sep not in pattern and posix_sep in pattern: + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posix_sep, sep) + + if sep not in pattern: + name = path.name + else: + name = six.text_type(path) + return fnmatch.fnmatch(name, pattern) diff --git a/testing/test_paths.py b/testing/test_paths.py new file mode 100644 index 00000000000..2bb1335fbc1 --- /dev/null +++ b/testing/test_paths.py @@ -0,0 +1,69 @@ +import sys + +import py + +import pytest + +from _pytest.paths import fnmatch_ex + + +class TestPort: + """Test that our port of py.common.FNMatcher (fnmatch_ex) produces the same results as the + original py.path.local.fnmatch method. + """ + + @pytest.fixture(params=["pathlib", "py.path"]) + def match(self, request): + if request.param == "py.path": + + def match_(pattern, path): + return py.path.local(path).fnmatch(pattern) + + else: + assert request.param == "pathlib" + + def match_(pattern, path): + return fnmatch_ex(pattern, path) + + return match_ + + if sys.platform == "win32": + drv1 = "c:" + drv2 = "d:" + else: + drv1 = "/c" + drv2 = "/d" + + @pytest.mark.parametrize( + "pattern, path", + [ + ("*.py", "foo.py"), + ("*.py", "bar/foo.py"), + ("test_*.py", "foo/test_foo.py"), + ("tests/*.py", "tests/foo.py"), + (drv1 + "/*.py", drv1 + "/foo.py"), + (drv1 + "/foo/*.py", drv1 + "/foo/foo.py"), + ("tests/**/test*.py", "tests/foo/test_foo.py"), + ("tests/**/doc/test*.py", "tests/foo/bar/doc/test_foo.py"), + ("tests/**/doc/**/test*.py", "tests/foo/doc/bar/test_foo.py"), + ], + ) + def test_matching(self, match, pattern, path): + assert match(pattern, path) + + @pytest.mark.parametrize( + "pattern, path", + [ + ("*.py", "foo.pyc"), + ("*.py", "foo/foo.pyc"), + ("tests/*.py", "foo/foo.py"), + (drv1 + "/*.py", drv2 + "/foo.py"), + (drv1 + "/foo/*.py", drv2 + "/foo/foo.py"), + ("tests/**/test*.py", "tests/foo.py"), + ("tests/**/test*.py", "foo/test_foo.py"), + ("tests/**/doc/test*.py", "tests/foo/bar/doc/foo.py"), + ("tests/**/doc/test*.py", "tests/foo/bar/test_foo.py"), + ], + ) + def test_not_matching(self, match, pattern, path): + assert not match(pattern, path) From ccb90b5c46fb7de9aac269b414e829b8871123e5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 18 Sep 2018 20:56:40 -0300 Subject: [PATCH 18/31] [WIP] Introduce deprecations page fix #3996 --- CHANGELOG.rst | 27 +---- doc/en/backwards-compatibility.rst | 4 +- doc/en/contents.rst | 1 + doc/en/deprecations.rst | 176 +++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 22 deletions(-) create mode 100644 doc/en/deprecations.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fbe5dbb9922..40b329436a8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,19 +24,9 @@ pytest 3.8.0 (2018-09-05) Deprecations and Removals ------------------------- -- `#2452 `_: ``Config.warn`` has been deprecated, it should be replaced by calls to the standard ``warnings.warn``. - - ``Node.warn`` now supports two signatures: - - * ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. The warning - instance must be a ``PytestWarning`` or subclass instance. - - * ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to - the warning instance form above. - - ``RemovedInPytest4Warning`` and ``PytestExperimentalApiWarning`` are now part of the public API and should be accessed - using ``pytest.RemovedInPytest4Warning`` and ``pytest.PytestExperimentalApiWarning``. - +- `#2452 `_: ``Config.warn`` and ``Node.warn`` have been + deprecated, see ``_ for rationale and + examples. - `#3936 `_: ``@pytest.mark.filterwarnings`` second parameter is no longer regex-escaped, making it possible to actually use regular expressions to check the warning message. @@ -253,15 +243,10 @@ pytest 3.7.0 (2018-07-30) Deprecations and Removals ------------------------- -- `#2639 `_: ``pytest_namespace`` has been deprecated. - - See the documentation for ``pytest_namespace`` hook for suggestions on how to deal - with this in plugins which use this functionality. - +- `#2639 `_: ``pytest_namespace`` has been `deprecated `_. -- `#3661 `_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. It will be changed into an error in pytest ``4.0``. - This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model. +- `#3661 `_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. See `the documentation for rationale and examples `_. @@ -623,7 +608,7 @@ Deprecations and Removals `_) - Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py - files, because they "leak" to the entire directory tree. (`#3084 + files, because they "leak" to the entire directory tree. `See the docs `_ for the rationale behind this decision (`#3084 `_) diff --git a/doc/en/backwards-compatibility.rst b/doc/en/backwards-compatibility.rst index 3c30d040eef..56afd98afa7 100644 --- a/doc/en/backwards-compatibility.rst +++ b/doc/en/backwards-compatibility.rst @@ -17,4 +17,6 @@ When the deprecation expires (e.g. 4.0 is released), we won't remove the depreca Deprecation Roadmap ------------------- -We track deprecation and removal of features using milestones and the `deprecation `_ and `removal `_ labels on GitHub. +Features currently deprecated and removed in previous releases can be found in :ref:`deprecations`. + +We track future deprecation and removal of features using milestones and the `deprecation `_ and `removal `_ labels on GitHub. diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 9f1d8d85ac2..58e2324ffc9 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -39,6 +39,7 @@ Full pytest documentation bash-completion backwards-compatibility + deprecations historical-notes license contributing diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst new file mode 100644 index 00000000000..c8a1ffb515c --- /dev/null +++ b/doc/en/deprecations.rst @@ -0,0 +1,176 @@ +.. _deprecations: + +Deprecations and Removals +========================= + +This page lists all pytest features that are currently deprecated or have been removed in previous major releases. +The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives can be +used instead. + +Deprecated Features +------------------- + + +``Config.warn`` and ``Node.warn`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.8 + +Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning +system for its own warnings, so those two functions are now deprecated. + +``Config.warn`` should be replaced by calls to the standard ``warnings.warn``. + +``Node.warn`` now supports two signatures: + +* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. + The warning instance must be a PytestWarning or subclass. + +* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to the warning instance form above. + + +``pytest_namespace`` +~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.7 + +This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some +bug fixes and refactorings impossible. + +Example of usage: + +.. code-block:: python + + class MySymbol: + ... + + + def pytest_namespace(): + return {"my_symbol": MySymbol()} + + +Plugin authors relying on this hook should instead require that users now import the plugin modules directly (with an appropriate public API). + +As a stopgap measure, plugin authors may still inject their names into pytest's namespace, usually during ``pytest_configure``: + +.. code-block:: python + + import pytest + + + def pytest_configure(): + pytest.my_symbol = MySymbol() + + + +Calling fixtures directly +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.7 + +Calling a fixture function directly, as opposed to request them in a test function, is deprecated. + +For example: + +.. code-block:: python + + @pytest.fixture + def cell(): + return ... + + + @pytest.fixture + def full_cell(): + cell = cell() + cell.make_full() + return cell + +This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model. + +In those cases just request the function directly in the dependent fixture: + +.. code-block:: python + + @pytest.fixture + def cell(): + return ... + + + @pytest.fixture + def full_cell(cell): + cell.make_full() + return cell + + +record_xml_property +~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.5 + +The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which +can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run. + +This is just a matter of renaming the fixture as the API is the same: + +.. code-block:: python + + def test_foo(record_xml_property): + ... + +Change to: + +.. code-block:: python + + def test_foo(record_property): + ... + +pytest_plugins in non-top-level conftest files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.5 + +Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py +files because they will activate referenced plugins *globally*, which is surprising because for all other pytest +features ``conftest.py`` files are only *active* for tests at or below it. + + +Removed Features +---------------- + +As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after +an appropriate period of deprecation has passed. + + +Reinterpretation mode (``--assert=reinterp``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*Removed in version 3.0.* + +Reinterpretation mode has now been removed and only plain and rewrite +mode are available, consequently the ``--assert=reinterp`` option is +no longer available. This also means files imported from plugins or +``conftest.py`` will not benefit from improved assertions by +default, you should use ``pytest.register_assert_rewrite()`` to +explicitly turn on assertion rewriting for those files. + +Removed command-line options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*Removed in version 3.0.* + +The following deprecated commandline options were removed: + +* ``--genscript``: no longer supported; +* ``--no-assert``: use ``--assert=plain`` instead; +* ``--nomagic``: use ``--assert=plain`` instead; +* ``--report``: use ``-r`` instead; + +py.test-X* entry points +~~~~~~~~~~~~~~~~~~~~~~~ + +*Removed in version 3.0.* + +Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points +were never documented and a leftover from a pre-virtualenv era. These entry +points also created broken entry points in wheels, so removing them also +removes a source of confusion for users. From 28c9cc73217c3052575056c5cae3a69ad0ecf9bb Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 17 Sep 2018 12:00:03 +0200 Subject: [PATCH 19/31] coverage: use modules for source This should increase coverage for subprocesses, where previously `source` paths were used only from the config file, but not the initial `--source` argument. --- .coveragerc | 7 ++++++- .travis.yml | 2 +- scripts/prepare-coverage.bat | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 9ef95584334..97934dc3b06 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,9 @@ [run] -source = _pytest,testing +source = pytest,_pytest,testing/ parallel = 1 branch = 1 + +[paths] +source = src/ + .tox/*/lib/python*/site-packages/ + .tox\*\Lib\site-packages\ diff --git a/.travis.yml b/.travis.yml index dd7529d20c9..a319c092201 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,7 +84,7 @@ before_script: if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then export COVERAGE_FILE="$PWD/.coverage" export COVERAGE_PROCESS_START="$PWD/.coveragerc" - export _PYTEST_TOX_COVERAGE_RUN="coverage run --source {envsitepackagesdir}/_pytest/,{toxinidir}/testing -m" + export _PYTEST_TOX_COVERAGE_RUN="coverage run -m" export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess fi diff --git a/scripts/prepare-coverage.bat b/scripts/prepare-coverage.bat index fbf4da66a41..bff1e62b041 100644 --- a/scripts/prepare-coverage.bat +++ b/scripts/prepare-coverage.bat @@ -2,7 +2,7 @@ REM scripts called by AppVeyor to setup the environment variables to enable cove if not defined PYTEST_NO_COVERAGE ( set "COVERAGE_FILE=%CD%\.coverage" set "COVERAGE_PROCESS_START=%CD%\.coveragerc" - set "_PYTEST_TOX_COVERAGE_RUN=coverage run --source {envsitepackagesdir}/_pytest/,{toxinidir}/testing -m" + set "_PYTEST_TOX_COVERAGE_RUN=coverage run -m" set "_PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess" echo Coverage setup completed ) else ( From 1e2e65f0fa159f3a0e89fd548a64ce0b67f5d97e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 19 Sep 2018 08:20:23 -0300 Subject: [PATCH 20/31] Add references to the relevant Python issues --- src/_pytest/paths.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/_pytest/paths.py b/src/_pytest/paths.py index 52b2392d0a5..e5177e23178 100644 --- a/src/_pytest/paths.py +++ b/src/_pytest/paths.py @@ -31,6 +31,10 @@ def fnmatch_ex(pattern, path): This algorithm was ported to keep backward-compatibility with existing settings which assume paths match according this logic. + + References: + * https://bugs.python.org/issue29249 + * https://bugs.python.org/issue34731 """ path = PurePath(path) iswin32 = sys.platform.startswith("win") From 7f48f552c1f1e0e9a379fde629d9e6e3c9b89a23 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 19 Sep 2018 10:18:05 -0300 Subject: [PATCH 21/31] Fix linting --- src/_pytest/paths.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/paths.py b/src/_pytest/paths.py index e5177e23178..031ea6b26bd 100644 --- a/src/_pytest/paths.py +++ b/src/_pytest/paths.py @@ -31,7 +31,7 @@ def fnmatch_ex(pattern, path): This algorithm was ported to keep backward-compatibility with existing settings which assume paths match according this logic. - + References: * https://bugs.python.org/issue29249 * https://bugs.python.org/issue34731 From e7eb7e799bdfbd9ec167cdd0684291216ef169a3 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 19 Sep 2018 12:31:00 +0200 Subject: [PATCH 22/31] logging: del item.catch_log_handler only in teardown Without this caplog.record_tuples etc is not available anymore when using `--pdb`. --- changelog/3998.bugfix.rst | 1 + src/_pytest/logging.py | 2 +- testing/test_pdb.py | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 changelog/3998.bugfix.rst diff --git a/changelog/3998.bugfix.rst b/changelog/3998.bugfix.rst new file mode 100644 index 00000000000..9bb0fb7ad01 --- /dev/null +++ b/changelog/3998.bugfix.rst @@ -0,0 +1 @@ +Fix caplog's catch_log_handler not being available with --pdb diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index c9c65c4c18d..2d0f54a6454 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -445,8 +445,8 @@ def _runtest_for(self, item, when): try: yield # run test finally: - del item.catch_log_handler if when == "teardown": + del item.catch_log_handler del item.catch_log_handlers if self.print_logs: diff --git a/testing/test_pdb.py b/testing/test_pdb.py index ed1c49a1acf..246f514b4a9 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -397,6 +397,24 @@ def test_1(capsys): child.read() self.flush(child) + def test_pdb_with_caplog_on_pdb_invocation(self, testdir): + p1 = testdir.makepyfile( + """ + def test_1(capsys, caplog): + import logging + logging.getLogger(__name__).warning("some_warning") + assert 0 + """ + ) + child = testdir.spawn_pytest("--pdb %s" % str(p1)) + child.send("caplog.record_tuples\n") + child.expect_exact( + "[('test_pdb_with_caplog_on_pdb_invocation', 30, 'some_warning')]" + ) + child.sendeof() + child.read() + self.flush(child) + def test_set_trace_capturing_afterwards(self, testdir): p1 = testdir.makepyfile( """ From d1fa8ae08e52d5dbe966258746791dfbfd568c0a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 19 Sep 2018 12:52:10 -0300 Subject: [PATCH 23/31] Improve CHANGELOG entry --- changelog/3998.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3998.bugfix.rst b/changelog/3998.bugfix.rst index 9bb0fb7ad01..e696f7087bd 100644 --- a/changelog/3998.bugfix.rst +++ b/changelog/3998.bugfix.rst @@ -1 +1 @@ -Fix caplog's catch_log_handler not being available with --pdb +Fix issue that prevented some caplog properties (for example ``record_tuples``) from being available when entering the debugger with ``--pdb``. From e86b01e8314e56daba2db501bf9552413b45608b Mon Sep 17 00:00:00 2001 From: wim glenn Date: Wed, 19 Sep 2018 14:06:36 -0500 Subject: [PATCH 24/31] Update customize.rst --- doc/en/customize.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/customize.rst b/doc/en/customize.rst index 2c9c070ad5d..47b92ab9e60 100644 --- a/doc/en/customize.rst +++ b/doc/en/customize.rst @@ -32,7 +32,7 @@ Here's a summary what ``pytest`` uses ``rootdir`` for: class name, function name and parametrization (if any). * Is used by plugins as a stable location to store project/test run specific information; - for example, the internal :ref:`cache ` plugin creates a ``.cache`` subdirectory + for example, the internal :ref:`cache ` plugin creates a ``.pytest_cache`` subdirectory in ``rootdir`` to store its cross-test run state. Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or From c2841542af6e6c83edd1d799714eec2f7fcff316 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 19 Sep 2018 19:24:05 -0300 Subject: [PATCH 25/31] Introduce deprecations page fix #3996 --- changelog/3996.doc.rst | 3 + doc/en/deprecations.rst | 145 +++++++++++++++++++++++++++++++++++++++- doc/en/mark.rst | 2 + 3 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 changelog/3996.doc.rst diff --git a/changelog/3996.doc.rst b/changelog/3996.doc.rst new file mode 100644 index 00000000000..de7e6e93430 --- /dev/null +++ b/changelog/3996.doc.rst @@ -0,0 +1,3 @@ +New `Deprecations and Removals `_ page shows all currently +deprecated features, the rationale to do so, and alternatives to update your code. It also list features removed +from pytest in past major releases to help those with ancient pytest versions to upgrade. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index c8a1ffb515c..ee6c9919374 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -3,13 +3,16 @@ Deprecations and Removals ========================= -This page lists all pytest features that are currently deprecated or have been removed in previous major releases. -The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives can be -used instead. +This page lists all pytest features that are currently deprecated or have been removed in past major releases. +The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives +should be used instead. Deprecated Features ------------------- +Below is a complete list of all pytest features which are considered deprecated. Using those features will issue +:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using +:ref:`standard warning filters `. ``Config.warn`` and ``Node.warn`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -101,6 +104,14 @@ In those cases just request the function directly in the dependent fixture: cell.make_full() return cell +``Node.get_marker`` +~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.6 + +As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See +:ref:`the documentation ` on tips on how to update your code. + record_xml_property ~~~~~~~~~~~~~~~~~~~ @@ -133,6 +144,134 @@ Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py files because they will activate referenced plugins *globally*, which is surprising because for all other pytest features ``conftest.py`` files are only *active* for tests at or below it. +Metafunc.addcall +~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.3 + +:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use +:meth:`_pytest.python.Metafunc.parametrize` instead. + +marks in ``pytest.mark.parametrize`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.2 + +Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example: + +.. code-block:: python + + @pytest.mark.parametrize( + "a, b", [(3, 9), pytest.mark.xfail(reason="flaky")(6, 36), (10, 100)] + ) + def test_foo(a, b): + ... + +This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization +call. + +This was considered hard to read and understand, and also its implementation presented problems to the code preventing +further internal improvements in the marks architecture. + +To update the code, use ``pytest.param``: + +.. code-block:: python + + @pytest.mark.parametrize( + "a, b", + [(3, 9), pytest.param((6, 36), marks=pytest.mark.xfail(reason="flaky")), (10, 100)], + ) + def test_foo(a, b): + ... + + + +Passing command-line string to ``pytest.main()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.0 + +Passing a command-line string to ``pytest.main()`` is deprecated: + +.. code-block:: python + + pytest.main("-v -s") + +Pass a list instead: + +.. code-block:: python + + pytest.main(["-v", "-s"]) + + +By passing a string, users expect that pytest will interpret that command-line using the shell rules they are working +on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way. + + +``yield`` tests +~~~~~~~~~~~~~~~ + +.. deprecated:: 3.0 + +pytest supports ``yield``-style tests, where a test function actually ``yield`` functions and values +that are then turned into proper test methods. Example: + +.. code-block:: python + + def check(x, y): + assert x ** x == y + + + def test_squared(): + yield check, 2, 4 + yield check, 3, 9 + +This would result into two actual test functions being generated. + +This form of test function doesn't support fixtures properly, and users should switch to ``pytest.mark.parametrize``: + +.. code-block:: python + + @pytest.mark.parametrize("x, y", [(2, 4), (3, 9)]) + def test_squared(): + assert x ** x == y + + +``pytest_funcarg__`` prefix +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.0 + +In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix: + +.. code-block:: python + + def pytest_funcarg__data(): + return SomeData() + +Switch over to the ``@pytest.fixture`` decorator: + +.. code-block:: python + + @pytest.fixture + def data(): + return SomeData() + +[pytest] section in setup.cfg files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.0 + +``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]`` +to avoid conflicts with other distutils commands. + +Result log (``--result-log``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.0 + +The ``--resultlog`` command line option has been deprecated: it is little used +and there are more modern and better alternatives, for example `pytest-tap `_. Removed Features ---------------- diff --git a/doc/en/mark.rst b/doc/en/mark.rst index 8f247afa951..5d1cd00f4f4 100644 --- a/doc/en/mark.rst +++ b/doc/en/mark.rst @@ -52,6 +52,8 @@ should add ``--strict`` to ``addopts``: serial +.. _marker-revamp: + Marker revamp and iteration --------------------------- From 7122fa5613de8ef767eeb8106a5fee6dea0d9285 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 19 Sep 2018 09:44:26 -0700 Subject: [PATCH 26/31] Fix UnicodeDecodeError in assertion with mixed non-ascii bytes repr + text --- changelog/3999.bugfix.rst | 1 + src/_pytest/assertion/rewrite.py | 13 +++++++++---- testing/test_assertrewrite.py | 18 +++++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 changelog/3999.bugfix.rst diff --git a/changelog/3999.bugfix.rst b/changelog/3999.bugfix.rst new file mode 100644 index 00000000000..e072f729e6c --- /dev/null +++ b/changelog/3999.bugfix.rst @@ -0,0 +1 @@ +Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text. diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 3539bd55d5f..be8c6dc4df6 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -8,6 +8,7 @@ import os import re import six +import string import struct import sys import types @@ -466,10 +467,14 @@ def _saferepr(obj): """ r = py.io.saferepr(obj) - if isinstance(r, six.text_type): - return r.replace(u"\n", u"\\n") - else: - return r.replace(b"\n", b"\\n") + # only occurs in python2.x, repr must return text in python3+ + if isinstance(r, bytes): + # Represent unprintable bytes as `\x##` + r = u"".join( + u"\\x{:x}".format(ord(c)) if c not in string.printable else c.decode() + for c in r + ) + return r.replace(u"\n", u"\\n") from _pytest.assertion.util import format_explanation as _format_explanation # noqa diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 394d30a05d4..a2cd8e81c2e 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function import glob @@ -57,7 +58,7 @@ def getmsg(f, extra_ns=None, must_pass=False): except AssertionError: if must_pass: pytest.fail("shouldn't have raised") - s = str(sys.exc_info()[1]) + s = six.text_type(sys.exc_info()[1]) if not s.startswith("assert"): return "AssertionError: " + s return s @@ -608,6 +609,21 @@ def __repr__(self): assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0] + def test_custom_repr_non_ascii(self): + def f(): + class A(object): + name = u"รค" + + def __repr__(self): + return self.name.encode("UTF-8") # only legal in python2 + + a = A() + assert not a.name + + msg = getmsg(f) + assert "UnicodeDecodeError" not in msg + assert "UnicodeEncodeError" not in msg + class TestRewriteOnImport(object): def test_pycache_is_a_file(self, testdir): From 41f6ea13cecef7007dd13adc3c6a484b1eed4a20 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 20 Sep 2018 17:52:35 -0300 Subject: [PATCH 27/31] Fix 'Package has no len()' error during collection Fix #3749 --- changelog/3749.bugfix.rst | 3 +++ src/_pytest/main.py | 7 ++++--- .../collect/package_init_given_as_arg/pkg/__init__.py | 0 .../collect/package_init_given_as_arg/pkg/test_foo.py | 2 ++ testing/python/collect.py | 7 +++++++ 5 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 changelog/3749.bugfix.rst create mode 100644 testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py create mode 100644 testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py diff --git a/changelog/3749.bugfix.rst b/changelog/3749.bugfix.rst new file mode 100644 index 00000000000..395ff12f5ae --- /dev/null +++ b/changelog/3749.bugfix.rst @@ -0,0 +1,3 @@ +Fix the following error during collection of tests inside packages:: + + TypeError: object of type 'Package' has no len() diff --git a/src/_pytest/main.py b/src/_pytest/main.py index f5078b9e77f..ce07285a41a 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -504,13 +504,14 @@ def _collect(self, arg): pkginit = parent.join("__init__.py") if pkginit.isfile(): if pkginit in self._node_cache: - root = self._node_cache[pkginit] + root = self._node_cache[pkginit][0] else: col = root._collectfile(pkginit) if col: if isinstance(col[0], Package): root = col[0] - self._node_cache[root.fspath] = root + # always store a list in the cache, matchnodes expects it + self._node_cache[root.fspath] = [root] # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. @@ -530,8 +531,8 @@ def _collect(self, arg): if (type(x), x.fspath) in self._node_cache: yield self._node_cache[(type(x), x.fspath)] else: - yield x self._node_cache[(type(x), x.fspath)] = x + yield x else: assert argpath.check(file=1) diff --git a/testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py b/testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py b/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py new file mode 100644 index 00000000000..f174823854e --- /dev/null +++ b/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py @@ -0,0 +1,2 @@ +def test(): + pass diff --git a/testing/python/collect.py b/testing/python/collect.py index c92de12a09c..cbfc4a9d2ab 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1591,6 +1591,13 @@ def test_package_collection_infinite_recursion(testdir): result.stdout.fnmatch_lines("*1 passed*") +def test_package_collection_init_given_as_argument(testdir): + """Regression test for #3749""" + p = testdir.copy_example("collect/package_init_given_as_arg") + result = testdir.runpytest(p / "pkg" / "__init__.py") + result.stdout.fnmatch_lines("*1 passed*") + + def test_package_with_modules(testdir): """ . From 9b382ed16c72d16357cdc772b47a68634d5946df Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Fri, 21 Sep 2018 17:11:15 +0100 Subject: [PATCH 28/31] Fix typo in docstring --- src/_pytest/monkeypatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 22ffffd4cee..67279003f18 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -219,8 +219,8 @@ def setenv(self, name, value, prepend=None): self.setitem(os.environ, name, value) def delenv(self, name, raising=True): - """ Delete ``name`` from the environment. Raise KeyError it does not - exist. + """ Delete ``name`` from the environment. Raise KeyError if it does + not exist. If ``raising`` is set to False, no exception will be raised if the environment variable is missing. From 650c458df9b9386e69c8c66292450a299c27c653 Mon Sep 17 00:00:00 2001 From: William Jamir Silva Date: Fri, 21 Sep 2018 19:18:51 -0300 Subject: [PATCH 29/31] Include Python 3.7 on getting started doc Close #3932 --- doc/en/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 5393195d843..418d4f8cd0b 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -1,7 +1,7 @@ Installation and Getting Started =================================== -**Pythons**: Python 2.7, 3.4, 3.5, 3.6, Jython, PyPy-2.3 +**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3 **Platforms**: Unix/Posix and Windows From 3f6a46c2a47bc29993d07d47139ef101f7aa630f Mon Sep 17 00:00:00 2001 From: CrazyMerlyn Date: Sat, 22 Sep 2018 16:34:06 +0000 Subject: [PATCH 30/31] Preparing release version 3.8.1 --- CHANGELOG.rst | 45 +++++++++++++++++++++++++++++++ changelog/3286.bugfix.rst | 1 - changelog/3749.bugfix.rst | 3 --- changelog/3941.bugfix.rst | 1 - changelog/3955.trivial.rst | 1 - changelog/3973.bugfix.rst | 1 - changelog/3975.trivial.rst | 1 - changelog/3996.doc.rst | 3 --- changelog/3998.bugfix.rst | 1 - changelog/3999.bugfix.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-3.8.1.rst | 25 +++++++++++++++++ doc/en/example/parametrize.rst | 7 ++--- 13 files changed, 75 insertions(+), 16 deletions(-) delete mode 100644 changelog/3286.bugfix.rst delete mode 100644 changelog/3749.bugfix.rst delete mode 100644 changelog/3941.bugfix.rst delete mode 100644 changelog/3955.trivial.rst delete mode 100644 changelog/3973.bugfix.rst delete mode 100644 changelog/3975.trivial.rst delete mode 100644 changelog/3996.doc.rst delete mode 100644 changelog/3998.bugfix.rst delete mode 100644 changelog/3999.bugfix.rst create mode 100644 doc/en/announce/release-3.8.1.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 40b329436a8..d68a160ab7d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,51 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 3.8.1 (2018-09-22) +========================= + +Bug Fixes +--------- + +- `#3286 `_: ``.pytest_cache`` directory is now automatically ignored by Git. Users who would like to contribute a solution for other SCMs please consult/comment on this issue. + + +- `#3749 `_: Fix the following error during collection of tests inside packages:: + + TypeError: object of type 'Package' has no len() + + +- `#3941 `_: Fix bug where indirect parametrization would consider the scope of all fixtures used by the test function to determine the parametrization scope, and not only the scope of the fixtures being parametrized. + + +- `#3973 `_: Fix crash of the assertion rewriter if a test changed the current working directory without restoring it afterwards. + + +- `#3998 `_: Fix issue that prevented some caplog properties (for example ``record_tuples``) from being available when entering the debugger with ``--pdb``. + + +- `#3999 `_: Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text. + + + +Improved Documentation +---------------------- + +- `#3996 `_: New `Deprecations and Removals `_ page shows all currently + deprecated features, the rationale to do so, and alternatives to update your code. It also list features removed + from pytest in past major releases to help those with ancient pytest versions to upgrade. + + + +Trivial/Internal Changes +------------------------ + +- `#3955 `_: Improve pre-commit detection for changelog filenames + + +- `#3975 `_: Remove legacy code around im_func as that was python2 only + + pytest 3.8.0 (2018-09-05) ========================= diff --git a/changelog/3286.bugfix.rst b/changelog/3286.bugfix.rst deleted file mode 100644 index d69e085319b..00000000000 --- a/changelog/3286.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -``.pytest_cache`` directory is now automatically ignored by Git. Users who would like to contribute a solution for other SCMs please consult/comment on this issue. diff --git a/changelog/3749.bugfix.rst b/changelog/3749.bugfix.rst deleted file mode 100644 index 395ff12f5ae..00000000000 --- a/changelog/3749.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix the following error during collection of tests inside packages:: - - TypeError: object of type 'Package' has no len() diff --git a/changelog/3941.bugfix.rst b/changelog/3941.bugfix.rst deleted file mode 100644 index 1cd3e843fab..00000000000 --- a/changelog/3941.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bug where indirect parametrization would consider the scope of all fixtures used by the test function to determine the parametrization scope, and not only the scope of the fixtures being parametrized. diff --git a/changelog/3955.trivial.rst b/changelog/3955.trivial.rst deleted file mode 100644 index 2c7cd550f44..00000000000 --- a/changelog/3955.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Improve pre-commit detection for changelog filenames diff --git a/changelog/3973.bugfix.rst b/changelog/3973.bugfix.rst deleted file mode 100644 index 29c70b8403f..00000000000 --- a/changelog/3973.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash of the assertion rewriter if a test changed the current working directory without restoring it afterwards. diff --git a/changelog/3975.trivial.rst b/changelog/3975.trivial.rst deleted file mode 100644 index 1013e13b983..00000000000 --- a/changelog/3975.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Remove legacy code around im_func as that was python2 only diff --git a/changelog/3996.doc.rst b/changelog/3996.doc.rst deleted file mode 100644 index de7e6e93430..00000000000 --- a/changelog/3996.doc.rst +++ /dev/null @@ -1,3 +0,0 @@ -New `Deprecations and Removals `_ page shows all currently -deprecated features, the rationale to do so, and alternatives to update your code. It also list features removed -from pytest in past major releases to help those with ancient pytest versions to upgrade. diff --git a/changelog/3998.bugfix.rst b/changelog/3998.bugfix.rst deleted file mode 100644 index e696f7087bd..00000000000 --- a/changelog/3998.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix issue that prevented some caplog properties (for example ``record_tuples``) from being available when entering the debugger with ``--pdb``. diff --git a/changelog/3999.bugfix.rst b/changelog/3999.bugfix.rst deleted file mode 100644 index e072f729e6c..00000000000 --- a/changelog/3999.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 1eaae502a17..cdedd3ca73d 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-3.8.1 release-3.8.0 release-3.7.4 release-3.7.3 diff --git a/doc/en/announce/release-3.8.1.rst b/doc/en/announce/release-3.8.1.rst new file mode 100644 index 00000000000..3e05e58cb3f --- /dev/null +++ b/doc/en/announce/release-3.8.1.rst @@ -0,0 +1,25 @@ +pytest-3.8.1 +======================================= + +pytest 3.8.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: + +* Ankit Goel +* Anthony Sottile +* Bruno Oliveira +* Daniel Hahler +* Maximilian Albert +* Ronny Pfannschmidt +* William Jamir Silva +* wim glenn + + +Happy testing, +The pytest Development Team diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 7dca5510ac1..5af269bba64 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -411,10 +411,11 @@ is to be run with different sets of arguments for its three arguments: Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize):: . $ pytest -rs -q multipython.py - ...sss...sssssssss...sss... [100%] + ...ssssssssssssssssssssssss [100%] ========================= short test summary info ========================== - SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found - 12 passed, 15 skipped in 0.12 seconds + SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found + SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found + 3 passed, 24 skipped in 0.12 seconds Indirect parametrization of optional implementations/imports -------------------------------------------------------------------- From fcc5b6d60407d13b068e84a33744844cc7010a1c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 22 Sep 2018 18:43:22 -0300 Subject: [PATCH 31/31] Add "deprecation" to possible changelog entries in pre-commit --- .pre-commit-config.yaml | 2 +- changelog/{3964.rst => 3964.feature.rst} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename changelog/{3964.rst => 3964.feature.rst} (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f0340fb9ddb..5a1dd5ee60d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,5 +41,5 @@ repos: name: changelog filenames language: fail entry: 'changelog files must be named ####.(feature|bugfix|doc|removal|vendor|trivial).rst' - exclude: changelog/(\d+\.(feature|bugfix|doc|removal|vendor|trivial).rst|README.rst|_template.rst) + exclude: changelog/(\d+\.(feature|bugfix|doc|deprecation|removal|vendor|trivial).rst|README.rst|_template.rst) files: ^changelog/ diff --git a/changelog/3964.rst b/changelog/3964.feature.rst similarity index 100% rename from changelog/3964.rst rename to changelog/3964.feature.rst