Merge remote-tracking branch 'upstream/features' into features

This commit is contained in:
turturica 2018-04-18 00:11:03 -07:00
commit 7d923c389e
61 changed files with 131 additions and 160 deletions

View File

@ -191,6 +191,7 @@ Tareq Alayan
Ted Xiao
Thomas Grainger
Thomas Hisch
Tim Strazny
Tom Dalton
Tom Viner
Trevor Bekolay

View File

@ -136,7 +136,7 @@ Trivial/Internal Changes
- Internal ``mark.py`` module has been turned into a package. (`#3250
<https://github.com/pytest-dev/pytest/issues/3250>`_)
- ``pytest`` now depends on the `more_itertools
- ``pytest`` now depends on the `more-itertools
<https://github.com/erikrose/more-itertools>`_ package. (`#3265
<https://github.com/pytest-dev/pytest/issues/3265>`_)

View File

@ -12,7 +12,9 @@ import struct
import sys
import types
import atomicwrites
import py
from _pytest.assertion import util
@ -140,7 +142,7 @@ class AssertionRewritingHook(object):
# Probably a SyntaxError in the test.
return None
if write:
_make_rewritten_pyc(state, source_stat, pyc, co)
_write_pyc(state, co, source_stat, pyc)
else:
state.trace("found cached rewritten pyc for %r" % (fn,))
self.modules[name] = co, pyc
@ -258,22 +260,21 @@ def _write_pyc(state, co, source_stat, pyc):
# sometime to be able to use imp.load_compiled to load them. (See
# the comment in load_module above.)
try:
fp = open(pyc, "wb")
except IOError:
err = sys.exc_info()[1].errno
state.trace("error writing pyc file at %s: errno=%s" % (pyc, err))
with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp:
fp.write(imp.get_magic())
mtime = int(source_stat.mtime)
size = source_stat.size & 0xFFFFFFFF
fp.write(struct.pack("<ll", mtime, size))
if six.PY2:
marshal.dump(co, fp.file)
else:
marshal.dump(co, fp)
except EnvironmentError as e:
state.trace("error writing pyc file at %s: errno=%s" % (pyc, e.errno))
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, __pycache__ being a
# file etc.
return False
try:
fp.write(imp.get_magic())
mtime = int(source_stat.mtime)
size = source_stat.size & 0xFFFFFFFF
fp.write(struct.pack("<ll", mtime, size))
marshal.dump(co, fp)
finally:
fp.close()
return True
@ -338,20 +339,6 @@ def _rewrite_test(config, fn):
return stat, co
def _make_rewritten_pyc(state, source_stat, pyc, co):
"""Try to dump rewritten code to *pyc*."""
if sys.platform.startswith("win"):
# Windows grants exclusive access to open files and doesn't have atomic
# rename, so just write into the final file.
_write_pyc(state, co, source_stat, pyc)
else:
# When not on windows, assume rename is atomic. Dump the code object
# into a file specific to this process and atomically replace it.
proc_pyc = pyc + "." + str(os.getpid())
if _write_pyc(state, co, source_stat, proc_pyc):
os.rename(proc_pyc, pyc)
def _read_pyc(source, pyc, trace=lambda x: None):
"""Possibly read a pytest pyc containing rewritten code.

View File

@ -5,7 +5,7 @@ import pprint
import _pytest._code
import py
import six
from collections import Sequence
from ..compat import Sequence
u = six.text_type

View File

@ -38,6 +38,14 @@ PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
if _PY3:
from collections.abc import MutableMapping as MappingMixin # noqa
from collections.abc import Sequence # noqa
else:
# those raise DeprecationWarnings in Python >=3.7
from collections import MutableMapping as MappingMixin # noqa
from collections import Sequence # noqa
def _format_args(func):
return str(signature(func))

View File

@ -413,14 +413,15 @@ def pytest_fixture_post_finalizer(fixturedef, request):
def pytest_sessionstart(session):
""" before session.main() is called.
""" called after the ``Session`` object has been created and before performing collection
and entering the run test loop.
:param _pytest.main.Session session: the pytest session object
"""
def pytest_sessionfinish(session, exitstatus):
""" whole test run finishes.
""" called after whole test run finished, right before returning the exit status to the system.
:param _pytest.main.Session session: the pytest session object
:param int exitstatus: the status which pytest will return to the system

View File

