Compare commits
23 Commits
7.0.0.post
...
4.6.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8e65d03bf | ||
|
|
f37ea715d8 | ||
|
|
45d36ddb47 | ||
|
|
355954df5d | ||
|
|
a93c50ccb9 | ||
|
|
1cae76b0fe | ||
|
|
1b7597ac91 | ||
|
|
21680ffa77 | ||
|
|
8076f48eae | ||
|
|
0ae27714d1 | ||
|
|
92432ac45c | ||
|
|
937f945946 | ||
|
|
829a5986e8 | ||
|
|
54dbfb5167 | ||
|
|
70f0b77c72 | ||
|
|
2a8b463b38 | ||
|
|
12bf458719 | ||
|
|
114dba56f8 | ||
|
|
abb853f482 | ||
|
|
8208a376cc | ||
|
|
f078984c2e | ||
|
|
dba62f8a46 | ||
|
|
f7bf914108 |
@@ -130,7 +130,7 @@ before_script:
|
||||
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
fi
|
||||
|
||||
script: tox --recreate
|
||||
script: tox
|
||||
|
||||
after_success:
|
||||
- |
|
||||
|
||||
@@ -18,6 +18,49 @@ with advance notice in the **Deprecations** section of releases.
|
||||
|
||||
.. towncrier release notes start
|
||||
|
||||
pytest 4.6.3 (2019-06-11)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#5383 <https://github.com/pytest-dev/pytest/issues/5383>`_: ``-q`` has again an impact on the style of the collected items
|
||||
(``--collect-only``) when ``--log-cli-level`` is used.
|
||||
|
||||
|
||||
- `#5389 <https://github.com/pytest-dev/pytest/issues/5389>`_: Fix regressions of `#5063 <https://github.com/pytest-dev/pytest/pull/5063>`__ for ``importlib_metadata.PathDistribution`` which have their ``files`` attribute being ``None``.
|
||||
|
||||
|
||||
- `#5390 <https://github.com/pytest-dev/pytest/issues/5390>`_: Fix regression where the ``obj`` attribute of ``TestCase`` items was no longer bound to methods.
|
||||
|
||||
|
||||
pytest 4.6.2 (2019-06-03)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#5370 <https://github.com/pytest-dev/pytest/issues/5370>`_: Revert unrolling of ``all()`` to fix ``NameError`` on nested comprehensions.
|
||||
|
||||
|
||||
- `#5371 <https://github.com/pytest-dev/pytest/issues/5371>`_: Revert unrolling of ``all()`` to fix incorrect handling of generators with ``if``.
|
||||
|
||||
|
||||
- `#5372 <https://github.com/pytest-dev/pytest/issues/5372>`_: Revert unrolling of ``all()`` to fix incorrect assertion when using ``all()`` in an expression.
|
||||
|
||||
|
||||
pytest 4.6.1 (2019-06-02)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#5354 <https://github.com/pytest-dev/pytest/issues/5354>`_: Fix ``pytest.mark.parametrize`` when the argvalues is an iterator.
|
||||
|
||||
|
||||
- `#5358 <https://github.com/pytest-dev/pytest/issues/5358>`_: Fix assertion rewriting of ``all()`` calls to deal with non-generators.
|
||||
|
||||
|
||||
pytest 4.6.0 (2019-05-31)
|
||||
=========================
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-4.6.3
|
||||
release-4.6.2
|
||||
release-4.6.1
|
||||
release-4.6.0
|
||||
release-4.5.0
|
||||
release-4.4.2
|
||||
|
||||
19
doc/en/announce/release-4.6.1.rst
Normal file
19
doc/en/announce/release-4.6.1.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
pytest-4.6.1
|
||||
=======================================
|
||||
|
||||
pytest 4.6.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
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
18
doc/en/announce/release-4.6.2.rst
Normal file
18
doc/en/announce/release-4.6.2.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
pytest-4.6.2
|
||||
=======================================
|
||||
|
||||
pytest 4.6.2 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
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
21
doc/en/announce/release-4.6.3.rst
Normal file
21
doc/en/announce/release-4.6.3.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-4.6.3
|
||||
=======================================
|
||||
|
||||
pytest 4.6.3 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
|
||||
* Daniel Hahler
|
||||
* Dirk Thomas
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -953,8 +953,6 @@ warn_explicit(
|
||||
"""
|
||||
visit `ast.Call` nodes on Python3.5 and after
|
||||
"""
|
||||
if isinstance(call.func, ast.Name) and call.func.id == "all":
|
||||
return self._visit_all(call)
|
||||
new_func, func_expl = self.visit(call.func)
|
||||
arg_expls = []
|
||||
new_args = []
|
||||
@@ -978,27 +976,6 @@ warn_explicit(
|
||||
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
|
||||
return res, outer_expl
|
||||
|
||||
def _visit_all(self, call):
|
||||
"""Special rewrite for the builtin all function, see #5062"""
|
||||
if not isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp)):
|
||||
return
|
||||
gen_exp = call.args[0]
|
||||
assertion_module = ast.Module(
|
||||
body=[ast.Assert(test=gen_exp.elt, lineno=1, msg="", col_offset=1)]
|
||||
)
|
||||
AssertionRewriter(module_path=None, config=None).run(assertion_module)
|
||||
for_loop = ast.For(
|
||||
iter=gen_exp.generators[0].iter,
|
||||
target=gen_exp.generators[0].target,
|
||||
body=assertion_module.body,
|
||||
orelse=[],
|
||||
)
|
||||
self.statements.append(for_loop)
|
||||
return (
|
||||
ast.Num(n=1),
|
||||
"",
|
||||
) # Return an empty expression, all the asserts are in the for_loop
|
||||
|
||||
def visit_Starred(self, starred):
|
||||
# From Python 3.5, a Starred node can appear in a function call
|
||||
res, expl = self.visit(starred.value)
|
||||
@@ -1009,8 +986,6 @@ warn_explicit(
|
||||
"""
|
||||
visit `ast.Call nodes on 3.4 and below`
|
||||
"""
|
||||
if isinstance(call.func, ast.Name) and call.func.id == "all":
|
||||
return self._visit_all(call)
|
||||
new_func, func_expl = self.visit(call.func)
|
||||
arg_expls = []
|
||||
new_args = []
|
||||
|
||||
@@ -800,7 +800,7 @@ class Config(object):
|
||||
str(file)
|
||||
for dist in importlib_metadata.distributions()
|
||||
if any(ep.group == "pytest11" for ep in dist.entry_points)
|
||||
for file in dist.files
|
||||
for file in dist.files or []
|
||||
)
|
||||
|
||||
for name in _iter_rewritable_modules(package_files):
|
||||
|
||||
@@ -40,8 +40,8 @@ GETFUNCARGVALUE = RemovedInPytest4Warning(
|
||||
RAISES_MESSAGE_PARAMETER = PytestDeprecationWarning(
|
||||
"The 'message' parameter is deprecated.\n"
|
||||
"(did you mean to use `match='some regex'` to check the exception message?)\n"
|
||||
"Please comment on https://github.com/pytest-dev/pytest/issues/3974 "
|
||||
"if you have concerns about removal of this parameter."
|
||||
"Please see:\n"
|
||||
" https://docs.pytest.org/en/4.6-maintenance/deprecations.html#message-parameter-of-pytest-raises"
|
||||
)
|
||||
|
||||
RESULT_LOG = PytestDeprecationWarning(
|
||||
|
||||
@@ -424,10 +424,6 @@ class LoggingPlugin(object):
|
||||
"""
|
||||
self._config = config
|
||||
|
||||
# enable verbose output automatically if live logging is enabled
|
||||
if self._log_cli_enabled() and config.getoption("verbose") < 1:
|
||||
config.option.verbose = 1
|
||||
|
||||
self.print_logs = get_option_ini(config, "log_print")
|
||||
self.formatter = self._create_formatter(
|
||||
get_option_ini(config, "log_format"),
|
||||
@@ -644,6 +640,15 @@ class LoggingPlugin(object):
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtestloop(self, session):
|
||||
"""Runs all collected test items."""
|
||||
|
||||
if session.config.option.collectonly:
|
||||
yield
|
||||
return
|
||||
|
||||
if self._log_cli_enabled() and self._config.getoption("verbose") < 1:
|
||||
# setting verbose flag is needed to avoid messy test progress output
|
||||
self._config.option.verbose = 1
|
||||
|
||||
with self.live_logs_context():
|
||||
if self.log_file_handler is not None:
|
||||
with catching_logs(self.log_file_handler, level=self.log_file_level):
|
||||
|
||||
@@ -113,14 +113,18 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
|
||||
force_tuple = len(argnames) == 1
|
||||
else:
|
||||
force_tuple = False
|
||||
parameters = [
|
||||
return argnames, force_tuple
|
||||
|
||||
@staticmethod
|
||||
def _parse_parametrize_parameters(argvalues, force_tuple):
|
||||
return [
|
||||
ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues
|
||||
]
|
||||
return argnames, parameters
|
||||
|
||||
@classmethod
|
||||
def _for_parametrize(cls, argnames, argvalues, func, config, function_definition):
|
||||
argnames, parameters = cls._parse_parametrize_args(argnames, argvalues)
|
||||
argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues)
|
||||
parameters = cls._parse_parametrize_parameters(argvalues, force_tuple)
|
||||
del argvalues
|
||||
|
||||
if parameters:
|
||||
|
||||
@@ -114,6 +114,7 @@ class TestCaseFunction(Function):
|
||||
def setup(self):
|
||||
self._testcase = self.parent.obj(self.name)
|
||||
self._fix_unittest_skip_decorator()
|
||||
self._obj = getattr(self._testcase, self.name)
|
||||
if hasattr(self, "_request"):
|
||||
self._request._fillfixtures()
|
||||
|
||||
@@ -132,6 +133,7 @@ class TestCaseFunction(Function):
|
||||
|
||||
def teardown(self):
|
||||
self._testcase = None
|
||||
self._obj = None
|
||||
|
||||
def startTest(self, testcase):
|
||||
pass
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
if sys.gettrace():
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def restore_tracing():
|
||||
"""Restore tracing function (when run with Coverage.py).
|
||||
|
||||
https://bugs.python.org/issue37011
|
||||
"""
|
||||
orig_trace = sys.gettrace()
|
||||
yield
|
||||
if sys.gettrace() != orig_trace:
|
||||
sys.settrace(orig_trace)
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
|
||||
@@ -921,15 +921,46 @@ def test_collection_live_logging(testdir):
|
||||
|
||||
result = testdir.runpytest("--log-cli-level=INFO")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"collecting*",
|
||||
"*--- live log collection ---*",
|
||||
"*Normal message*",
|
||||
"collected 0 items",
|
||||
]
|
||||
["*--- live log collection ---*", "*Normal message*", "collected 0 items"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("verbose", ["", "-q", "-qq"])
|
||||
def test_collection_collect_only_live_logging(testdir, verbose):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_simple():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
|
||||
result = testdir.runpytest("--collect-only", "--log-cli-level=INFO", verbose)
|
||||
|
||||
expected_lines = []
|
||||
|
||||
if not verbose:
|
||||
expected_lines.extend(
|
||||
[
|
||||
"*collected 1 item*",
|
||||
"*<Module test_collection_collect_only_live_logging.py>*",
|
||||
"*no tests ran*",
|
||||
]
|
||||
)
|
||||
elif verbose == "-q":
|
||||
assert "collected 1 item*" not in result.stdout.str()
|
||||
expected_lines.extend(
|
||||
[
|
||||
"*test_collection_collect_only_live_logging.py::test_simple*",
|
||||
"no tests ran in * seconds",
|
||||
]
|
||||
)
|
||||
elif verbose == "-qq":
|
||||
assert "collected 1 item*" not in result.stdout.str()
|
||||
expected_lines.extend(["*test_collection_collect_only_live_logging.py: 1*"])
|
||||
|
||||
result.stdout.fnmatch_lines(expected_lines)
|
||||
|
||||
|
||||
def test_collection_logging_to_file(testdir):
|
||||
log_file = testdir.tmpdir.join("pytest.log").strpath
|
||||
|
||||
|
||||
@@ -656,12 +656,6 @@ class TestAssertionRewrite(object):
|
||||
else:
|
||||
assert lines == ["assert 0 == 1\n + where 1 = \\n{ \\n~ \\n}.a"]
|
||||
|
||||
def test_unroll_expression(self):
|
||||
def f():
|
||||
assert all(x == 1 for x in range(10))
|
||||
|
||||
assert "0 == 1" in getmsg(f)
|
||||
|
||||
def test_custom_repr_non_ascii(self):
|
||||
def f():
|
||||
class A(object):
|
||||
@@ -677,53 +671,6 @@ class TestAssertionRewrite(object):
|
||||
assert "UnicodeDecodeError" not in msg
|
||||
assert "UnicodeEncodeError" not in msg
|
||||
|
||||
def test_unroll_generator(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def check_even(num):
|
||||
if num % 2 == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def test_generator():
|
||||
odd_list = list(range(1,9,2))
|
||||
assert all(check_even(num) for num in odd_list)"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*assert False*", "*where False = check_even(1)*"])
|
||||
|
||||
def test_unroll_list_comprehension(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def check_even(num):
|
||||
if num % 2 == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def test_list_comprehension():
|
||||
odd_list = list(range(1,9,2))
|
||||
assert all([check_even(num) for num in odd_list])"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*assert False*", "*where False = check_even(1)*"])
|
||||
|
||||
def test_for_loop(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def check_even(num):
|
||||
if num % 2 == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def test_for_loop():
|
||||
odd_list = list(range(1,9,2))
|
||||
for num in odd_list:
|
||||
assert check_even(num)
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*assert False*", "*where False = check_even(1)*"])
|
||||
|
||||
|
||||
class TestRewriteOnImport(object):
|
||||
def test_pycache_is_a_file(self, testdir):
|
||||
|
||||
@@ -586,6 +586,29 @@ def test_setuptools_importerror_issue1479(testdir, monkeypatch):
|
||||
testdir.parseconfig()
|
||||
|
||||
|
||||
def test_importlib_metadata_broken_distribution(testdir, monkeypatch):
|
||||
"""Integration test for broken distributions with 'files' metadata being None (#5389)"""
|
||||
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
||||
|
||||
class DummyEntryPoint:
|
||||
name = "mytestplugin"
|
||||
group = "pytest11"
|
||||
|
||||
def load(self):
|
||||
return object()
|
||||
|
||||
class Distribution:
|
||||
version = "1.0"
|
||||
files = None
|
||||
entry_points = (DummyEntryPoint(),)
|
||||
|
||||
def distributions():
|
||||
return (Distribution(),)
|
||||
|
||||
monkeypatch.setattr(importlib_metadata, "distributions", distributions)
|
||||
testdir.parseconfig()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("block_it", [True, False])
|
||||
def test_plugin_preparse_prevents_setuptools_loading(testdir, monkeypatch, block_it):
|
||||
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
||||
|
||||
@@ -413,6 +413,28 @@ def test_parametrized_with_kwargs(testdir):
|
||||
assert result.ret == 0
|
||||
|
||||
|
||||
def test_parametrize_iterator(testdir):
|
||||
"""parametrize should work with generators (#5354)."""
|
||||
py_file = testdir.makepyfile(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
def gen():
|
||||
yield 1
|
||||
yield 2
|
||||
yield 3
|
||||
|
||||
@pytest.mark.parametrize('a', gen())
|
||||
def test(a):
|
||||
assert a >= 1
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(py_file)
|
||||
assert result.ret == 0
|
||||
# should not skip any tests
|
||||
result.stdout.fnmatch_lines(["*3 passed*"])
|
||||
|
||||
|
||||
class TestFunctional(object):
|
||||
def test_merging_markers_deep(self, testdir):
|
||||
# issue 199 - propagate markers into nested classes
|
||||
|
||||
@@ -144,6 +144,29 @@ def test_new_instances(testdir):
|
||||
reprec.assertoutcome(passed=2)
|
||||
|
||||
|
||||
def test_function_item_obj_is_instance(testdir):
|
||||
"""item.obj should be a bound method on unittest.TestCase function items (#5390)."""
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
def pytest_runtest_makereport(item, call):
|
||||
if call.when == 'call':
|
||||
class_ = item.parent.obj
|
||||
assert isinstance(item.obj.__self__, class_)
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def test_foo(self):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_inprocess()
|
||||
result.stdout.fnmatch_lines(["* 1 passed in*"])
|
||||
|
||||
|
||||
def test_teardown(testdir):
|
||||
testpath = testdir.makepyfile(
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user