Merge pull request #4645 from blueyed/merge-master-into-features

Merge master into features
This commit is contained in:
Anthony Sottile 2019-01-14 14:41:27 -08:00 committed by GitHub
commit 4947eb85c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 278 additions and 66 deletions

2
.github/config.yml vendored Normal file
View File

@ -0,0 +1,2 @@
rtd:
project: pytest

View File

@ -24,6 +24,7 @@ Andy Freeland
Anthon van der Neut
Anthony Shaw
Anthony Sottile
Anton Lodder
Antony Lee
Armin Rigo
Aron Coyle
@ -176,6 +177,7 @@ Oliver Bestwalter
Omar Kohl
Omer Hadari
Ondřej Súkup
Oscar Benjamin
Patrick Hayes
Paweł Adamczak
Pedro Algarvio

View File

@ -18,6 +18,38 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 4.1.1 (2019-01-12)
=========================
Bug Fixes
---------
- `#2256 <https://github.com/pytest-dev/pytest/issues/2256>`_: Show full repr with ``assert a==b`` and ``-vv``.
- `#3456 <https://github.com/pytest-dev/pytest/issues/3456>`_: Extend Doctest-modules to ignore mock objects.
- `#4617 <https://github.com/pytest-dev/pytest/issues/4617>`_: Fixed ``pytest.warns`` bug when context manager is reused (e.g. multiple parametrization).
- `#4631 <https://github.com/pytest-dev/pytest/issues/4631>`_: Don't rewrite assertion when ``__getattr__`` is broken
Improved Documentation
----------------------
- `#3375 <https://github.com/pytest-dev/pytest/issues/3375>`_: Document that using ``setup.cfg`` may crash other tools or cause hard to track down problems because it uses a different parser than ``pytest.ini`` or ``tox.ini`` files.
Trivial/Internal Changes
------------------------
- `#4602 <https://github.com/pytest-dev/pytest/issues/4602>`_: Uninstall ``hypothesis`` in regen tox env.
pytest 4.1.0 (2019-01-05)
=========================

View File

@ -1 +0,0 @@
Uninstall ``hypothesis`` in regen tox env.

View File

@ -1 +0,0 @@
Fixed ``pytest.warns`` bug when context manager is reused (e.g. multiple parametrization).

View File

@ -0,0 +1,3 @@
Use ``a.item()`` instead of the deprecated ``np.asscalar(a)`` in ``pytest.approx``.
``np.asscalar`` has been `deprecated <https://github.com/numpy/numpy/blob/master/doc/release/1.16.0-notes.rst#new-deprecations>`__ in ``numpy 1.16.``.

View File

@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2
release-4.1.1
release-4.1.0
release-4.0.2
release-4.0.1

View File

@ -0,0 +1,27 @@
pytest-4.1.1
=======================================
pytest 4.1.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
* Anton Lodder
* Bruno Oliveira
* Daniel Hahler
* David Vo
* Oscar Benjamin
* Ronny Pfannschmidt
* Victor Maryama
* Yoav Caspi
* dmitry.dygalo
Happy testing,
The pytest Development Team

View File

@ -214,6 +214,8 @@ If you run this command for the first time, you can see the print statement:
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
E -42
E +23
test_caching.py:17: AssertionError
-------------------------- Captured stdout setup ---------------------------
@ -235,6 +237,8 @@ the cache and nothing will be printed:
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
E -42
E +23
test_caching.py:17: AssertionError
1 failed in 0.12 seconds

View File

@ -42,10 +42,11 @@ todo_include_todos = 1
extensions = [
"pygments_pytest",
"sphinx.ext.autodoc",
"sphinx.ext.todo",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
"sphinx_removed_in",
"sphinxcontrib_trio",
]

View File

