Merge pull request #4808 from nicoddemus/merge-master-into-features

Merge master into features
This commit is contained in:
Bruno Oliveira 2019-02-19 08:08:06 -03:00 committed by GitHub
commit b9561e29ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 188 additions and 71 deletions

View File

@ -1,4 +1,3 @@
sudo: false
language: python language: python
dist: xenial dist: xenial
stages: stages:
@ -26,7 +25,7 @@ env:
matrix: matrix:
allow_failures: allow_failures:
- python: '3.8-dev' - python: '3.8-dev'
env: TOXENV=py38 env: TOXENV=py38-xdist
jobs: jobs:
include: include:
@ -34,14 +33,12 @@ jobs:
- env: TOXENV=pypy PYTEST_NO_COVERAGE=1 - env: TOXENV=pypy PYTEST_NO_COVERAGE=1
python: 'pypy-5.4' python: 'pypy-5.4'
dist: trusty dist: trusty
- env: TOXENV=py34 - env: TOXENV=py34-xdist
python: '3.4' python: '3.4'
- env: TOXENV=py35 - env: TOXENV=py35-xdist
python: '3.5' python: '3.5'
- env: TOXENV=py36 - env: TOXENV=py36-xdist
python: '3.6' python: '3.6'
- env: TOXENV=py38
python: '3.8-dev'
- env: TOXENV=py37 - env: TOXENV=py37
- &test-macos - &test-macos
language: generic language: generic
@ -50,15 +47,20 @@ jobs:
sudo: required sudo: required
install: install:
- python -m pip install --pre tox - python -m pip install --pre tox
env: TOXENV=py27 env: TOXENV=py27-xdist
- <<: *test-macos - <<: *test-macos
env: TOXENV=py37 env: TOXENV=py37-xdist
before_install: before_install:
- brew update - brew update
- brew upgrade python - brew upgrade python
- brew unlink python - brew unlink python
- brew link python - brew link python
# Jobs only run via Travis cron jobs (currently daily).
- env: TOXENV=py38-xdist
python: '3.8-dev'
if: type = cron
- stage: baseline - stage: baseline
env: TOXENV=py27-pexpect,py27-trial,py27-numpy env: TOXENV=py27-pexpect,py27-trial,py27-numpy
- env: TOXENV=py37-xdist - env: TOXENV=py37-xdist

View File

@ -18,6 +18,48 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start .. towncrier release notes start
pytest 4.3.0 (2019-02-16)
=========================
Deprecations
------------
- `#4724 <https://github.com/pytest-dev/pytest/issues/4724>`_: ``pytest.warns()`` now emits a warning when it receives unknown keyword arguments.
This will be changed into an error in the future.
Features
--------
- `#2753 <https://github.com/pytest-dev/pytest/issues/2753>`_: Usage errors from argparse are mapped to pytest's ``UsageError``.
- `#3711 <https://github.com/pytest-dev/pytest/issues/3711>`_: Add the ``--ignore-glob`` parameter to exclude test-modules with Unix shell-style wildcards.
Add the ``collect_ignore_glob`` for ``conftest.py`` to exclude test-modules with Unix shell-style wildcards.
- `#4698 <https://github.com/pytest-dev/pytest/issues/4698>`_: The warning about Python 2.7 and 3.4 not being supported in pytest 5.0 has been removed.
In the end it was considered to be more
of a nuisance than actual utility and users of those Python versions shouldn't have problems as ``pip`` will not
install pytest 5.0 on those interpreters.
- `#4707 <https://github.com/pytest-dev/pytest/issues/4707>`_: With the help of new ``set_log_path()`` method there is a way to set ``log_file`` paths from hooks.
Bug Fixes
---------
- `#4651 <https://github.com/pytest-dev/pytest/issues/4651>`_: ``--help`` and ``--version`` are handled with ``UsageError``.
- `#4782 <https://github.com/pytest-dev/pytest/issues/4782>`_: Fix ``AssertionError`` with collection of broken symlinks with packages.
pytest 4.2.1 (2019-02-12) pytest 4.2.1 (2019-02-12)
========================= =========================

View File

@ -1 +0,0 @@
Usage errors from argparse are mapped to pytest's ``UsageError``.

View File

@ -1,2 +0,0 @@
Add the ``--ignore-glob`` parameter to exclude test-modules with Unix shell-style wildcards.
Add the ``collect_ignore_glob`` for ``conftest.py`` to exclude test-modules with Unix shell-style wildcards.

