mirror of https://github.com/pytest-dev/pytest.git
Merge pull request #6614 from blueyed/release-5.3.5
Preparing release version 5.3.5
This commit is contained in:
commit
1b3eb4184d
|
@ -6,6 +6,7 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-5.3.5
|
||||||
release-5.3.4
|
release-5.3.4
|
||||||
release-5.3.3
|
release-5.3.3
|
||||||
release-5.3.2
|
release-5.3.2
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
pytest-5.3.5
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 5.3.5 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:
|
||||||
|
|
||||||
|
* Daniel Hahler
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -28,6 +28,15 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 5.3.5 (2020-01-29)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#6517 <https://github.com/pytest-dev/pytest/issues/6517>`_: Fix regression in pytest 5.3.4 causing an INTERNALERROR due to a wrong assertion.
|
||||||
|
|
||||||
|
|
||||||
pytest 5.3.4 (2020-01-20)
|
pytest 5.3.4 (2020-01-20)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -475,11 +475,8 @@ 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%]
|
........................... [100%]
|
||||||
========================= short test summary info ==========================
|
27 passed in 0.12s
|
||||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found
|
|
||||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.7' not found
|
|
||||||
3 passed, 24 skipped in 0.12s
|
|
||||||
|
|
||||||
Indirect parametrization of optional implementations/imports
|
Indirect parametrization of optional implementations/imports
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
|
@ -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 ______________________
|
||||||
|
|
|
@ -443,7 +443,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.20s call test_some_are_slow.py::test_funcslow1
|
0.20s call test_some_are_slow.py::test_funcslow1
|
||||||
0.11s 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 =============================
|
||||||
|
|
||||||
incremental testing - test steps
|
incremental testing - test steps
|
||||||
|
|
|
@ -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/__init__.py
|
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.8/site-packages/pytest/__init__.py
|
||||||
|
|
||||||
.. _`simpletest`:
|
.. _`simpletest`:
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,8 @@ class Parser:
|
||||||
|
|
||||||
self.optparser = self._getparser()
|
self.optparser = self._getparser()
|
||||||
try_argcomplete(self.optparser)
|
try_argcomplete(self.optparser)
|
||||||
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||||
return self.optparser.parse_args(strargs, namespace=namespace)
|
return self.optparser.parse_args(args, namespace=namespace)
|
||||||
|
|
||||||
def _getparser(self) -> "MyOptionParser":
|
def _getparser(self) -> "MyOptionParser":
|
||||||
from _pytest._argcomplete import filescompleter
|
from _pytest._argcomplete import filescompleter
|
||||||
|
@ -124,8 +124,8 @@ class Parser:
|
||||||
the remaining arguments unknown at this point.
|
the remaining arguments unknown at this point.
|
||||||
"""
|
"""
|
||||||
optparser = self._getparser()
|
optparser = self._getparser()
|
||||||
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||||
return optparser.parse_known_args(strargs, namespace=namespace)
|
return optparser.parse_known_args(args, namespace=namespace)
|
||||||
|
|
||||||
def addini(self, name, help, type=None, default=None):
|
def addini(self, name, help, type=None, default=None):
|
||||||
""" register an ini-file option.
|
""" register an ini-file option.
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import os
|
import os
|
||||||
from typing import Any
|
|
||||||
from typing import Iterable
|
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -63,7 +60,7 @@ def getcfg(args, config=None):
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
|
||||||
def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local:
|
def get_common_ancestor(paths):
|
||||||
common_ancestor = None
|
common_ancestor = None
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
|
@ -116,7 +113,7 @@ def determine_setup(
|
||||||
args: List[str],
|
args: List[str],
|
||||||
rootdir_cmd_arg: Optional[str] = None,
|
rootdir_cmd_arg: Optional[str] = None,
|
||||||
config: Optional["Config"] = None,
|
config: Optional["Config"] = None,
|
||||||
) -> Tuple[py.path.local, Optional[str], Any]:
|
):
|
||||||
dirs = get_dirs_from_args(args)
|
dirs = get_dirs_from_args(args)
|
||||||
if inifile:
|
if inifile:
|
||||||
iniconfig = py.iniconfig.IniConfig(inifile)
|
iniconfig = py.iniconfig.IniConfig(inifile)
|
||||||
|
|
|
@ -308,7 +308,7 @@ class DoctestItem(pytest.Item):
|
||||||
else:
|
else:
|
||||||
return super().repr_failure(excinfo)
|
return super().repr_failure(excinfo)
|
||||||
|
|
||||||
def reportinfo(self) -> Tuple[py.path.local, int, str]:
|
def reportinfo(self) -> Tuple[str, int, str]:
|
||||||
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
|
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,7 @@ class FixtureRequest:
|
||||||
self.fixturename = None
|
self.fixturename = None
|
||||||
#: Scope string, one of "function", "class", "module", "session"
|
#: Scope string, one of "function", "class", "module", "session"
|
||||||
self.scope = "function"
|
self.scope = "function"
|
||||||
self._fixture_defs = {} # type: Dict[str, FixtureDef]
|
self._fixture_defs = {} # argname -> FixtureDef
|
||||||
fixtureinfo = pyfuncitem._fixtureinfo
|
fixtureinfo = pyfuncitem._fixtureinfo
|
||||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
|
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
|
||||||
self._arg2index = {}
|
self._arg2index = {}
|
||||||
|
@ -426,8 +426,7 @@ class FixtureRequest:
|
||||||
@scopeproperty()
|
@scopeproperty()
|
||||||
def fspath(self) -> py.path.local:
|
def fspath(self) -> py.path.local:
|
||||||
""" the file system path of the test module which collected this test. """
|
""" the file system path of the test module which collected this test. """
|
||||||
# TODO: Remove ignore once _pyfuncitem is properly typed.
|
return self._pyfuncitem.fspath
|
||||||
return self._pyfuncitem.fspath # type: ignore
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
|
@ -550,9 +549,7 @@ class FixtureRequest:
|
||||||
source_lineno = frameinfo.lineno
|
source_lineno = frameinfo.lineno
|
||||||
source_path = py.path.local(source_path)
|
source_path = py.path.local(source_path)
|
||||||
if source_path.relto(funcitem.config.rootdir):
|
if source_path.relto(funcitem.config.rootdir):
|
||||||
source_path_str = source_path.relto(funcitem.config.rootdir)
|
source_path = source_path.relto(funcitem.config.rootdir)
|
||||||
else:
|
|
||||||
source_path_str = str(source_path)
|
|
||||||
msg = (
|
msg = (
|
||||||
"The requested fixture has no parameter defined for test:\n"
|
"The requested fixture has no parameter defined for test:\n"
|
||||||
" {}\n\n"
|
" {}\n\n"
|
||||||
|
@ -561,7 +558,7 @@ class FixtureRequest:
|
||||||
funcitem.nodeid,
|
funcitem.nodeid,
|
||||||
fixturedef.argname,
|
fixturedef.argname,
|
||||||
getlocation(fixturedef.func, funcitem.config.rootdir),
|
getlocation(fixturedef.func, funcitem.config.rootdir),
|
||||||
source_path_str,
|
source_path,
|
||||||
source_lineno,
|
source_lineno,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -367,9 +367,9 @@ class Failed(Exception):
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
class _bestrelpath_cache(dict):
|
class _bestrelpath_cache(dict):
|
||||||
path = attr.ib(type=py.path.local)
|
path = attr.ib()
|
||||||
|
|
||||||
def __missing__(self, path: py.path.local) -> str:
|
def __missing__(self, path: str) -> str:
|
||||||
r = self.path.bestrelpath(path) # type: str
|
r = self.path.bestrelpath(path) # type: str
|
||||||
self[path] = r
|
self[path] = r
|
||||||
return r
|
return r
|
||||||
|
@ -399,7 +399,7 @@ class Session(nodes.FSCollector):
|
||||||
self._node_cache = {}
|
self._node_cache = {}
|
||||||
self._bestrelpathcache = _bestrelpath_cache(
|
self._bestrelpathcache = _bestrelpath_cache(
|
||||||
config.rootdir
|
config.rootdir
|
||||||
) # type: Dict[py.path.local, str]
|
) # type: Dict[str, str]
|
||||||
# Dirnames of pkgs with dunder-init files.
|
# Dirnames of pkgs with dunder-init files.
|
||||||
self._pkg_roots = {}
|
self._pkg_roots = {}
|
||||||
|
|
||||||
|
@ -414,7 +414,7 @@ class Session(nodes.FSCollector):
|
||||||
self.testscollected,
|
self.testscollected,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _node_location_to_relpath(self, node_path: py.path.local) -> str:
|
def _node_location_to_relpath(self, node_path: str) -> str:
|
||||||
# bestrelpath is a quite slow function
|
# bestrelpath is a quite slow function
|
||||||
return self._bestrelpathcache[node_path]
|
return self._bestrelpathcache[node_path]
|
||||||
|
|
||||||
|
|
|
@ -462,7 +462,6 @@ class Item(Node):
|
||||||
@cached_property
|
@cached_property
|
||||||
def location(self) -> Tuple[str, Optional[int], str]:
|
def location(self) -> Tuple[str, Optional[int], str]:
|
||||||
location = self.reportinfo()
|
location = self.reportinfo()
|
||||||
assert isinstance(location[0], py.path.local), location[0]
|
|
||||||
fspath = self.session._node_location_to_relpath(location[0])
|
fspath = self.session._node_location_to_relpath(location[0])
|
||||||
assert type(location[2]) is str
|
assert type(location[2]) is str
|
||||||
return (fspath, location[1], location[2])
|
return (fspath, location[1], location[2])
|
||||||
|
|
|
@ -1018,10 +1018,10 @@ class TestReportInfo:
|
||||||
def test_itemreport_reportinfo(self, testdir):
|
def test_itemreport_reportinfo(self, testdir):
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
import pytest, py
|
import pytest
|
||||||
class MyFunction(pytest.Function):
|
class MyFunction(pytest.Function):
|
||||||
def reportinfo(self):
|
def reportinfo(self):
|
||||||
return py.path.local("foo"), 42, "custom"
|
return "ABCDE", 42, "custom"
|
||||||
def pytest_pycollect_makeitem(collector, name, obj):
|
def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
if name == "test_func":
|
if name == "test_func":
|
||||||
return MyFunction(name, parent=collector)
|
return MyFunction(name, parent=collector)
|
||||||
|
@ -1029,7 +1029,7 @@ class TestReportInfo:
|
||||||
)
|
)
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
item.config.pluginmanager.getplugin("runner")
|
item.config.pluginmanager.getplugin("runner")
|
||||||
assert item.location == ("foo", 42, "custom")
|
assert item.location == ("ABCDE", 42, "custom")
|
||||||
|
|
||||||
def test_func_reportinfo(self, testdir):
|
def test_func_reportinfo(self, testdir):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
|
|
|
@ -375,3 +375,17 @@ def test_skip_test_with_unicode(testdir):
|
||||||
)
|
)
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(["* 1 skipped *"])
|
result.stdout.fnmatch_lines(["* 1 skipped *"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_issue_6517(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
from nose.tools import raises
|
||||||
|
|
||||||
|
@raises(RuntimeError)
|
||||||
|
def test_fail_without_tcp():
|
||||||
|
raise RuntimeError
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(["* 1 passed *"])
|
||||||
|
|
Loading…
Reference in New Issue