@ -245,11 +245,6 @@ def record_property(request):
def test_function(record_property):
record_property("example_key", 1)
"""
request.node.warn(
code='C3',
message='record_property is an experimental feature',
)
def append_property(name, value):
request.node.user_properties.append((name, value))
return append_property

View File

@ -20,7 +20,8 @@ class MarkerError(Exception):
def param(*values, **kw):
"""Specify a parameter in a `pytest.mark.parametrize`_ call.
"""Specify a parameter in `pytest.mark.parametrize`_ calls or
:ref:`parametrized fixtures <fixture-parametrize-marks>`.
.. code-block:: python

View File

@ -1,12 +1,12 @@
from collections import namedtuple, MutableMapping as MappingMixin
import warnings
from operator import attrgetter
import inspect
import warnings
from collections import namedtuple
from operator import attrgetter
import attr
from ..deprecated import MARK_PARAMETERSET_UNPACKING, MARK_INFO_ATTRIBUTE
from ..compat import NOTSET, getfslineno
from ..compat import NOTSET, getfslineno, MappingMixin
from six.moves import map, reduce

View File

@ -2,6 +2,7 @@ import math
import sys
import py
from six import binary_type, text_type
from six.moves import zip, filterfalse
from more_itertools.more import always_iterable
@ -584,7 +585,8 @@ def raises(expected_exception, *args, **kwargs):
"""
__tracebackhide__ = True
for exc in filterfalse(isclass, always_iterable(expected_exception)):
base_type = (type, text_type, binary_type)
for exc in filterfalse(isclass, always_iterable(expected_exception, base_type)):
msg = ("exceptions must be old-style classes or"
" derived from BaseException, not %s")
raise TypeError(msg % type(exc))
@ -597,6 +599,10 @@ def raises(expected_exception, *args, **kwargs):
message = kwargs.pop("message")
if "match" in kwargs:
match_expr = kwargs.pop("match")
if kwargs:
msg = 'Unexpected keyword arguments passed to pytest.raises: '
msg += ', '.join(kwargs.keys())
raise TypeError(msg)
return RaisesContext(expected_exception, message, match_expr)
elif isinstance(args[0], str):
code, = args

View File

@ -1 +0,0 @@
New ``--show-capture`` command-line option that allows to specify how to display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default).

View File

@ -1 +0,0 @@
New ``--rootdir`` command-line option to override the rules for discovering the root directory. See `customize <https://docs.pytest.org/en/latest/customize.html>`_ in the documentation for details.

View File

@ -1 +0,0 @@
Added a `reference <https://docs.pytest.org/en/latest/reference.html>`_ page to the docs.

View File

@ -1 +0,0 @@
Suppress ``IOError`` when closing the temporary file used for capturing streams in Python 2.7.

View File

@ -1 +0,0 @@
Fixtures are now instantiated based on their scopes, with higher-scoped fixtures (such as ``session``) being instantiated first than lower-scoped fixtures (such as ``function``). The relative order of fixtures of the same scope is kept unchanged, based in their declaration order and their dependencies.

View File

@ -1,2 +0,0 @@
``record_xml_property`` renamed to ``record_property`` and is now compatible with xdist, markers and any reporter.
``record_xml_property`` name is now deprecated.

View File

@ -1 +0,0 @@
``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``.

View File

@ -0,0 +1 @@
A rare race-condition which might result in corrupted ``.pyc`` files on Windows has been hopefully solved.

View File

@ -0,0 +1 @@
``pytest`` now depends on the `python-atomicwrites <https://github.com/untitaker/python-atomicwrites>`_ library.

View File

@ -1 +0,0 @@
New ``--nf``, ``--new-first`` options: run new tests first followed by the rest of the tests, in both cases tests are also sorted by the file modified time, with more recent files coming first.

View File

@ -1 +0,0 @@
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py files, because they "leak" to the entire directory tree.

View File

@ -1 +0,0 @@
New ``--last-failed-no-failures`` command-line option that allows to specify the behavior of the cache plugin's ```--last-failed`` feature when no tests failed in the last run (or no cache was found): ``none`` or ``all`` (the default).

View File

@ -1 +0,0 @@
New ``--doctest-continue-on-failure`` command-line option to enable doctests to show multiple failures for each snippet, instead of stopping at the first failure.

View File

