Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3579bfce7d | ||
|
|
365499eeaf | ||
|
|
a518bd4e2a | ||
|
|
d84ae73775 | ||
|
|
673fe9bd67 | ||
|
|
9217b80fb1 | ||
|
|
b151e56fbd | ||
|
|
70688b7986 | ||
|
|
adc385253c |
14
.travis.yml
14
.travis.yml
@@ -21,24 +21,26 @@ env:
|
||||
- TOXENV=py-pytest31
|
||||
- TOXENV=py-pytest32
|
||||
- TOXENV=py-pytest33
|
||||
- TOXENV=py-pytest36
|
||||
- TOXENV=py-pytest38
|
||||
|
||||
install: pip install tox setuptools_scm
|
||||
script: tox
|
||||
|
||||
stages:
|
||||
- linting
|
||||
- baseline
|
||||
- test
|
||||
- name: deploy
|
||||
if: repo = pytest-dev/pytest-xdist AND tag IS present
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: linting
|
||||
- stage: baseline
|
||||
python: '3.6'
|
||||
script:
|
||||
- tox -e linting
|
||||
env: TOXENV=linting
|
||||
- python: '3.6'
|
||||
env: TOXENV=py36-pytestlatest
|
||||
- python: '2.7'
|
||||
env: TOXENV=py27-pytestlatest
|
||||
|
||||
- stage: test
|
||||
# python x env above are already included into this stage
|
||||
- python: "2.7"
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
pytest-xdist 1.24.1 (2018-11-09)
|
||||
================================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#349 <https://github.com/pytest-dev/pytest-xdist/issues/349>`_: Correctly handle warnings created with arguments that can't be serialized during the transfer from workers to master node.
|
||||
|
||||
|
||||
pytest-xdist 1.24.0 (2018-10-18)
|
||||
================================
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ environment:
|
||||
- TOXENV: "py34-pytest33"
|
||||
- TOXENV: "py35-pytest33"
|
||||
- TOXENV: "py36-pytest33"
|
||||
- TOXENV: "py36-pytest36"
|
||||
- TOXENV: "py36-pytest38"
|
||||
- TOXENV: "py36-pytestlatest"
|
||||
- TOXENV: "py27-pytest33-pexpect"
|
||||
- TOXENV: "py36-pytest33-pexpect"
|
||||
|
||||
|
||||
@@ -400,64 +400,6 @@ class TestTerminalReporting:
|
||||
]
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("n", ["-n0", "-n1"])
|
||||
@pytest.mark.parametrize("warn_type", ["pytest", "builtin"])
|
||||
def test_warnings(self, testdir, n, warn_type):
|
||||
from pkg_resources import parse_version
|
||||
|
||||
if parse_version(pytest.__version__) < parse_version("3.1"):
|
||||
pytest.skip("pytest warnings requires >= 3.1")
|
||||
|
||||
if warn_type == "builtin":
|
||||
warn_code = """warnings.warn(UserWarning('this is a warning'))"""
|
||||
elif warn_type == "pytest":
|
||||
warn_code = """request.config.warn('', 'this is a warning',
|
||||
fslocation=py.path.local())"""
|
||||
else:
|
||||
assert False
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import warnings, py, pytest
|
||||
|
||||
@pytest.mark.filterwarnings('ignore:config.warn has been deprecated')
|
||||
def test_func(request):
|
||||
{warn_code}
|
||||
""".format(
|
||||
warn_code=warn_code
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest(n)
|
||||
result.stdout.fnmatch_lines(["*this is a warning*", "*1 passed, 1 warnings*"])
|
||||
|
||||
@pytest.mark.parametrize("n", ["-n0", "-n1"])
|
||||
def test_custom_subclass(self, testdir, n):
|
||||
"""Check that warning subclasses that don't honor the args attribute don't break
|
||||
pytest-xdist (#344)
|
||||
"""
|
||||
from pkg_resources import parse_version
|
||||
|
||||
if parse_version(pytest.__version__) < parse_version("3.1"):
|
||||
pytest.skip("pytest warnings requires >= 3.1")
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import warnings, py, pytest
|
||||
|
||||
class MyWarning(UserWarning):
|
||||
|
||||
def __init__(self, p1, p2):
|
||||
self.p1 = p1
|
||||
self.p2 = p2
|
||||
self.args = ()
|
||||
|
||||
def test_func(request):
|
||||
warnings.warn(MyWarning("foo", 1))
|
||||
"""
|
||||
)
|
||||
testdir.syspathinsert()
|
||||
result = testdir.runpytest(n)
|
||||
result.stdout.fnmatch_lines(["*MyWarning*", "*1 passed, 1 warnings*"])
|
||||
|
||||
def test_logfinish_hook(self, testdir):
|
||||
"""Ensure the pytest_runtest_logfinish hook is being properly handled"""
|
||||
from _pytest import hookspec
|
||||
@@ -765,6 +707,83 @@ def test_sub_plugins_disabled(testdir, plugin):
|
||||
result.stdout.fnmatch_lines("*1 passed*")
|
||||
|
||||
|
||||
class TestWarnings:
|
||||
@pytest.fixture(autouse=True)
|
||||
def skip_if_unsupported_pytest_version(self):
|
||||
"""Skip tests of this class if we are running in a pytest version which does not
|
||||
support warnings yet.
|
||||
"""
|
||||
from pkg_resources import parse_version
|
||||
|
||||
if parse_version(pytest.__version__) < parse_version("3.1"):
|
||||
pytest.skip("pytest warnings requires >= 3.1")
|
||||
|
||||
@pytest.mark.parametrize("n", ["-n0", "-n1"])
|
||||
@pytest.mark.parametrize("warn_type", ["pytest", "builtin"])
|
||||
def test_warnings(self, testdir, n, warn_type):
|
||||
if warn_type == "builtin":
|
||||
warn_code = """warnings.warn(UserWarning('this is a warning'))"""
|
||||
elif warn_type == "pytest":
|
||||
warn_code = """request.config.warn('', 'this is a warning',
|
||||
fslocation=py.path.local())"""
|
||||
else:
|
||||
assert False
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import warnings, py, pytest
|
||||
|
||||
@pytest.mark.filterwarnings('ignore:config.warn has been deprecated')
|
||||
def test_func(request):
|
||||
{warn_code}
|
||||
""".format(
|
||||
warn_code=warn_code
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest(n)
|
||||
result.stdout.fnmatch_lines(["*this is a warning*", "*1 passed, 1 warnings*"])
|
||||
|
||||
@pytest.mark.parametrize("n", ["-n0", "-n1"])
|
||||
def test_custom_subclass(self, testdir, n):
|
||||
"""Check that warning subclasses that don't honor the args attribute don't break
|
||||
pytest-xdist (#344)
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import warnings, py, pytest
|
||||
|
||||
class MyWarning(UserWarning):
|
||||
|
||||
def __init__(self, p1, p2):
|
||||
self.p1 = p1
|
||||
self.p2 = p2
|
||||
self.args = ()
|
||||
|
||||
def test_func(request):
|
||||
warnings.warn(MyWarning("foo", 1))
|
||||
"""
|
||||
)
|
||||
testdir.syspathinsert()
|
||||
result = testdir.runpytest(n)
|
||||
result.stdout.fnmatch_lines(["*MyWarning*", "*1 passed, 1 warnings*"])
|
||||
|
||||
@pytest.mark.parametrize("n", ["-n0", "-n1"])
|
||||
def test_unserializable_arguments(self, testdir, n):
|
||||
"""Check that warnings with unserializable arguments are handled correctly (#349)."""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import warnings, pytest
|
||||
|
||||
def test_func(tmpdir):
|
||||
fn = (tmpdir / 'foo.txt').ensure(file=1)
|
||||
with fn.open('r') as f:
|
||||
warnings.warn(UserWarning("foo", f))
|
||||
"""
|
||||
)
|
||||
testdir.syspathinsert()
|
||||
result = testdir.runpytest(n)
|
||||
result.stdout.fnmatch_lines(["*UserWarning*foo.txt*", "*1 passed, 1 warnings*"])
|
||||
|
||||
|
||||
class TestNodeFailure:
|
||||
def test_load_single(self, testdir):
|
||||
f = testdir.makepyfile(
|
||||
|
||||
7
tox.ini
7
tox.ini
@@ -2,7 +2,7 @@
|
||||
# if you change the envlist, please update .travis.yml file as well
|
||||
envlist=
|
||||
linting
|
||||
py{27,34,35,36}-pytest{30,31,32,33,36,38}
|
||||
py{27,34,35,36}-pytest{30,31,32,33,latest}
|
||||
py{27,36}-pytest36-pexpect
|
||||
py{27,36}-pytest{master,features}
|
||||
|
||||
@@ -18,8 +18,7 @@ deps =
|
||||
pytest31: pytest~=3.1.0
|
||||
pytest32: pytest~=3.2.0
|
||||
pytest33: pytest~=3.3.0
|
||||
pytest36: pytest~=3.6.0
|
||||
pytest38: pytest~=3.8.0
|
||||
pytestlatest: pytest
|
||||
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
|
||||
pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
|
||||
pexpect: pexpect
|
||||
@@ -27,8 +26,6 @@ deps =
|
||||
platform=
|
||||
pexpect: linux|darwin
|
||||
commands=
|
||||
# always clean to avoid code unmarshal mismatch on old python/pytest
|
||||
py.cleanup -aq
|
||||
pytest {posargs}
|
||||
|
||||
[testenv:linting]
|
||||
|
||||
@@ -12,6 +12,7 @@ import time
|
||||
|
||||
import _pytest.hookspec
|
||||
import pytest
|
||||
from execnet.gateway_base import dumps, DumpError
|
||||
|
||||
|
||||
class WorkerInteractor(object):
|
||||
@@ -181,8 +182,15 @@ def serialize_warning_message(warning_message):
|
||||
if isinstance(warning_message.message, Warning):
|
||||
message_module = type(warning_message.message).__module__
|
||||
message_class_name = type(warning_message.message).__name__
|
||||
message_args = warning_message.message.args
|
||||
message_str = str(warning_message.message)
|
||||
# check now if we can serialize the warning arguments (#349)
|
||||
# if not, we will just use the exception message on the master node
|
||||
try:
|
||||
dumps(warning_message.message.args)
|
||||
except DumpError:
|
||||
message_args = None
|
||||
else:
|
||||
message_args = warning_message.message.args
|
||||
else:
|
||||
message_str = warning_message.message
|
||||
message_module = None
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from xdist.scheduler.each import EachScheduling # noqa
|
||||
from xdist.scheduler.load import LoadScheduling # noqa
|
||||
from xdist.scheduler.loadfile import LoadFileScheduling # noqa
|
||||
from xdist.scheduler.loadscope import LoadScopeScheduling # noqa
|
||||
from xdist.scheduler.filescope import LoadFileScheduling # noqa
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from . import LoadScopeScheduling
|
||||
from .loadscope import LoadScopeScheduling
|
||||
from py.log import Producer
|
||||
|
||||
|
||||
@@ -426,9 +426,16 @@ def unserialize_warning_message(data):
|
||||
if data["message_module"]:
|
||||
mod = importlib.import_module(data["message_module"])
|
||||
cls = getattr(mod, data["message_class_name"])
|
||||
try:
|
||||
message = cls(*data["message_args"])
|
||||
except TypeError:
|
||||
message = None
|
||||
if data["message_args"] is not None:
|
||||
try:
|
||||
message = cls(*data["message_args"])
|
||||
except TypeError:
|
||||
pass
|
||||
if message is None:
|
||||
# could not recreate the original warning instance;
|
||||
# create a generic Warning instance with the original
|
||||
# message at least
|
||||
message_text = "{mod}.{cls}: {msg}".format(
|
||||
mod=data["message_module"],
|
||||
cls=data["message_class_name"],
|
||||
|
||||
Reference in New Issue
Block a user