diff --git a/.travis.yml b/.travis.yml index 5de40f3a4..c713a19a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,8 +49,9 @@ jobs: # - pytester's LsofFdLeakChecker # - TestArgComplete (linux only) # - numpy + # - old attrs # Empty PYTEST_ADDOPTS to run this non-verbose. - - env: TOXENV=py37-lsof-numpy-twisted-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS= + - env: TOXENV=py37-lsof-oldattrs-numpy-twisted-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS= # Specialized factors for py37. # Coverage for: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5d2fa4b38..c03fa631e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,54 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 5.2.1 (2019-10-06) +========================= + +Bug Fixes +--------- + +- `#5902 `_: Fix warnings about deprecated ``cmp`` attribute in ``attrs>=19.2``. + + +pytest 5.2.0 (2019-09-28) +========================= + +Deprecations +------------ + +- `#1682 `_: Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them + as a keyword argument instead. + + + +Features +-------- + +- `#1682 `_: The ``scope`` parameter of ``@pytest.fixture`` can now be a callable that receives + the fixture name and the ``config`` object as keyword-only parameters. + See `the docs `__ for more information. + + +- `#5764 `_: New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run + + + +Bug Fixes +--------- + +- `#5806 `_: Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text". + + +- `#5884 `_: Fix ``--setup-only`` and ``--setup-show`` for custom pytest items. + + + +Trivial/Internal Changes +------------------------ + +- `#5056 `_: The HelpFormatter uses ``py.io.get_terminal_width`` for better width detection. + + pytest 5.1.3 (2019-09-18) ========================= diff --git a/changelog/1682.deprecation.rst b/changelog/1682.deprecation.rst deleted file mode 100644 index 741164eb6..000000000 --- a/changelog/1682.deprecation.rst +++ /dev/null @@ -1,2 +0,0 @@ -Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them -as a keyword argument instead. diff --git a/changelog/1682.feature.rst b/changelog/1682.feature.rst deleted file mode 100644 index 392de6363..000000000 --- a/changelog/1682.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``scope`` parameter of ``@pytest.fixture`` can now be a callable that receives -the fixture name and the ``config`` object as keyword-only parameters. -See `the docs `__ for more information. diff --git a/changelog/5056.trivial.rst b/changelog/5056.trivial.rst deleted file mode 100644 index 75e01a88b..000000000 --- a/changelog/5056.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -The HelpFormatter uses ``py.io.get_terminal_width`` for better width detection. diff --git a/changelog/5764.feature.rst b/changelog/5764.feature.rst deleted file mode 100644 index 3ac77b8fe..000000000 --- a/changelog/5764.feature.rst +++ /dev/null @@ -1 +0,0 @@ -New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run diff --git a/changelog/5806.bugfix.rst b/changelog/5806.bugfix.rst deleted file mode 100644 index ec887768d..000000000 --- a/changelog/5806.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text". diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index a601b247e..d5e684198 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,8 @@ Release announcements :maxdepth: 2 + release-5.2.1 + release-5.2.0 release-5.1.3 release-5.1.2 release-5.1.1 diff --git a/doc/en/announce/release-5.2.0.rst b/doc/en/announce/release-5.2.0.rst new file mode 100644 index 000000000..f61d70222 --- /dev/null +++ b/doc/en/announce/release-5.2.0.rst @@ -0,0 +1,36 @@ +pytest-5.2.0 +======================================= + +The pytest team is proud to announce the 5.2.0 release! + +pytest is a mature Python testing tool with more than a 2000 tests +against itself, passing on many different interpreters and platforms. + +This release contains a number of bugs fixes and improvements, so users are encouraged +to take a look at the CHANGELOG: + + https://docs.pytest.org/en/latest/changelog.html + +For complete documentation, please visit: + + https://docs.pytest.org/en/latest/ + +As usual, you can upgrade from pypi via: + + pip install -U pytest + +Thanks to all who contributed to this release, among them: + +* Andrzej Klajnert +* Anthony Sottile +* Bruno Oliveira +* Daniel Hahler +* James Cooke +* Michael Goerz +* Ran Benita +* Tomáš Chvátal +* aklajnert + + +Happy testing, +The Pytest Development Team diff --git a/doc/en/announce/release-5.2.1.rst b/doc/en/announce/release-5.2.1.rst new file mode 100644 index 000000000..312cfd778 --- /dev/null +++ b/doc/en/announce/release-5.2.1.rst @@ -0,0 +1,23 @@ +pytest-5.2.1 +======================================= + +pytest 5.2.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: + +* Anthony Sottile +* Bruno Oliveira +* Florian Bruhin +* Hynek Schlawack +* Kevin J. Foley +* tadashigaki + + +Happy testing, +The pytest Development Team diff --git a/doc/en/customize.rst b/doc/en/customize.rst index 3471463a3..891922373 100644 --- a/doc/en/customize.rst +++ b/doc/en/customize.rst @@ -134,10 +134,13 @@ progress output, you can write it into a configuration file: .. code-block:: ini # content of pytest.ini or tox.ini - # setup.cfg files should use [tool:pytest] section instead [pytest] addopts = -ra -q + # content of setup.cfg + [tool:pytest] + addopts = -ra -q + Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command line options while the environment is in use: diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 640106ccd..0b94e425c 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -475,10 +475,10 @@ Running it results in some skips if we don't have all the python interpreters in .. code-block:: pytest . $ pytest -rs -q multipython.py - ssssssssssss...ssssssssssss [100%] + ssssssssssssssssssssssss... [100%] ========================= short test summary info ========================== SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.5' not found - SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.7' not found + SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.6' not found 3 passed, 24 skipped in 0.12s Indirect parametrization of optional implementations/imports diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 1c06782f6..eb978c5ea 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: items = [1, 2, 3] print("items is {!r}".format(items)) > a, b = items.pop() - E TypeError: 'int' object is not iterable + E TypeError: cannot unpack non-iterable int object failure_demo.py:181: TypeError --------------------------- Captured stdout call --------------------------- @@ -516,7 +516,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: def test_z2_type_error(self): items = 3 > a, b = items - E TypeError: 'int' object is not iterable + E TypeError: cannot unpack non-iterable int object failure_demo.py:222: TypeError ______________________ TestMoreErrors.test_startswith ______________________ diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 71ac77f7b..a7cd06d31 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -445,7 +445,7 @@ Now we can profile which test functions execute the slowest: ========================= slowest 3 test durations ========================= 0.30s call test_some_are_slow.py::test_funcslow2 - 0.21s call test_some_are_slow.py::test_funcslow1 + 0.20s call test_some_are_slow.py::test_funcslow1 0.10s call test_some_are_slow.py::test_funcfast ============================ 3 passed in 0.12s ============================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 2bdd68ea3..97347f126 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py + This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest.py .. _`simpletest`: diff --git a/doc/en/logging.rst b/doc/en/logging.rst index c32205f13..e6f91cdf7 100644 --- a/doc/en/logging.rst +++ b/doc/en/logging.rst @@ -161,7 +161,7 @@ the records for the ``setup`` and ``call`` stages during teardown like so: yield window for when in ("setup", "call"): messages = [ - x.message for x in caplog.get_records(when) if x.level == logging.WARNING + x.message for x in caplog.get_records(when) if x.levelno == logging.WARNING ] if messages: pytest.fail( diff --git a/doc/en/reference.rst b/doc/en/reference.rst index b6df27f9c..9c3a4c731 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -1003,7 +1003,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] addopts = --maxfail=2 -rf # exit after 2 failures, report fail info - issuing ``pytest test_hello.py`` actually means:: + issuing ``pytest test_hello.py`` actually means: .. code-block:: bash diff --git a/scripts/report-coverage.sh b/scripts/report-coverage.sh index 755783d2a..7fdeda464 100755 --- a/scripts/report-coverage.sh +++ b/scripts/report-coverage.sh @@ -13,4 +13,5 @@ fi python -m coverage combine python -m coverage xml python -m coverage report -m -bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml +curl -S -L --retry 6 -s https://codecov.io/bash -o codecov-upload.sh +bash codecov-upload.sh -Z -X fix -f coverage.xml diff --git a/setup.py b/setup.py index adbafb557..dcf63f6fd 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup INSTALL_REQUIRES = [ "py>=1.5.0", "packaging", - "attrs>=17.4.0", + "attrs>=17.4.0", # should match oldattrs tox env. "more-itertools>=4.0.0", "atomicwrites>=1.0", 'pathlib2>=2.2.0;python_version<"3.6"', diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 11c7bdf6f..c2a4e446f 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -8,6 +8,7 @@ from typing import Optional import _pytest._code from _pytest import outcomes from _pytest._io.saferepr import saferepr +from _pytest.compat import ATTRS_EQ_FIELD # The _reprcompare attribute on the util module is used by the new assertion # interpretation code and assertion rewriter to detect this plugin was @@ -375,7 +376,9 @@ def _compare_eq_cls(left, right, verbose, type_fns): fields_to_check = [field for field, info in all_fields.items() if info.compare] elif isattrs(left): all_fields = left.__attrs_attrs__ - fields_to_check = [field.name for field in all_fields if field.cmp] + fields_to_check = [ + field.name for field in all_fields if getattr(field, ATTRS_EQ_FIELD) + ] same = [] diff = [] diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 3d1531e77..3898fb252 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -354,3 +354,9 @@ if sys.version_info < (3, 5, 2): # pragma: no cover def overload(f): # noqa: F811 return f + + +if getattr(attr, "__version_info__", ()) >= (19, 2): + ATTRS_EQ_FIELD = "eq" +else: + ATTRS_EQ_FIELD = "cmp" diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index f8cf55b4c..2cab96d67 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -8,6 +8,7 @@ from typing import Set import attr from ..compat import ascii_escaped +from ..compat import ATTRS_EQ_FIELD from ..compat import getfslineno from ..compat import NOTSET from _pytest.outcomes import fail @@ -367,7 +368,8 @@ class NodeKeywords(MutableMapping): return "".format(self.node) -@attr.s(cmp=False, hash=False) +# mypy cannot find this overload, remove when on attrs>=19.2 +@attr.s(hash=False, **{ATTRS_EQ_FIELD: False}) # type: ignore class NodeMarkers: """ internal structure for storing marks belonging to a node diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 94058f70c..0f3460741 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -351,15 +351,14 @@ class RunResult: Attributes: - :ret: the return value - :outlines: list of lines captured from stdout - :errlines: list of lines captures from stderr - :stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to + :ivar ret: the return value + :ivar outlines: list of lines captured from stdout + :ivar errlines: list of lines captured from stderr + :ivar stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to reconstruct stdout or the commonly used ``stdout.fnmatch_lines()`` method - :stderr: :py:class:`LineMatcher` of stderr - :duration: duration in seconds - + :ivar stderr: :py:class:`LineMatcher` of stderr + :ivar duration: duration in seconds """ def __init__(self, ret, outlines, errlines, duration): @@ -454,9 +453,9 @@ class Testdir: Attributes: - :tmpdir: The :py:class:`py.path.local` instance of the temporary directory. + :ivar tmpdir: The :py:class:`py.path.local` instance of the temporary directory. - :plugins: A list of plugins to use with :py:meth:`parseconfig` and + :ivar plugins: A list of plugins to use with :py:meth:`parseconfig` and :py:meth:`runpytest`. Initially this is an empty list but plugins can be added to the list. The type of items to add to the list depends on the method using them so refer to them for details. diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 7d8b74a80..fce4c1e3f 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -107,8 +107,8 @@ def show_test_item(item): tw = item.config.get_terminal_writer() tw.line() tw.write(" " * 8) - tw.write(item._nodeid) - used_fixtures = sorted(item._fixtureinfo.name2fixturedefs.keys()) + tw.write(item.nodeid) + used_fixtures = sorted(getattr(item, "fixturenames", [])) if used_fixtures: tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) diff --git a/testing/conftest.py b/testing/conftest.py index d7f94ce45..a03efb0cf 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -88,3 +88,30 @@ def tw_mock(): fullwidth = 80 return TWMock() + + +@pytest.fixture +def dummy_yaml_custom_test(testdir): + """Writes a conftest file that collects and executes a dummy yaml test. + + Taken from the docs, but stripped down to the bare minimum, useful for + tests which needs custom items collected. + """ + testdir.makeconftest( + """ + import pytest + + def pytest_collect_file(parent, path): + if path.ext == ".yaml" and path.basename.startswith("test"): + return YamlFile(path, parent) + + class YamlFile(pytest.File): + def collect(self): + yield YamlItem(self.fspath.basename, self) + + class YamlItem(pytest.Item): + def runtest(self): + pass + """ + ) + testdir.makefile(".yaml", test1="") diff --git a/testing/python/setup_only.py b/testing/python/setup_only.py index 4ae24b15a..7c871a9ee 100644 --- a/testing/python/setup_only.py +++ b/testing/python/setup_only.py @@ -6,8 +6,8 @@ def mode(request): return request.param -def test_show_only_active_fixtures(testdir, mode): - p = testdir.makepyfile( +def test_show_only_active_fixtures(testdir, mode, dummy_yaml_custom_test): + testdir.makepyfile( ''' import pytest @pytest.fixture @@ -21,7 +21,7 @@ def test_show_only_active_fixtures(testdir, mode): ''' ) - result = testdir.runpytest(mode, p) + result = testdir.runpytest(mode) assert result.ret == 0 result.stdout.fnmatch_lines( diff --git a/testing/python/setup_plan.py b/testing/python/setup_plan.py index 0321939a8..e323ba240 100644 --- a/testing/python/setup_plan.py +++ b/testing/python/setup_plan.py @@ -1,6 +1,6 @@ -def test_show_fixtures_and_test(testdir): +def test_show_fixtures_and_test(testdir, dummy_yaml_custom_test): """ Verifies that fixtures are not executed. """ - p = testdir.makepyfile( + testdir.makepyfile( """ import pytest @pytest.fixture @@ -11,7 +11,7 @@ def test_show_fixtures_and_test(testdir): """ ) - result = testdir.runpytest("--setup-plan", p) + result = testdir.runpytest("--setup-plan") assert result.ret == 0 result.stdout.fnmatch_lines( diff --git a/testing/test_assertion.py b/testing/test_assertion.py index bf23e3202..8fce5e279 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -9,6 +9,7 @@ import pytest from _pytest import outcomes from _pytest.assertion import truncate from _pytest.assertion import util +from _pytest.compat import ATTRS_EQ_FIELD def mock_config(): @@ -687,7 +688,7 @@ class TestAssert_reprcompare_attrsclass: @attr.s class SimpleDataObject: field_a = attr.ib() - field_b = attr.ib(cmp=False) + field_b = attr.ib(**{ATTRS_EQ_FIELD: False}) left = SimpleDataObject(1, "b") right = SimpleDataObject(1, "b") diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index dd7bc8753..d39065b82 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -1,6 +1,7 @@ import argparse import distutils.spawn import os +import shlex import sys import py @@ -298,7 +299,11 @@ def test_argcomplete(testdir, monkeypatch): # redirect output from argcomplete to stdin and stderr is not trivial # http://stackoverflow.com/q/12589419/1307905 # so we use bash - fp.write('COMP_WORDBREAKS="$COMP_WORDBREAKS" python -m pytest 8>&1 9>&2') + fp.write( + 'COMP_WORDBREAKS="$COMP_WORDBREAKS" {} -m pytest 8>&1 9>&2'.format( + shlex.quote(sys.executable) + ) + ) # alternative would be exteneded Testdir.{run(),_run(),popen()} to be able # to handle a keyword argument env that replaces os.environ in popen or # extends the copy, advantage: could not forget to restore diff --git a/tox.ini b/tox.ini index 15360826f..e1b611f58 100644 --- a/tox.ini +++ b/tox.ini @@ -41,6 +41,8 @@ setenv = xdist: _PYTEST_TOX_POSARGS_XDIST=-n auto extras = testing deps = + oldattrs: attrs==17.4.0 + oldattrs: hypothesis<=4.38.1 numpy: numpy pexpect: pexpect pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master @@ -77,7 +79,7 @@ commands = [testenv:regen] changedir = doc/en skipsdist = True -basepython = python3.6 +basepython = python3 deps = sphinx PyYAML @@ -103,7 +105,7 @@ commands = [testenv:release] decription = do a release, required posarg of the version number -basepython = python3.6 +basepython = python3 usedevelop = True passenv = * deps = @@ -116,7 +118,7 @@ commands = python scripts/release.py {posargs} [testenv:publish_gh_release_notes] description = create GitHub release after deployment -basepython = python3.6 +basepython = python3 usedevelop = True passenv = GH_RELEASE_NOTES_TOKEN TRAVIS_TAG TRAVIS_REPO_SLUG deps =