@ -1 +0,0 @@
Captured log messages are added to the ``<system-out>`` tag in the generated junit xml file if the ``junit_logging`` ini option is set to ``system-out``. If the value of this ini option is ``system-err`, the logs are written to ``<system-err>``. The default value for ``junit_logging`` is ``no``, meaning captured logs are not written to the output file.

View File

@ -1 +0,0 @@
Allow the logging plugin to handle ``pytest_runtest_logstart`` and ``pytest_runtest_logfinish`` hooks when live logs are enabled.

View File

@ -1 +0,0 @@
Passing `--log-cli-level` in the command-line now automatically activates live logging.

View File

@ -1 +0,0 @@
Add command line option ``--deselect`` to allow deselection of individual tests at collection time.

View File

@ -1 +0,0 @@
Captured logs are printed before entering pdb.

View File

@ -1 +0,0 @@
Deselected item count is now shown before tests are run, e.g. ``collected X items / Y deselected``.

View File

@ -1 +0,0 @@
Change minimum requirement of ``attrs`` to ``17.4.0``.

View File

@ -1 +0,0 @@
The builtin module ``platform`` is now available for use in expressions in ``pytest.mark``.

View File

@ -1 +0,0 @@
Renamed example directories so all tests pass when ran from the base directory.

View File

@ -1 +0,0 @@
Remove usage of deprecated ``metafunc.addcall`` in our own tests.

View File

@ -1 +0,0 @@
Internal ``mark.py`` module has been turned into a package.

View File

@ -1 +0,0 @@
The *short test summary info* section now is displayed after tracebacks and warnings in the terminal.

View File

@ -1 +0,0 @@
``pytest`` now depends on the `more_itertools <https://github.com/erikrose/more-itertools>`_ package.

View File

@ -1 +0,0 @@
Added warning when ``[pytest]`` section is used in a ``.cfg`` file passed with ``-c``

View File

@ -1 +0,0 @@
``nodeids`` can now be passed explicitly to ``FSCollector`` and ``Node`` constructors.

View File

@ -1 +0,0 @@
Internal refactoring of ``FormattedExcinfo`` to use ``attrs`` facilities and remove old support code for legacy Python versions.

View File

@ -1 +0,0 @@
New ``--verbosity`` flag to set verbosity level explicitly.

View File

@ -1 +0,0 @@
Refactoring to unify how verbosity is handled internally.

View File

@ -1,2 +0,0 @@
Fixed ``clear()`` method on ``caplog`` fixture which cleared ``records``,
but not the ``text`` property.

View File

@ -1 +0,0 @@
Internal refactoring to better integrate with argparse.

View File

@ -1 +0,0 @@
Fix a python example when calling a fixture in doc/en/usage.rst

View File

@ -1 +0,0 @@
``pytest.approx`` now accepts comparing a numpy array with a scalar.

View File

@ -1,3 +0,0 @@
During test collection, when stdin is not allowed to be read, the
``DontReadFromStdin`` object still allow itself to be iterable and
resolved to an iterator without crashing.

1
changelog/3339.trivial Normal file
View File

@ -0,0 +1 @@
Import some modules from ``collections`` instead of ``collections.abc`` as the former modules trigger ``DeprecationWarning`` in Python 3.7.

View File

@ -0,0 +1 @@
``pytest.raises`` now raises ``TypeError`` when receiving an unknown keyword argument.

2
changelog/3360.trivial Normal file
View File

@ -0,0 +1,2 @@
record_property is no longer experimental, removing the warnings was forgotten.

View File

@ -0,0 +1 @@
``pytest.raises`` now works with exception classes that look like iterables.

View File

