Merge pull request #5923 from nicoddemus/mm

Merge master into features
This commit is contained in:
Daniel Hahler 2019-10-06 23:01:19 +02:00 committed by GitHub
commit b847d5712b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 195 additions and 44 deletions

View File

@ -49,8 +49,9 @@ jobs:
# - pytester's LsofFdLeakChecker # - pytester's LsofFdLeakChecker
# - TestArgComplete (linux only) # - TestArgComplete (linux only)
# - numpy # - numpy
# - old attrs
# Empty PYTEST_ADDOPTS to run this non-verbose. # 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. # Specialized factors for py37.
# Coverage for: # Coverage for:

View File

@ -18,6 +18,54 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start .. towncrier release notes start
pytest 5.2.1 (2019-10-06)
=========================
Bug Fixes
---------
- `#5902 <https://github.com/pytest-dev/pytest/issues/5902>`_: Fix warnings about deprecated ``cmp`` attribute in ``attrs>=19.2``.
pytest 5.2.0 (2019-09-28)
=========================
Deprecations
------------
- `#1682 <https://github.com/pytest-dev/pytest/issues/1682>`_: Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them
as a keyword argument instead.
Features
--------
- `#1682 <https://github.com/pytest-dev/pytest/issues/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 <https://docs.pytest.org/en/latest/fixture.html#dynamic-scope>`__ for more information.
- `#5764 <https://github.com/pytest-dev/pytest/issues/5764>`_: New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run
Bug Fixes
---------
- `#5806 <https://github.com/pytest-dev/pytest/issues/5806>`_: Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text".
- `#5884 <https://github.com/pytest-dev/pytest/issues/5884>`_: Fix ``--setup-only`` and ``--setup-show`` for custom pytest items.
Trivial/Internal Changes
------------------------
- `#5056 <https://github.com/pytest-dev/pytest/issues/5056>`_: The HelpFormatter uses ``py.io.get_terminal_width`` for better width detection.
pytest 5.1.3 (2019-09-18) pytest 5.1.3 (2019-09-18)
========================= =========================

View File

@ -1,2 +0,0 @@
Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them
as a keyword argument instead.

View File

@ -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 <https://docs.pytest.org/en/fixture.html#dynamic-scope>`__ for more information.

View File

@ -1 +0,0 @@
The HelpFormatter uses ``py.io.get_terminal_width`` for better width detection.

View File

@ -1 +0,0 @@
New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run

View File

@ -1 +0,0 @@
Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text".

View File

@ -6,6 +6,8 @@ Release announcements
:maxdepth: 2 :maxdepth: 2
release-5.2.1
release-5.2.0
release-5.1.3 release-5.1.3
release-5.1.2 release-5.1.2
release-5.1.1 release-5.1.1

View File

@ -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

View File

@ -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

View File

@ -134,10 +134,13 @@ progress output, you can write it into a configuration file:
.. code-block:: ini .. code-block:: ini
# content of pytest.ini or tox.ini # content of pytest.ini or tox.ini
# setup.cfg files should use [tool:pytest] section instead
[pytest] [pytest]
addopts = -ra -q addopts = -ra -q
# content of setup.cfg
[tool:pytest]
addopts = -ra -q
Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command
line options while the environment is in use: line options while the environment is in use:

View File

@ -475,10 +475,10 @@ Running it results in some skips if we don't have all the python interpreters in
.. code-block:: pytest .. code-block:: pytest
. $ pytest -rs -q multipython.py . $ pytest -rs -q multipython.py
ssssssssssss...ssssssssssss [100%] ssssssssssssssssssssssss... [100%]
========================= short test summary info ========================== ========================= 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.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 3 passed, 24 skipped in 0.12s
Indirect parametrization of optional implementations/imports Indirect parametrization of optional implementations/imports

View File

@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
items = [1, 2, 3] items = [1, 2, 3]
print("items is {!r}".format(items)) print("items is {!r}".format(items))
> a, b = items.pop() > a, b = items.pop()
E TypeError: 'int' object is not iterable E TypeError: cannot unpack non-iterable int object
failure_demo.py:181: TypeError failure_demo.py:181: TypeError
--------------------------- Captured stdout call --------------------------- --------------------------- 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): def test_z2_type_error(self):
items = 3 items = 3
> a, b = items > a, b = items
E TypeError: 'int' object is not iterable E TypeError: cannot unpack non-iterable int object
failure_demo.py:222: TypeError failure_demo.py:222: TypeError
______________________ TestMoreErrors.test_startswith ______________________ ______________________ TestMoreErrors.test_startswith ______________________

