Merge master into features

This commit is contained in:
Daniel Hahler 2019-03-15 00:52:12 +01:00
commit 7afe17740f
21 changed files with 132 additions and 40 deletions

View File

@ -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

View File

@ -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)
========================= =========================

View File

@ -1 +0,0 @@
Logging messages inside ``pytest_runtest_logreport()`` are now properly captured and displayed.

View File

@ -1 +0,0 @@
Improve validation of contents written to captured output so it behaves the same as when capture is disabled.

View File

@ -0,0 +1 @@
Use the correct modified time for years after 2038 in rewritten ``.pyc`` files.

View File

@ -0,0 +1 @@
Remove deprecated Sphinx directive, ``add_description_unit()``.

View File

@ -0,0 +1 @@
Fix pytest tests invocation with custom ``PYTHONPATH``.

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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
-------------------------------------------------------------------- --------------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -238,8 +238,9 @@ 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):
"""Underlying Python object."""
obj = getattr(self, "_obj", None) obj = getattr(self, "_obj", None)
if obj is None: if obj is None:
self._obj = obj = self._getobj() self._obj = obj = self._getobj()
@ -249,14 +250,12 @@ class PyobjMixin(PyobjContext):
self.own_markers.extend(get_unpacked_marks(self.obj)) self.own_markers.extend(get_unpacked_marks(self.obj))
return obj return obj
def fset(self, value): @obj.setter
def obj(self, value):
self._obj = 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):

View File

@ -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

View File

@ -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*"])

View File

@ -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__,