@ -2,7 +2,7 @@
Development Guide
=================
Some general guidelines regarding development in pytest for core maintainers and general contributors. Nothing here
Some general guidelines regarding development in pytest for maintainers and contributors. Nothing here
is set in stone and can't be changed, feel free to suggest improvements or changes in the workflow.
@ -37,72 +37,19 @@ Any question, feature, bug or proposal is welcome as an issue. Users are encoura
GitHub issues should use labels to categorize them. Labels should be created sporadically, to fill a niche; we should
avoid creating labels just for the sake of creating them.
Here is a list of labels and a brief description mentioning their intent.
Each label should include a description in the GitHub's interface stating its purpose.
Temporary labels
~~~~~~~~~~~~~~~~
**Type**
To classify issues for a special event it is encouraged to create a temporary label. This helps those involved to find
the relevant issues to work on. Examples of that are sprints in Python events or global hacking events.
* ``type: backward compatibility``: issue that will cause problems with old pytest versions.
* ``type: bug``: problem that needs to be addressed.
* ``type: deprecation``: feature that will be deprecated in the future.
* ``type: docs``: documentation missing or needing clarification.
* ``type: enhancement``: new feature or API change, should be merged into ``features``.
* ``type: feature-branch``: new feature or API change, should be merged into ``features``.
* ``type: infrastructure``: improvement to development/releases/CI structure.
* ``type: performance``: performance or memory problem/improvement.
* ``type: proposal``: proposal for a new feature, often to gather opinions or design the API around the new feature.
* ``type: question``: question regarding usage, installation, internals or how to test something.
* ``type: refactoring``: internal improvements to the code.
* ``type: regression``: indicates a problem that was introduced in a release which was working previously.
* ``temporary: EP2017 sprint``: candidate issues or PRs tackled during the EuroPython 2017
**Status**
Issues created at those events should have other relevant labels added as well.
* ``status: critical``: grave problem or usability issue that affects lots of users.
* ``status: easy``: easy issue that is friendly to new contributors.
* ``status: help wanted``: core developers need help from experts on this topic.
* ``status: needs information``: reporter needs to provide more information; can be closed after 2 or more weeks of inactivity.
**Topic**
* ``topic: collection``
* ``topic: fixtures``
* ``topic: parametrize``
* ``topic: reporting``
* ``topic: selection``
* ``topic: tracebacks``
**Plugin (internal or external)**
* ``plugin: cache``
* ``plugin: capture``
* ``plugin: doctests``
* ``plugin: junitxml``
* ``plugin: monkeypatch``
* ``plugin: nose``
* ``plugin: pastebin``
* ``plugin: pytester``
* ``plugin: tmpdir``
* ``plugin: unittest``
* ``plugin: warnings``
* ``plugin: xdist``
**OS**
Issues specific to a single operating system. Do not use as a means to indicate where an issue originated from, only
for problems that happen **only** in that system.
* ``os: linux``
* ``os: mac``
* ``os: windows``
**Temporary**
Used to classify issues for limited time, to help find issues related in events for example.
They should be removed after they are no longer relevant.
* ``temporary: EP2017 sprint``:
* ``temporary: sprint-candidate``:
Those labels should be removed after they are no longer relevant.
.. include:: ../../HOWTORELEASE.rst

View File

@ -358,7 +358,7 @@ get on the terminal - we are working on that)::
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:609>:1: ValueError
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:613>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises object at 0xdeadbeef>

View File

