diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5d2fa4b3825..504b0f51777 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,45 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +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 741164eb67b..00000000000 --- 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 392de636390..00000000000 --- 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 75e01a88b1d..00000000000 --- 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 3ac77b8fe7d..00000000000 --- 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 ec887768ddd..00000000000 --- 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 a601b247e22..5fe3cc78c5e 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + 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 00000000000..f61d7022232 --- /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/customize.rst b/doc/en/customize.rst index 3471463a310..89192237354 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 640106ccd98..0b94e425c54 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 1c06782f631..eb978c5eaba 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 71ac77f7b83..a7cd06d31fc 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 2bdd68ea355..97347f12643 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 c32205f13ad..e6f91cdf781 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 b6df27f9cc2..9c3a4c73175 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 755783d2adf..7fdeda46404 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/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 11c7bdf6f85..c2a4e446ffb 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -8,6 +8,7 @@ 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 3d1531e77c5..3898fb252a1 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -354,3 +354,9 @@ def funcargnames(self): 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 f8cf55b4cb6..2cab96d675a 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -8,6 +8,7 @@ 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 @@ def __repr__(self): 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 94058f70cba..0f346074184 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 7d8b74a806e..fce4c1e3f21 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 d7f94ce4590..a03efb0cf2c 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -88,3 +88,30 @@ def get_write_msg(self, idx): 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 4ae24b15a6d..7c871a9eead 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_arg1(arg1): ''' ) - 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 0321939a8aa..e323ba24011 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_arg(arg): """ ) - 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 bf23e32023a..8fce5e279bb 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -9,6 +9,7 @@ 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 @@ def test_attrs_with_attribute_comparison_off(self): @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 dd7bc875387..d39065b8270 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 15360826f95..e95b011b6f7 100644 --- a/tox.ini +++ b/tox.ini @@ -77,7 +77,7 @@ commands = [testenv:regen] changedir = doc/en skipsdist = True -basepython = python3.6 +basepython = python3 deps = sphinx PyYAML @@ -103,7 +103,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 +116,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 =