@ -101,7 +101,7 @@ an appropriate period of deprecation has passed.
Using ``Class`` in custom Collectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector``
subclasses has been deprecated. Users instead should use ``pytest_pycollect_makeitem`` to customize node types during
@ -114,7 +114,7 @@ message please contact the authors so they can change the code.
marks in ``pytest.mark.parametrize``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
@ -162,7 +162,7 @@ To update the code, use ``pytest.param``:
``pytest_funcarg__`` prefix
~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix:
@ -184,7 +184,7 @@ Switch over to the ``@pytest.fixture`` decorator:
[pytest] section in setup.cfg files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
to avoid conflicts with other distutils commands.
@ -193,7 +193,7 @@ to avoid conflicts with other distutils commands.
Metafunc.addcall
~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use
:meth:`_pytest.python.Metafunc.parametrize` instead.
@ -217,7 +217,7 @@ Becomes:
``cached_setup``
~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
``request.cached_setup`` was the precursor of the setup/teardown mechanism available to fixtures.
@ -249,7 +249,7 @@ more information.
pytest_plugins in non-top-level conftest files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
@ -259,7 +259,7 @@ features ``conftest.py`` files are only *active* for tests at or below it.
``Config.warn`` and ``Node.warn``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
system for its own warnings, so those two functions are now deprecated.
@ -286,7 +286,7 @@ Becomes:
record_xml_property
~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which
can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run.
@ -309,7 +309,7 @@ Change to:
Passing command-line string to ``pytest.main()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Passing a command-line string to ``pytest.main()`` is deprecated:
@ -331,7 +331,7 @@ on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to
Calling fixtures directly
~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
@ -384,7 +384,7 @@ with the ``name`` parameter:
``yield`` tests
~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
pytest supported ``yield``-style tests, where a test function actually ``yield`` functions and values
that are then turned into proper test methods. Example:
@ -412,7 +412,7 @@ This form of test function doesn't support fixtures properly, and users should s
Internal classes accessed through ``Node``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances now issue
this warning::
@ -423,10 +423,28 @@ Users should just ``import pytest`` and access those objects using the ``pytest`
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
``Node.get_marker``
~~~~~~~~~~~~~~~~~~~
.. versionremoved:: 4.0
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
:ref:`the documentation <update marker code>` on tips on how to update your code.
``somefunction.markname``
~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionremoved:: 4.0
As part of a large :ref:`marker-revamp` we already deprecated using ``MarkInfo``
the only correct way to get markers of an element is via ``node.iter_markers(name)``.
``pytest_namespace``
~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
.. versionremoved:: 4.0
This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some
bug fixes and refactorings impossible.
@ -461,7 +479,7 @@ As a stopgap measure, plugin authors may still inject their names into pytest's
Reinterpretation mode (``--assert=reinterp``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
.. versionremoved:: 3.0
Reinterpretation mode has now been removed and only plain and rewrite
mode are available, consequently the ``--assert=reinterp`` option is
@ -473,7 +491,7 @@ explicitly turn on assertion rewriting for those files.
Removed command-line options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
.. versionremoved:: 3.0
The following deprecated commandline options were removed:
@ -485,27 +503,9 @@ The following deprecated commandline options were removed:
py.test-X* entry points
~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
.. versionremoved:: 3.0
Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
were never documented and a leftover from a pre-virtualenv era. These entry
points also created broken entry points in wheels, so removing them also
removes a source of confusion for users.
``Node.get_marker``
~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0*
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
:ref:`the documentation <update marker code>` on tips on how to update your code.
``somefunction.markname``
~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0*
As part of a large :ref:`marker-revamp` we already deprecated using ``MarkInfo``
the only correct way to get markers of an element is via ``node.iter_markers(name)``.

View File

@ -406,6 +406,8 @@ argument sets to use for each test function. Let's run it:
def test_equals(self, a, b):
> assert a == b
E assert 1 == 2
E -1
E +2
test_parametrize.py:18: AssertionError
1 failed, 2 passed in 0.12 seconds

View File

@ -72,8 +72,18 @@ to keep tests separate from actual application code (often a good idea)::
test_view.py
...
This way your tests can run easily against an installed version
of ``mypkg``.
This has the following benefits:
* Your tests can run against an installed version after executing ``pip install .``.
* Your tests can run against the local copy with an editable install after executing ``pip install --editable .``.
* If you don't have a ``setup.py`` file and are relying on the fact that Python by default puts the current
directory in ``sys.path`` to import your package, you can execute ``python -m pytest`` to execute the tests against the
local copy directly, without using ``pip``.
.. note::
See :ref:`pythonpath` for more information about the difference between calling ``pytest`` and
``python -m pytest``.
Note that using this scheme your test files must have **unique names**, because
``pytest`` will import them as *top-level* modules since there are no packages

View File

@ -889,6 +889,12 @@ Here is a list of builtin configuration options that may be written in a ``pytes
file, usually located at the root of your repository. All options must be under a ``[pytest]`` section
(``[tool:pytest]`` for ``setup.cfg`` files).
.. warning::
Usage of ``setup.cfg`` is not recommended unless for very simple use cases. ``.cfg``
files use a different parser than ``pytest.ini`` and ``tox.ini`` which might cause hard to track
down problems.
When possible, it is recommended to use the latter files to hold your pytest configuration.
Configuration file options may be overwritten in the command-line by using ``-o/--override``, which can also be
passed multiple times. The expected format is ``name=value``. For example::

View File

@ -1,3 +1,4 @@
pygments-pytest>=1.1.0
sphinx>=1.8.2
sphinxcontrib-trio
sphinx-removed-in>=0.1.3

View File

@ -512,7 +512,13 @@ def _format_assertmsg(obj):
def _should_repr_global_name(obj):
return not hasattr(obj, "__name__") and not callable(obj)
if callable(obj):
return False
try:
return not hasattr(obj, "__name__")
except Exception:
return True
def _format_boolop(explanations, is_or):

View File

@ -151,6 +151,8 @@ def assertrepr_compare(config, op, left, right):
elif type(left) == type(right) and (isdatacls(left) or isattrs(left)):
type_fn = (isdatacls, isattrs)
explanation = _compare_eq_cls(left, right, verbose, type_fn)
elif verbose:
explanation = _compare_eq_verbose(left, right)
if isiterable(left) and isiterable(right):
expl = _compare_eq_iterable(left, right, verbose)
if explanation is not None:
@ -236,6 +238,18 @@ def _diff_text(left, right, verbose=False):
return explanation
def _compare_eq_verbose(left, right):
keepends = True
left_lines = repr(left).splitlines(keepends)
right_lines = repr(right).splitlines(keepends)
explanation = []
explanation += [u"-" + line for line in left_lines]
explanation += [u"+" + line for line in right_lines]
return explanation
def _compare_eq_iterable(left, right, verbose=False):
if not verbose:
return [u"Use -v to get the full diff"]

View File

@ -3,17 +3,19 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import inspect
import platform
import sys
import traceback
from contextlib import contextmanager
import pytest
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ReprFileLocation
from _pytest._code.code import TerminalRepr
from _pytest.compat import safe_getattr
from _pytest.fixtures import FixtureRequest
DOCTEST_REPORT_CHOICE_NONE = "none"
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
DOCTEST_REPORT_CHOICE_NDIFF = "ndiff"
@ -346,10 +348,61 @@ def _check_all_skipped(test):
pytest.skip("all tests skipped by +SKIP option")
def _is_mocked(obj):
"""
returns if a object is possibly a mock object by checking the existence of a highly improbable attribute
"""
return (
safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None)
is not None
)
@contextmanager
def _patch_unwrap_mock_aware():
"""
contextmanager which replaces ``inspect.unwrap`` with a version
that's aware of mock objects and doesn't recurse on them
"""
real_unwrap = getattr(inspect, "unwrap", None)
if real_unwrap is None:
yield
else:
def _mock_aware_unwrap(obj, stop=None):
if stop is None:
return real_unwrap(obj, stop=_is_mocked)
else:
return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj))
inspect.unwrap = _mock_aware_unwrap
try:
yield
finally:
inspect.unwrap = real_unwrap
class DoctestModule(pytest.Module):
def collect(self):
import doctest
class MockAwareDocTestFinder(doctest.DocTestFinder):
"""
a hackish doctest finder that overrides stdlib internals to fix a stdlib bug
https://github.com/pytest-dev/pytest/issues/3456
https://bugs.python.org/issue25532
"""
def _find(self, tests, obj, name, module, source_lines, globs, seen):
if _is_mocked(obj):
return
with _patch_unwrap_mock_aware():
doctest.DocTestFinder._find(
self, tests, obj, name, module, source_lines, globs, seen
)
if self.fspath.basename == "conftest.py":
module = self.config.pluginmanager._importconftest(self.fspath)
else:
@ -361,7 +414,7 @@ class DoctestModule(pytest.Module):
else:
raise
# uses internal doctest module parsing mechanism
finder = doctest.DocTestFinder()
finder = MockAwareDocTestFinder()
optionflags = get_optionflags(self)
runner = _get_runner(
verbose=0,

View File

@ -150,10 +150,10 @@ class ApproxNumpy(ApproxBase):
if np.isscalar(actual):
for i in np.ndindex(self.expected.shape):
yield actual, np.asscalar(self.expected[i])
yield actual, self.expected[i].item()
else:
for i in np.ndindex(self.expected.shape):
yield np.asscalar(actual[i]), np.asscalar(self.expected[i])
yield actual[i].item(), self.expected[i].item()
class ApproxMapping(ApproxBase):

View File

@ -488,6 +488,30 @@ class TestAssert_reprcompare(object):
expl = callequal([(1, 2)], [])
assert len(expl) > 1
def test_repr_verbose(self):
class Nums:
def __init__(self, nums):
self.nums = nums
def __repr__(self):
return str(self.nums)
list_x = list(range(5000))
list_y = list(range(5000))
list_y[len(list_y) // 2] = 3
nums_x = Nums(list_x)
nums_y = Nums(list_y)
assert callequal(nums_x, nums_y) is None
expl = callequal(nums_x, nums_y, verbose=1)
assert "-" + repr(nums_x) in expl
assert "+" + repr(nums_y) in expl
expl = callequal(nums_x, nums_y, verbose=2)
assert "-" + repr(nums_x) in expl
assert "+" + repr(nums_y) in expl
def test_list_bad_repr(self):
class A(object):
def __repr__(self):

View File

@ -180,6 +180,27 @@ class TestAssertionRewrite(object):
assert getmsg(f, {"cls": X}) == "assert cls == 42"
def test_dont_rewrite_if_hasattr_fails(self):
class Y(object):
""" A class whos getattr fails, but not with `AttributeError` """
def __getattr__(self, attribute_name):
raise KeyError()
def __repr__(self):
return "Y"
def __init__(self):
self.foo = 3
def f():
assert cls().foo == 2 # noqa
message = getmsg(f, {"cls": Y})
assert "assert 3 == 2" in message
assert "+ where 3 = Y.foo" in message
assert "+ where Y = cls()" in message
def test_assert_already_has_message(self):
def f():
assert False, "something bad!"

View File

@ -1206,3 +1206,22 @@ class TestDoctestReportingOption(object):
"*error: argument --doctest-report: invalid choice: 'obviously_invalid_format' (choose from*"
]
)
@pytest.mark.parametrize("mock_module", ["mock", "unittest.mock"])
def test_doctest_mock_objects_dont_recurse_missbehaved(mock_module, testdir):
pytest.importorskip(mock_module)
testdir.makepyfile(
"""
from {mock_module} import call
class Example(object):
'''
>>> 1 + 1
2
'''
""".format(
mock_module=mock_module
)
)
result = testdir.runpytest("--doctest-modules")
result.stdout.fnmatch_lines(["* 1 passed *"])

View File

@ -2,6 +2,7 @@
from __future__ import unicode_literals
import sys
import warnings
import six
@ -685,25 +686,10 @@ class TestAssertionWarnings:
result.stdout.fnmatch_lines(["*1 failed in*"])
def test_warningschecker_twice(testdir):
def test_warnings_checker_twice():
"""Issue #4617"""
testdir.makepyfile(
"""
import pytest
import warnings
@pytest.mark.parametrize("other", [1, 2])
@pytest.mark.parametrize("expectation", [
pytest.warns(DeprecationWarning,
match="Message A"),
pytest.warns(DeprecationWarning,
match="Message A"),
])
def test_parametrized_warnings(other, expectation):
expectation = pytest.warns(UserWarning)
with expectation:
warnings.warn("Message A", DeprecationWarning)
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(["* 4 passed in *"])
warnings.warn("Message A", UserWarning)
with expectation:
warnings.warn("Message B", UserWarning)