View File

@ -1 +0,0 @@
``--help`` and ``--version`` are handled with ``UsageError``.

View File

@ -1,5 +0,0 @@
The warning about Python 2.7 and 3.4 not being supported in pytest 5.0 has been removed.
In the end it was considered to be more
of a nuisance than actual utility and users of those Python versions shouldn't have problems as ``pip`` will not
install pytest 5.0 on those interpreters.

View File

@ -1 +0,0 @@
With the help of new ``set_log_path()`` method there is a way to set ``log_file`` paths from hooks.

View File

@ -1,3 +0,0 @@
``pytest.warns()`` now emits a warning when it receives unknown keyword arguments.
This will be changed into an error in the future.

View File

@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2 :maxdepth: 2
release-4.3.0
release-4.2.1 release-4.2.1
release-4.2.0 release-4.2.0
release-4.1.1 release-4.1.1

View File

@ -0,0 +1,36 @@
pytest-4.3.0
=======================================
The pytest team is proud to announce the 4.3.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:
* Andras Mitzki
* Anthony Sottile
* Bruno Oliveira
* Christian Fetzer
* Daniel Hahler
* Grygorii Iermolenko
* R. Alex Matevish
* Ronny Pfannschmidt
* cclauss
Happy testing,
The Pytest Development Team

View File

@ -88,23 +88,30 @@ and if you need to have access to the actual exception info you may use::
the actual exception raised. The main attributes of interest are the actual exception raised. The main attributes of interest are
``.type``, ``.value`` and ``.traceback``. ``.type``, ``.value`` and ``.traceback``.
.. versionchanged:: 3.0 You can pass a ``match`` keyword parameter to the context-manager to test
that a regular expression matches on the string representation of an exception
(similar to the ``TestCase.assertRaisesRegexp`` method from ``unittest``)::
In the context manager form you may use the keyword argument import pytest
``message`` to specify a custom failure message::
>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"): def myfunc():
... pass raise ValueError("Exception 123 raised")
... Failed: Expecting ZeroDivisionError
If you want to write test code that works on Python 2.4 as well, def test_match():
you may also use two other ways to test for an expected exception:: with pytest.raises(ValueError, match=r'.* 123 .*'):
myfunc()
The regexp parameter of the ``match`` method is matched with the ``re.search``
function, so in the above example ``match='123'`` would have worked as
well.
There's an alternate form of the ``pytest.raises`` function where you pass
a function that will be executed with the given ``*args`` and ``**kwargs`` and
assert that the given exception is raised::
pytest.raises(ExpectedException, func, *args, **kwargs) pytest.raises(ExpectedException, func, *args, **kwargs)
which will execute the specified function with args and kwargs and The reporter will provide you with helpful output in case of failures such as *no
assert that the given ``ExpectedException`` is raised. The reporter will
provide you with helpful output in case of failures such as *no
exception* or *wrong exception*. exception* or *wrong exception*.
Note that it is also possible to specify a "raises" argument to Note that it is also possible to specify a "raises" argument to
@ -121,23 +128,6 @@ exceptions your own code is deliberately raising, whereas using
like documenting unfixed bugs (where the test describes what "should" happen) like documenting unfixed bugs (where the test describes what "should" happen)
or bugs in dependencies. or bugs in dependencies.
Also, the context manager form accepts a ``match`` keyword parameter to test
that a regular expression matches on the string representation of an exception
(like the ``TestCase.assertRaisesRegexp`` method from ``unittest``)::
import pytest
def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError, match=r'.* 123 .*'):
myfunc()
The regexp parameter of the ``match`` method is matched with the ``re.search``
function. So in the above example ``match='123'`` would have worked as
well.
.. _`assertwarns`: .. _`assertwarns`:

View File

@ -436,10 +436,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
...sss...sssssssss...sss... [100%] ........................... [100%]
========================= short test summary info ========================== 27 passed in 0.12 seconds
SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.4' not found
12 passed, 15 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports Indirect parametrization of optional implementations/imports
-------------------------------------------------------------------- --------------------------------------------------------------------

View File