@ -294,7 +294,7 @@ The fixtures requested by ``test_foo`` will be instantiated in the following ord
1. ``s1``: is the highest-scoped fixture (``session``).
2. ``m1``: is the second highest-scoped fixture (``module``).
3. ``tempdir``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point
3. ``tmpdir``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point
because it is a dependency of ``f1``.
4. ``f1``: is the first ``function``-scoped fixture in ``test_foo`` parameter list.
5. ``f2``: is the last ``function``-scoped fixture in ``test_foo`` parameter list.
@ -623,6 +623,40 @@ Running the above tests results in the following test IDs being used::
======================= no tests ran in 0.12 seconds =======================
.. _`fixture-parametrize-marks`:
Using marks with parametrized fixtures
--------------------------------------
:func:`pytest.param` can be used to apply marks in values sets of parametrized fixtures in the same way
that they can be used with :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>`.
Example::
# content of test_fixture_marks.py
import pytest
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
def data_set(request):
return request.param
def test_data(data_set):
pass
Running this test will *skip* the invocation of ``data_set`` with value ``2``::
$ pytest test_fixture_marks.py -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 3 items
test_fixture_marks.py::test_data[0] PASSED [ 33%]
test_fixture_marks.py::test_data[1] PASSED [ 66%]
test_fixture_marks.py::test_data[2] SKIPPED [100%]
=================== 2 passed, 1 skipped in 0.12 seconds ====================
.. _`interdependent fixtures`:
Modularity: using fixtures from a fixture function

View File

@ -529,6 +529,8 @@ Initialization hooks called for plugins and ``conftest.py`` files.
.. autofunction:: pytest_addhooks
.. autofunction:: pytest_configure
.. autofunction:: pytest_unconfigure
.. autofunction:: pytest_sessionstart
.. autofunction:: pytest_sessionfinish
Test running hooks
~~~~~~~~~~~~~~~~~~

View File

@ -85,8 +85,8 @@ sub directory but not for other directories::
Here is how you might run it::
pytest test_flat.py # will not show "setting up"
pytest a/test_sub.py # will show "setting up"
    pytest test_flat.py --capture=no # will not show "setting up"
pytest a/test_sub.py --capture=no # will show "setting up"
.. note::
If you have ``conftest.py`` files which do not reside in a

View File

@ -60,7 +60,8 @@ def main():
'six>=1.10.0',
'setuptools',
'attrs>=17.4.0',
'more_itertools>=4.0.0',
'more-itertools>=4.0.0',
'atomicwrites>=1.0',
]
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
# used by tox.ini to test with pluggy master

View File

@ -1,3 +1,4 @@
from _pytest.outcomes import Failed
import pytest
import sys
@ -61,6 +62,11 @@ class TestRaises(object):
with pytest.raises(TypeError):
pytest.raises('wrong', lambda: None)
def test_invalid_arguments_to_raises(self):
with pytest.raises(TypeError, match='unknown'):
with pytest.raises(TypeError, unknown='bogus'):
raise ValueError()
def test_tuple(self):
with pytest.raises((KeyError, ValueError)):
raise KeyError('oops')
@ -142,3 +148,20 @@ class TestRaises(object):
with pytest.raises(ValueError):
with pytest.raises(IndexError, match='nomatch'):
int('asdf')
def test_raises_exception_looks_iterable(self):
from six import add_metaclass
class Meta(type(object)):
def __getitem__(self, item):
return 1/0
def __len__(self):
return 1
@add_metaclass(Meta)
class ClassLooksIterableException(Exception):
pass
with pytest.raises(Failed, match="DID NOT RAISE <class 'raises.ClassLooksIterableException'>"):
pytest.raises(ClassLooksIterableException, lambda: None)

View File

@ -839,22 +839,22 @@ class TestAssertionRewriteHookDetails(object):
def test_write_pyc(self, testdir, tmpdir, monkeypatch):
from _pytest.assertion.rewrite import _write_pyc
from _pytest.assertion import AssertionState
try:
import __builtin__ as b
except ImportError:
import builtins as b
import atomicwrites
from contextlib import contextmanager
config = testdir.parseconfig([])
state = AssertionState(config, "rewrite")
source_path = tmpdir.ensure("source.py")
pycpath = tmpdir.join("pyc").strpath
assert _write_pyc(state, [1], source_path.stat(), pycpath)
def open(*args):
@contextmanager
def atomic_write_failed(fn, mode='r', overwrite=False):
e = IOError()
e.errno = 10
raise e
yield # noqa
monkeypatch.setattr(b, "open", open)
monkeypatch.setattr(atomicwrites, "atomic_write", atomic_write_failed)
assert not _write_pyc(state, [1], source_path.stat(), pycpath)
def test_resources_provider_for_loader(self, testdir):

View File

@ -868,17 +868,13 @@ def test_record_property(testdir):
def test_record(record_property, other):
record_property("foo", "<1");
""")
result, dom = runandparse(testdir, '-rw')
result, dom = runandparse(testdir, '-rwv')
node = dom.find_first_by_tag("testsuite")
tnode = node.find_first_by_tag("testcase")
psnode = tnode.find_first_by_tag('properties')
pnodes = psnode.find_by_tag('property')
pnodes[0].assert_attr(name="bar", value="1")
pnodes[1].assert_attr(name="foo", value="<1")
result.stdout.fnmatch_lines([
'test_record_property.py::test_record',
'*record_property*experimental*',
])
def test_record_property_same_name(testdir):

View File

@ -113,7 +113,7 @@ class TestDeprecatedCall(object):
pass
msg = 'Did not produce DeprecationWarning or PendingDeprecationWarning'
with pytest.raises(AssertionError, matches=msg):
with pytest.raises(AssertionError, match=msg):
if mode == 'call':
pytest.deprecated_call(f)
else:

View File

@ -128,7 +128,7 @@ usedevelop = True
changedir = doc/en
deps =
attrs
more_itertools
more-itertools
PyYAML
sphinx
sphinxcontrib-trio