Merge pull request #4926 from blueyed/merge-master-into-features
Merge master into features
This commit is contained in:
commit
5c57d92978
|
@ -86,6 +86,9 @@ jobs:
|
||||||
- env: TOXENV=py36-xdist
|
- env: TOXENV=py36-xdist
|
||||||
python: '3.6'
|
python: '3.6'
|
||||||
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
|
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.cache/pre-commit
|
||||||
|
|
||||||
- stage: deploy
|
- stage: deploy
|
||||||
python: '3.6'
|
python: '3.6'
|
||||||
|
@ -144,7 +147,4 @@ notifications:
|
||||||
skip_join: true
|
skip_join: true
|
||||||
email:
|
email:
|
||||||
- pytest-commit@python.org
|
- pytest-commit@python.org
|
||||||
cache:
|
cache: false
|
||||||
directories:
|
|
||||||
- $HOME/.cache/pip
|
|
||||||
- $HOME/.cache/pre-commit
|
|
||||||
|
|
|
@ -18,6 +18,28 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 4.3.1 (2019-03-11)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#4810 <https://github.com/pytest-dev/pytest/issues/4810>`_: Logging messages inside ``pytest_runtest_logreport()`` are now properly captured and displayed.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4861 <https://github.com/pytest-dev/pytest/issues/4861>`_: Improve validation of contents written to captured output so it behaves the same as when capture is disabled.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4898 <https://github.com/pytest-dev/pytest/issues/4898>`_: Fix ``AttributeError: FixtureRequest has no 'confg' attribute`` bug in ``testdir.copy_example``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Trivial/Internal Changes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- `#4768 <https://github.com/pytest-dev/pytest/issues/4768>`_: Avoid pkg_resources import at the top-level.
|
||||||
|
|
||||||
|
|
||||||
pytest 4.3.0 (2019-02-16)
|
pytest 4.3.0 (2019-02-16)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Logging messages inside ``pytest_runtest_logreport()`` are now properly captured and displayed.
|
|
|
@ -1 +0,0 @@
|
||||||
Improve validation of contents written to captured output so it behaves the same as when capture is disabled.
|
|
|
@ -0,0 +1 @@
|
||||||
|
Use the correct modified time for years after 2038 in rewritten ``.pyc`` files.
|
|
@ -0,0 +1 @@
|
||||||
|
Remove deprecated Sphinx directive, ``add_description_unit()``.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix pytest tests invocation with custom ``PYTHONPATH``.
|
|
@ -6,6 +6,7 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-4.3.1
|
||||||
release-4.3.0
|
release-4.3.0
|
||||||
release-4.2.1
|
release-4.2.1
|
||||||
release-4.2.0
|
release-4.2.0
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
pytest-4.3.1
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 4.3.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:
|
||||||
|
|
||||||
|
* Andras Mitzki
|
||||||
|
* Anthony Sottile
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Daniel Hahler
|
||||||
|
* Danilo Horta
|
||||||
|
* Grygorii Iermolenko
|
||||||
|
* Jeff Hale
|
||||||
|
* Kyle Altendorf
|
||||||
|
* Stephan Hoyer
|
||||||
|
* Zac Hatfield-Dodds
|
||||||
|
* Zac-HD
|
||||||
|
* songbowen
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -335,7 +335,7 @@ intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
|
||||||
def setup(app):
|
def setup(app):
|
||||||
# from sphinx.ext.autodoc import cut_lines
|
# from sphinx.ext.autodoc import cut_lines
|
||||||
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
||||||
app.add_description_unit(
|
app.add_object_type(
|
||||||
"confval",
|
"confval",
|
||||||
"confval",
|
"confval",
|
||||||
objname="configuration value",
|
objname="configuration value",
|
||||||
|
|
|
@ -436,8 +436,10 @@ 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
|
||||||
........................... [100%]
|
...sss...sssssssss...sss... [100%]
|
||||||
27 passed in 0.12 seconds
|
========================= short test summary info ==========================
|
||||||
|
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
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
|
@ -584,6 +584,8 @@ Initialization hooks called for plugins and ``conftest.py`` files.
|
||||||
.. autofunction:: pytest_sessionstart
|
.. autofunction:: pytest_sessionstart
|
||||||
.. autofunction:: pytest_sessionfinish
|
.. autofunction:: pytest_sessionfinish
|
||||||
|
|
||||||
|
.. autofunction:: pytest_plugin_registered
|
||||||
|
|
||||||
Test running hooks
|
Test running hooks
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -607,6 +609,8 @@ into interactive debugging when a test failure occurs.
|
||||||
The :py:mod:`_pytest.terminal` reported specifically uses
|
The :py:mod:`_pytest.terminal` reported specifically uses
|
||||||
the reporting hook to print information about a test run.
|
the reporting hook to print information about a test run.
|
||||||
|
|
||||||
|
.. autofunction:: pytest_pyfunc_call
|
||||||
|
|
||||||
Collection hooks
|
Collection hooks
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -616,6 +620,7 @@ Collection hooks
|
||||||
.. autofunction:: pytest_ignore_collect
|
.. autofunction:: pytest_ignore_collect
|
||||||
.. autofunction:: pytest_collect_directory
|
.. autofunction:: pytest_collect_directory
|
||||||
.. autofunction:: pytest_collect_file
|
.. autofunction:: pytest_collect_file
|
||||||
|
.. autofunction:: pytest_pycollect_makemodule
|
||||||
|
|
||||||
For influencing the collection of objects in Python modules
|
For influencing the collection of objects in Python modules
|
||||||
you can use the following hook:
|
you can use the following hook:
|
||||||
|
@ -629,12 +634,15 @@ items, delete or otherwise amend the test items:
|
||||||
|
|
||||||
.. autofunction:: pytest_collection_modifyitems
|
.. autofunction:: pytest_collection_modifyitems
|
||||||
|
|
||||||
|
.. autofunction:: pytest_collection_finish
|
||||||
|
|
||||||
Reporting hooks
|
Reporting hooks
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Session related reporting hooks:
|
Session related reporting hooks:
|
||||||
|
|
||||||
.. autofunction:: pytest_collectstart
|
.. autofunction:: pytest_collectstart
|
||||||
|
.. autofunction:: pytest_make_collect_report
|
||||||
.. autofunction:: pytest_itemcollected
|
.. autofunction:: pytest_itemcollected
|
||||||
.. autofunction:: pytest_collectreport
|
.. autofunction:: pytest_collectreport
|
||||||
.. autofunction:: pytest_deselected
|
.. autofunction:: pytest_deselected
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
pygments-pytest>=1.1.0
|
pygments-pytest>=1.1.0
|
||||||
sphinx>=1.8.2
|
sphinx>=1.8.2,<2.0
|
||||||
sphinxcontrib-trio
|
sphinxcontrib-trio
|
||||||
sphinx-removed-in>=0.1.3
|
sphinx-removed-in>=0.1.3
|
||||||
|
|
|
@ -66,7 +66,6 @@ Running this would result in a passed test except for the last
|
||||||
test_tmp_path.py:13: AssertionError
|
test_tmp_path.py:13: AssertionError
|
||||||
========================= 1 failed in 0.12 seconds =========================
|
========================= 1 failed in 0.12 seconds =========================
|
||||||
|
|
||||||
|
|
||||||
.. _`tmp_path_factory example`:
|
.. _`tmp_path_factory example`:
|
||||||
|
|
||||||
The ``tmp_path_factory`` fixture
|
The ``tmp_path_factory`` fixture
|
||||||
|
|
|
@ -347,9 +347,11 @@ def _write_pyc(state, co, source_stat, pyc):
|
||||||
try:
|
try:
|
||||||
with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp:
|
with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp:
|
||||||
fp.write(imp.get_magic())
|
fp.write(imp.get_magic())
|
||||||
mtime = int(source_stat.mtime)
|
# as of now, bytecode header expects 32-bit numbers for size and mtime (#4903)
|
||||||
|
mtime = int(source_stat.mtime) & 0xFFFFFFFF
|
||||||
size = source_stat.size & 0xFFFFFFFF
|
size = source_stat.size & 0xFFFFFFFF
|
||||||
fp.write(struct.pack("<ll", mtime, size))
|
# "<LL" stands for 2 unsigned longs, little-ending
|
||||||
|
fp.write(struct.pack("<LL", mtime, size))
|
||||||
fp.write(marshal.dumps(co))
|
fp.write(marshal.dumps(co))
|
||||||
except EnvironmentError as e:
|
except EnvironmentError as e:
|
||||||
state.trace("error writing pyc file at %s: errno=%s" % (pyc, e.errno))
|
state.trace("error writing pyc file at %s: errno=%s" % (pyc, e.errno))
|
||||||
|
@ -444,7 +446,7 @@ def _read_pyc(source, pyc, trace=lambda x: None):
|
||||||
if (
|
if (
|
||||||
len(data) != 12
|
len(data) != 12
|
||||||
or data[:4] != imp.get_magic()
|
or data[:4] != imp.get_magic()
|
||||||
or struct.unpack("<ll", data[4:]) != (mtime, size)
|
or struct.unpack("<LL", data[4:]) != (mtime & 0xFFFFFFFF, size & 0xFFFFFFFF)
|
||||||
):
|
):
|
||||||
trace("_read_pyc(%s): invalid or out of date pyc" % source)
|
trace("_read_pyc(%s): invalid or out of date pyc" % source)
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -14,7 +14,6 @@ import warnings
|
||||||
|
|
||||||
import py
|
import py
|
||||||
import six
|
import six
|
||||||
from pkg_resources import parse_version
|
|
||||||
from pluggy import HookimplMarker
|
from pluggy import HookimplMarker
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
from pluggy import PluginManager
|
from pluggy import PluginManager
|
||||||
|
@ -853,6 +852,7 @@ class Config(object):
|
||||||
|
|
||||||
def _checkversion(self):
|
def _checkversion(self):
|
||||||
import pytest
|
import pytest
|
||||||
|
from pkg_resources import parse_version
|
||||||
|
|
||||||
minver = self.inicfg.get("minversion", None)
|
minver = self.inicfg.get("minversion", None)
|
||||||
if minver:
|
if minver:
|
||||||
|
|
|
@ -652,7 +652,7 @@ class Testdir(object):
|
||||||
else:
|
else:
|
||||||
raise LookupError(
|
raise LookupError(
|
||||||
"{} cant be found as module or package in {}".format(
|
"{} cant be found as module or package in {}".format(
|
||||||
func_name, example_dir.bestrelpath(self.request.confg.rootdir)
|
func_name, example_dir.bestrelpath(self.request.config.rootdir)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -238,25 +238,24 @@ class PyobjMixin(PyobjContext):
|
||||||
def __init__(self, *k, **kw):
|
def __init__(self, *k, **kw):
|
||||||
super(PyobjMixin, self).__init__(*k, **kw)
|
super(PyobjMixin, self).__init__(*k, **kw)
|
||||||
|
|
||||||
def obj():
|
@property
|
||||||
def fget(self):
|
def obj(self):
|
||||||
obj = getattr(self, "_obj", None)
|
"""Underlying Python object."""
|
||||||
if obj is None:
|
obj = getattr(self, "_obj", None)
|
||||||
self._obj = obj = self._getobj()
|
if obj is None:
|
||||||
# XXX evil hack
|
self._obj = obj = self._getobj()
|
||||||
# used to avoid Instance collector marker duplication
|
# XXX evil hack
|
||||||
if self._ALLOW_MARKERS:
|
# used to avoid Instance collector marker duplication
|
||||||
self.own_markers.extend(get_unpacked_marks(self.obj))
|
if self._ALLOW_MARKERS:
|
||||||
return obj
|
self.own_markers.extend(get_unpacked_marks(self.obj))
|
||||||
|
return obj
|
||||||
|
|
||||||
def fset(self, value):
|
@obj.setter
|
||||||
self._obj = value
|
def obj(self, value):
|
||||||
|
self._obj = value
|
||||||
return property(fget, fset, None, "underlying python object")
|
|
||||||
|
|
||||||
obj = obj()
|
|
||||||
|
|
||||||
def _getobj(self):
|
def _getobj(self):
|
||||||
|
"""Gets the underlying Python object. May be overwritten by subclasses."""
|
||||||
return getattr(self.parent.obj, self.name)
|
return getattr(self.parent.obj, self.name)
|
||||||
|
|
||||||
def getmodpath(self, stopatmodule=True, includemodule=False):
|
def getmodpath(self, stopatmodule=True, includemodule=False):
|
||||||
|
|
|
@ -1197,6 +1197,29 @@ class TestIssue2121:
|
||||||
result.stdout.fnmatch_lines("*E*assert (1 + 1) == 3")
|
result.stdout.fnmatch_lines("*E*assert (1 + 1) == 3")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("offset", [-1, +1])
|
||||||
|
def test_source_mtime_long_long(testdir, offset):
|
||||||
|
"""Support modification dates after 2038 in rewritten files (#4903).
|
||||||
|
|
||||||
|
pytest would crash with:
|
||||||
|
|
||||||
|
fp.write(struct.pack("<ll", mtime, size))
|
||||||
|
E struct.error: argument out of range
|
||||||
|
"""
|
||||||
|
p = testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
def test(): pass
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# use unsigned long timestamp which overflows signed long,
|
||||||
|
# which was the cause of the bug
|
||||||
|
# +1 offset also tests masking of 0xFFFFFFFF
|
||||||
|
timestamp = 2 ** 32 + offset
|
||||||
|
os.utime(str(p), (timestamp, timestamp))
|
||||||
|
result = testdir.runpytest()
|
||||||
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_rewrite_infinite_recursion(testdir, pytestconfig, monkeypatch):
|
def test_rewrite_infinite_recursion(testdir, pytestconfig, monkeypatch):
|
||||||
"""Fix infinite recursion when writing pyc files: if an import happens to be triggered when writing the pyc
|
"""Fix infinite recursion when writing pyc files: if an import happens to be triggered when writing the pyc
|
||||||
file, this would cause another call to the hook, which would trigger another pyc writing, which could
|
file, this would cause another call to the hook, which would trigger another pyc writing, which could
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
@ -1108,7 +1109,7 @@ def test_collect_pyargs_with_testpaths(testdir, monkeypatch):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
monkeypatch.setenv("PYTHONPATH", str(testdir.tmpdir))
|
monkeypatch.setenv("PYTHONPATH", str(testdir.tmpdir), prepend=os.pathsep)
|
||||||
with root.as_cwd():
|
with root.as_cwd():
|
||||||
result = testdir.runpytest_subprocess()
|
result = testdir.runpytest_subprocess()
|
||||||
result.stdout.fnmatch_lines(["*1 passed in*"])
|
result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||||
|
|
|
@ -459,7 +459,7 @@ class TestPDB(object):
|
||||||
child.read()
|
child.read()
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_interaction_doctest(self, testdir):
|
def test_pdb_interaction_doctest(self, testdir, monkeypatch):
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -470,11 +470,18 @@ class TestPDB(object):
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
# Prevent ~/.pdbrc etc to output anything.
|
||||||
|
monkeypatch.setenv("HOME", str(testdir))
|
||||||
|
|
||||||
child = testdir.spawn_pytest("--doctest-modules --pdb %s" % p1)
|
child = testdir.spawn_pytest("--doctest-modules --pdb %s" % p1)
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
child.sendline("i")
|
|
||||||
child.expect("0")
|
assert "UNEXPECTED EXCEPTION: AssertionError()" in child.before.decode("utf8")
|
||||||
|
|
||||||
|
child.sendline("'i=%i.' % i")
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
|
assert "\r\n'i=0.'\r\n" in child.before.decode("utf8")
|
||||||
|
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
rest = child.read().decode("utf8")
|
rest = child.read().decode("utf8")
|
||||||
assert "1 failed" in rest
|
assert "1 failed" in rest
|
||||||
|
@ -530,15 +537,13 @@ class TestPDB(object):
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
|
|
||||||
newglobals = {
|
|
||||||
'Pdb': self.__class__, # NOTE: different with pdb.Pdb
|
|
||||||
'sys': sys,
|
|
||||||
}
|
|
||||||
if sys.version_info < (3, ):
|
if sys.version_info < (3, ):
|
||||||
do_debug_func = pdb.Pdb.do_debug.im_func
|
do_debug_func = pdb.Pdb.do_debug.im_func
|
||||||
else:
|
else:
|
||||||
do_debug_func = pdb.Pdb.do_debug
|
do_debug_func = pdb.Pdb.do_debug
|
||||||
|
|
||||||
|
newglobals = do_debug_func.__globals__.copy()
|
||||||
|
newglobals['Pdb'] = self.__class__
|
||||||
orig_do_debug = types.FunctionType(
|
orig_do_debug = types.FunctionType(
|
||||||
do_debug_func.__code__, newglobals,
|
do_debug_func.__code__, newglobals,
|
||||||
do_debug_func.__name__, do_debug_func.__defaults__,
|
do_debug_func.__name__, do_debug_func.__defaults__,
|
||||||
|
|
Loading…
Reference in New Issue