@ -25,6 +25,9 @@ Talks and blog postings
- pytest: recommendations, basic packages for testing in Python and Django, Andreu Vallbona, PyconES 2017 (`slides in english <http://talks.apsl.io/testing-pycones-2017/>`_, `video in spanish <https://www.youtube.com/watch?v=K20GeR-lXDk>`_) - pytest: recommendations, basic packages for testing in Python and Django, Andreu Vallbona, PyconES 2017 (`slides in english <http://talks.apsl.io/testing-pycones-2017/>`_, `video in spanish <https://www.youtube.com/watch?v=K20GeR-lXDk>`_)
- `pytest advanced, Andrew Svetlov (Russian, PyCon Russia, 2016)
<https://www.youtube.com/watch?v=7KgihdKTWY4>`_.
- `Pythonic testing, Igor Starikov (Russian, PyNsk, November 2016) - `Pythonic testing, Igor Starikov (Russian, PyNsk, November 2016)
<https://www.youtube.com/watch?v=_92nfdd5nK8>`_. <https://www.youtube.com/watch?v=_92nfdd5nK8>`_.

View File

@ -618,7 +618,12 @@ class Session(nodes.FSCollector):
yield y yield y
def _collectfile(self, path, handle_dupes=True): def _collectfile(self, path, handle_dupes=True):
assert path.isfile() assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % (
path,
path.isdir(),
path.exists(),
path.islink(),
)
ihook = self.gethookproxy(path) ihook = self.gethookproxy(path)
if not self.isinitpath(path): if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config): if ihook.pytest_ignore_collect(path=path, config=self.config):

View File

@ -113,11 +113,12 @@ class Node(object):
:raise ValueError: if ``warning`` instance is not a subclass of PytestWarning. :raise ValueError: if ``warning`` instance is not a subclass of PytestWarning.
Example usage:: Example usage:
.. code-block:: python .. code-block:: python
node.warn(PytestWarning("some message")) node.warn(PytestWarning("some message"))
""" """
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning

View File

@ -599,7 +599,12 @@ class Package(Module):
return proxy return proxy
def _collectfile(self, path, handle_dupes=True): def _collectfile(self, path, handle_dupes=True):
assert path.isfile() assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % (
path,
path.isdir(),
path.exists(),
path.islink(),
)
ihook = self.gethookproxy(path) ihook = self.gethookproxy(path)
if not self.isinitpath(path): if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config): if ihook.pytest_ignore_collect(path=path, config=self.config):
@ -632,7 +637,8 @@ class Package(Module):
pkg_prefixes = set() pkg_prefixes = set()
for path in this_path.visit(rec=self._recurse, bf=True, sort=True): for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
# We will visit our own __init__.py file, in which case we skip it. # We will visit our own __init__.py file, in which case we skip it.
if path.isfile(): is_file = path.isfile()
if is_file:
if path.basename == "__init__.py" and path.dirpath() == this_path: if path.basename == "__init__.py" and path.dirpath() == this_path:
continue continue
@ -643,12 +649,14 @@ class Package(Module):
): ):
continue continue
if path.isdir(): if is_file:
if path.join("__init__.py").check(file=1):
pkg_prefixes.add(path)
else:
for x in self._collectfile(path): for x in self._collectfile(path):
yield x yield x
elif not path.isdir():
# Broken symlink or invalid/missing file.
continue
elif path.join("__init__.py").check(file=1):
pkg_prefixes.add(path)
def _get_xunit_setup_teardown(holder, attr_name, param_obj=None): def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):

View File

@ -280,7 +280,9 @@ class TerminalReporter(object):
def write_fspath_result(self, nodeid, res, **markup): def write_fspath_result(self, nodeid, res, **markup):
fspath = self.config.rootdir.join(nodeid.split("::")[0]) fspath = self.config.rootdir.join(nodeid.split("::")[0])
if fspath != self.currentfspath: # NOTE: explicitly check for None to work around py bug, and for less
# overhead in general (https://github.com/pytest-dev/py/pull/207).
if self.currentfspath is None or fspath != self.currentfspath:
if self.currentfspath is not None and self._show_progress_info: if self.currentfspath is not None and self._show_progress_info:
self._write_progress_information_filling_space() self._write_progress_information_filling_space()
self.currentfspath = fspath self.currentfspath = fspath

View File

@ -31,7 +31,7 @@ class TempPathFactory(object):
# using os.path.abspath() to get absolute path instead of resolve() as it # using os.path.abspath() to get absolute path instead of resolve() as it
# does not work the same in all platforms (see #4427) # does not work the same in all platforms (see #4427)
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012) # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
convert=attr.converters.optional( converter=attr.converters.optional(
lambda p: Path(os.path.abspath(six.text_type(p))) lambda p: Path(os.path.abspath(six.text_type(p)))
) )
) )