View File

@ -445,7 +445,7 @@ Now we can profile which test functions execute the slowest:
========================= slowest 3 test durations ========================= ========================= slowest 3 test durations =========================
0.30s call test_some_are_slow.py::test_funcslow2 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 0.10s call test_some_are_slow.py::test_funcfast
============================ 3 passed in 0.12s ============================= ============================ 3 passed in 0.12s =============================

View File

@ -28,7 +28,7 @@ Install ``pytest``
.. code-block:: bash .. code-block:: bash
$ pytest --version $ 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`: .. _`simpletest`:

View File

@ -161,7 +161,7 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
yield window yield window
for when in ("setup", "call"): for when in ("setup", "call"):
messages = [ 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: if messages:
pytest.fail( pytest.fail(

View File

@ -1003,7 +1003,7 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest] [pytest]
addopts = --maxfail=2 -rf # exit after 2 failures, report fail info 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 .. code-block:: bash

View File

@ -13,4 +13,5 @@ fi
python -m coverage combine python -m coverage combine
python -m coverage xml python -m coverage xml
python -m coverage report -m 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

View File

@ -5,7 +5,7 @@ from setuptools import setup
INSTALL_REQUIRES = [ INSTALL_REQUIRES = [
"py>=1.5.0", "py>=1.5.0",
"packaging", "packaging",
"attrs>=17.4.0", "attrs>=17.4.0", # should match oldattrs tox env.
"more-itertools>=4.0.0", "more-itertools>=4.0.0",
"atomicwrites>=1.0", "atomicwrites>=1.0",
'pathlib2>=2.2.0;python_version<"3.6"', 'pathlib2>=2.2.0;python_version<"3.6"',

View File

@ -8,6 +8,7 @@ from typing import Optional
import _pytest._code import _pytest._code
from _pytest import outcomes from _pytest import outcomes
from _pytest._io.saferepr import saferepr 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 # The _reprcompare attribute on the util module is used by the new assertion
# interpretation code and assertion rewriter to detect this plugin was # 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] fields_to_check = [field for field, info in all_fields.items() if info.compare]
elif isattrs(left): elif isattrs(left):
all_fields = left.__attrs_attrs__ 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 = [] same = []
diff = [] diff = []

View File

@ -354,3 +354,9 @@ if sys.version_info < (3, 5, 2): # pragma: no cover
def overload(f): # noqa: F811 def overload(f): # noqa: F811
return f return f
if getattr(attr, "__version_info__", ()) >= (19, 2):
ATTRS_EQ_FIELD = "eq"
else:
ATTRS_EQ_FIELD = "cmp"

View File

@ -8,6 +8,7 @@ from typing import Set
import attr import attr
from ..compat import ascii_escaped from ..compat import ascii_escaped
from ..compat import ATTRS_EQ_FIELD
from ..compat import getfslineno from ..compat import getfslineno
from ..compat import NOTSET from ..compat import NOTSET
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -367,7 +368,8 @@ class NodeKeywords(MutableMapping):
return "<NodeKeywords for node {}>".format(self.node) return "<NodeKeywords for node {}>".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: class NodeMarkers:
""" """
internal structure for storing marks belonging to a node internal structure for storing marks belonging to a node

View File

@ -351,15 +351,14 @@ class RunResult:
Attributes: Attributes:
:ret: the return value :ivar ret: the return value
:outlines: list of lines captured from stdout :ivar outlines: list of lines captured from stdout
:errlines: list of lines captures from stderr :ivar errlines: list of lines captured from stderr
:stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to :ivar stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to
reconstruct stdout or the commonly used ``stdout.fnmatch_lines()`` reconstruct stdout or the commonly used ``stdout.fnmatch_lines()``
method method
:stderr: :py:class:`LineMatcher` of stderr :ivar stderr: :py:class:`LineMatcher` of stderr
:duration: duration in seconds :ivar duration: duration in seconds
""" """
def __init__(self, ret, outlines, errlines, duration): def __init__(self, ret, outlines, errlines, duration):
@ -454,9 +453,9 @@ class Testdir:
Attributes: 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 :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 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. the method using them so refer to them for details.

View File

@ -107,8 +107,8 @@ def show_test_item(item):
tw = item.config.get_terminal_writer() tw = item.config.get_terminal_writer()
tw.line() tw.line()
tw.write(" " * 8) tw.write(" " * 8)
tw.write(item._nodeid) tw.write(item.nodeid)
used_fixtures = sorted(item._fixtureinfo.name2fixturedefs.keys()) used_fixtures = sorted(getattr(item, "fixturenames", []))
if used_fixtures: if used_fixtures:
tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) tw.write(" (fixtures used: {})".format(", ".join(used_fixtures)))

View File

@ -88,3 +88,30 @@ def tw_mock():
fullwidth = 80 fullwidth = 80
return TWMock() 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="")

View File

@ -6,8 +6,8 @@ def mode(request):
return request.param return request.param
def test_show_only_active_fixtures(testdir, mode): def test_show_only_active_fixtures(testdir, mode, dummy_yaml_custom_test):
p = testdir.makepyfile( testdir.makepyfile(
''' '''
import pytest import pytest
@pytest.fixture @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 assert result.ret == 0
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(

View File

@ -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. """ """ Verifies that fixtures are not executed. """
p = testdir.makepyfile( testdir.makepyfile(
""" """
import pytest import pytest
@pytest.fixture @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 assert result.ret == 0
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(

View File

@ -9,6 +9,7 @@ import pytest
from _pytest import outcomes from _pytest import outcomes
from _pytest.assertion import truncate from _pytest.assertion import truncate
from _pytest.assertion import util from _pytest.assertion import util
from _pytest.compat import ATTRS_EQ_FIELD
def mock_config(): def mock_config():
@ -687,7 +688,7 @@ class TestAssert_reprcompare_attrsclass:
@attr.s @attr.s
class SimpleDataObject: class SimpleDataObject:
field_a = attr.ib() field_a = attr.ib()
field_b = attr.ib(cmp=False) field_b = attr.ib(**{ATTRS_EQ_FIELD: False})
left = SimpleDataObject(1, "b") left = SimpleDataObject(1, "b")
right = SimpleDataObject(1, "b") right = SimpleDataObject(1, "b")

View File

@ -1,6 +1,7 @@
import argparse import argparse
import distutils.spawn import distutils.spawn
import os import os
import shlex
import sys import sys
import py import py
@ -298,7 +299,11 @@ def test_argcomplete(testdir, monkeypatch):
# redirect output from argcomplete to stdin and stderr is not trivial # redirect output from argcomplete to stdin and stderr is not trivial
# http://stackoverflow.com/q/12589419/1307905 # http://stackoverflow.com/q/12589419/1307905
# so we use bash # 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 # alternative would be exteneded Testdir.{run(),_run(),popen()} to be able
# to handle a keyword argument env that replaces os.environ in popen or # to handle a keyword argument env that replaces os.environ in popen or
# extends the copy, advantage: could not forget to restore # extends the copy, advantage: could not forget to restore

View File

@ -41,6 +41,8 @@ setenv =
xdist: _PYTEST_TOX_POSARGS_XDIST=-n auto xdist: _PYTEST_TOX_POSARGS_XDIST=-n auto
extras = testing extras = testing
deps = deps =
oldattrs: attrs==17.4.0
oldattrs: hypothesis<=4.38.1
numpy: numpy numpy: numpy
pexpect: pexpect pexpect: pexpect
pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master
@ -77,7 +79,7 @@ commands =
[testenv:regen] [testenv:regen]
changedir = doc/en changedir = doc/en
skipsdist = True skipsdist = True
basepython = python3.6 basepython = python3
deps = deps =
sphinx sphinx
PyYAML PyYAML
@ -103,7 +105,7 @@ commands =
[testenv:release] [testenv:release]
decription = do a release, required posarg of the version number decription = do a release, required posarg of the version number
basepython = python3.6 basepython = python3
usedevelop = True usedevelop = True
passenv = * passenv = *
deps = deps =
@ -116,7 +118,7 @@ commands = python scripts/release.py {posargs}
[testenv:publish_gh_release_notes] [testenv:publish_gh_release_notes]
description = create GitHub release after deployment description = create GitHub release after deployment
basepython = python3.6 basepython = python3
usedevelop = True usedevelop = True
passenv = GH_RELEASE_NOTES_TOKEN TRAVIS_TAG TRAVIS_REPO_SLUG passenv = GH_RELEASE_NOTES_TOKEN TRAVIS_TAG TRAVIS_REPO_SLUG
deps = deps =