View File

@ -560,7 +560,6 @@ def test_oneline_and_comment():
assert str(source) == "raise ValueError" assert str(source) == "raise ValueError"
@pytest.mark.xfail(hasattr(sys, "pypy_version_info"), reason="does not work on pypy")
def test_comments(): def test_comments():
source = '''def test(): source = '''def test():
"comment 1" "comment 1"
@ -576,9 +575,15 @@ comment 4
''' '''
for line in range(2, 6): for line in range(2, 6):
assert str(getstatement(line, source)) == " x = 1" assert str(getstatement(line, source)) == " x = 1"
for line in range(6, 10): if sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"):
tqs_start = 8
else:
tqs_start = 10
assert str(getstatement(10, source)) == '"""'
for line in range(6, tqs_start):
assert str(getstatement(line, source)) == " assert False" assert str(getstatement(line, source)) == " assert False"
assert str(getstatement(10, source)) == '"""' for line in range(tqs_start, 10):
assert str(getstatement(line, source)) == '"""\ncomment 4\n"""'
def test_comment_in_statement(): def test_comment_in_statement():

View File

@ -1308,10 +1308,17 @@ class TestEarlyRewriteBailout(object):
@pytest.mark.skipif( @pytest.mark.skipif(
sys.platform.startswith("win32"), reason="cannot remove cwd on Windows" sys.platform.startswith("win32"), reason="cannot remove cwd on Windows"
) )
def test_cwd_changed(self, testdir): def test_cwd_changed(self, testdir, monkeypatch):
# Setup conditions for py's fspath trying to import pathlib on py34
# always (previously triggered via xdist only).
# Ref: https://github.com/pytest-dev/py/pull/207
monkeypatch.setattr(sys, "path", [""] + sys.path)
if "pathlib" in sys.modules:
del sys.modules["pathlib"]
testdir.makepyfile( testdir.makepyfile(
**{ **{
"test_bar.py": """ "test_setup_nonexisting_cwd.py": """
import os import os
import shutil import shutil
import tempfile import tempfile
@ -1320,7 +1327,7 @@ class TestEarlyRewriteBailout(object):
os.chdir(d) os.chdir(d)
shutil.rmtree(d) shutil.rmtree(d)
""", """,
"test_foo.py": """ "test_test.py": """
def test(): def test():
pass pass
""", """,

View File

@ -1206,3 +1206,30 @@ def test_collect_pkg_init_and_file_in_args(testdir):
"*2 passed in*", "*2 passed in*",
] ]
) )
@pytest.mark.skipif(
not hasattr(py.path.local, "mksymlinkto"),
reason="symlink not available on this platform",
)
@pytest.mark.parametrize("use_pkg", (True, False))
def test_collect_sub_with_symlinks(use_pkg, testdir):
sub = testdir.mkdir("sub")
if use_pkg:
sub.ensure("__init__.py")
sub.ensure("test_file.py").write("def test_file(): pass")
# Create a broken symlink.
sub.join("test_broken.py").mksymlinkto("test_doesnotexist.py")
# Symlink that gets collected.
sub.join("test_symlink.py").mksymlinkto("test_file.py")
result = testdir.runpytest("-v", str(sub))
result.stdout.fnmatch_lines(
[
"sub/test_file.py::test_file PASSED*",
"sub/test_symlink.py::test_file PASSED*",
"*2 passed in*",
]
)

View File

@ -910,7 +910,6 @@ def test_reportchars_all_error(testdir):
result.stdout.fnmatch_lines(["ERROR*test_foo*"]) result.stdout.fnmatch_lines(["ERROR*test_foo*"])
@pytest.mark.xfail("hasattr(sys, 'pypy_version_info')")
def test_errors_in_xfail_skip_expressions(testdir): def test_errors_in_xfail_skip_expressions(testdir):
testdir.makepyfile( testdir.makepyfile(
""" """
@ -931,6 +930,10 @@ def test_errors_in_xfail_skip_expressions(testdir):
if sys.platform.startswith("java"): if sys.platform.startswith("java"):
# XXX report this to java # XXX report this to java
markline = "*" + markline[8:] markline = "*" + markline[8:]
elif hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (6,):
markline = markline[5:]
elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"):
markline = markline[4:]
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"*ERROR*test_nameerror*", "*ERROR*test_nameerror*",