Skip to content

Commit 6548a1c

Browse files
committed
xdist fix
1 parent 3395cf9 commit 6548a1c

File tree

5 files changed

+51
-35
lines changed

5 files changed

+51
-35
lines changed

pytest_reportportal/listener.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import pytest
33
import logging
44

5-
from .service import PyTestService
65

76
try:
87
# This try/except can go away once we support pytest >= 3.3
@@ -14,16 +13,17 @@
1413

1514

1615
class RPReportListener(object):
17-
def __init__(self, log_level=logging.NOTSET):
16+
def __init__(self, py_test_service, log_level=logging.NOTSET):
1817
# Test Item result
18+
self.PyTestService = py_test_service
1919
self.result = None
2020
self._log_level = log_level
2121
if PYTEST_HAS_LOGGING_PLUGIN:
2222
self._log_handler = RPLogHandler(log_level, filter_reportportal_client_logs=True)
2323

2424
@pytest.hookimpl(hookwrapper=True)
2525
def pytest_runtest_protocol(self, item):
26-
PyTestService.start_pytest_item(item)
26+
self.PyTestService.start_pytest_item(item)
2727
if PYTEST_HAS_LOGGING_PLUGIN:
2828
# This check can go away once we support pytest >= 3.3
2929
with patching_logger_class():
@@ -32,14 +32,14 @@ def pytest_runtest_protocol(self, item):
3232
yield
3333
else:
3434
yield
35-
PyTestService.finish_pytest_item(self.result or 'SKIPPED')
35+
self.PyTestService.finish_pytest_item(self.result or 'SKIPPED')
3636

3737
@pytest.hookimpl(hookwrapper=True)
3838
def pytest_runtest_makereport(self):
3939
report = (yield).get_result()
4040

4141
if report.longrepr:
42-
PyTestService.post_log(
42+
self.PyTestService.post_log(
4343
# Used for support python 2.7
4444
cgi.escape(report.longreprtext),
4545
loglevel='ERROR',

pytest_reportportal/plugin.py

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
# and/or modify it under the terms of the GPL licence
33

44
import logging
5-
6-
from .service import PyTestService
5+
import dill as pickle
6+
import pytest
7+
from .service import PyTestServiceClass
78
from .listener import RPReportListener
89

910
try:
@@ -14,24 +15,38 @@
1415
PYTEST_HAS_LOGGING_PLUGIN = False
1516

1617

18+
def is_master(config):
19+
"""
20+
True if the code running the given pytest.config object is running in a xdist master
21+
node or not running xdist at all.
22+
"""
23+
return not hasattr(config, 'slaveinput')
24+
25+
26+
@pytest.mark.optionalhook
27+
def pytest_configure_node(node):
28+
node.slaveinput['py_test_service'] = pickle.dumps(node.config.py_test_service)
29+
30+
1731
def pytest_sessionstart(session):
1832
if session.config.getoption('--collect-only', default=False) is True:
1933
return
2034

21-
PyTestService.init_service(
22-
project=session.config.getini('rp_project'),
23-
endpoint=session.config.getini('rp_endpoint'),
24-
uuid=session.config.getini('rp_uuid'),
25-
log_batch_size=int(session.config.getini('rp_log_batch_size')),
26-
ignore_errors=bool(session.config.getini('rp_ignore_errors')),
27-
ignored_tags=session.config.getini('rp_ignore_tags'),
28-
)
35+
if is_master(session.config):
36+
session.config.py_test_service.init_service(
37+
project=session.config.getini('rp_project'),
38+
endpoint=session.config.getini('rp_endpoint'),
39+
uuid=session.config.getini('rp_uuid'),
40+
log_batch_size=int(session.config.getini('rp_log_batch_size')),
41+
ignore_errors=bool(session.config.getini('rp_ignore_errors')),
42+
ignored_tags=session.config.getini('rp_ignore_tags'),
43+
)
2944

30-
PyTestService.start_launch(
31-
session.config.option.rp_launch,
32-
tags=session.config.getini('rp_launch_tags'),
33-
description=session.config.option.rp_launch_description,
34-
)
45+
session.config.py_test_service.start_launch(
46+
session.config.option.rp_launch,
47+
tags=session.config.getini('rp_launch_tags'),
48+
description=session.config.getini('rp_launch_description'),
49+
)
3550

3651

3752
def pytest_sessionfinish(session):
@@ -40,7 +55,8 @@ def pytest_sessionfinish(session):
4055

4156
# FixMe: currently method of RP api takes the string parameter
4257
# so it is hardcoded
43-
PyTestService.finish_launch(status='RP_Launch')
58+
if is_master(session.config):
59+
session.config.py_test_service.finish_launch(status='RP_Launch')
4460

4561

4662
def pytest_configure(config):
@@ -49,30 +65,33 @@ def pytest_configure(config):
4965
if not config.option.rp_launch_description:
5066
config.option.rp_launch_description = config.getini('rp_launch_description')
5167

52-
if config.pluginmanager.hasplugin('xdist'):
53-
raise Exception(
54-
"pytest report portal is not compatible with 'xdist' plugin.")
68+
if is_master(config):
69+
config.py_test_service = PyTestServiceClass()
70+
else:
71+
config.py_test_service = pickle.loads(config.slaveinput['py_test_service'])
72+
config.py_test_service.RP.listener.start()
5573

5674
# set Pytest_Reporter and configure it
5775

5876
if PYTEST_HAS_LOGGING_PLUGIN:
5977
# This check can go away once we support pytest >= 3.3
6078
try:
6179
config._reporter = RPReportListener(
80+
config.py_test_service,
6281
_pytest.logging.get_actual_log_level(config, 'rp_log_level')
6382
)
6483
except TypeError:
6584
# No log level set either in INI or CLI
66-
config._reporter = RPReportListener()
85+
config._reporter = RPReportListener(config.py_test_service)
6786
else:
68-
config._reporter = RPReportListener()
87+
config._reporter = RPReportListener(config.py_test_service)
6988

7089
if hasattr(config, '_reporter'):
7190
config.pluginmanager.register(config._reporter)
7291

7392

7493
def pytest_unconfigure(config):
75-
PyTestService.terminate_service()
94+
config.py_test_service.terminate_service()
7695

7796
if hasattr(config, '_reporter'):
7897
reporter = config._reporter

pytest_reportportal/rp_logging.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
from functools import wraps
55
from six import PY2
66

7-
from .service import PyTestService
8-
97

108
class RPLogger(logging.getLoggerClass()):
119
def __init__(self, name, level=0):
@@ -62,10 +60,11 @@ class RPLogHandler(logging.Handler):
6260
}
6361
_sorted_levelnos = sorted(_loglevel_map.keys(), reverse=True)
6462

65-
def __init__(self, level=logging.NOTSET,
63+
def __init__(self, py_test_service, level=logging.NOTSET,
6664
filter_reportportal_client_logs=False):
6765
super(RPLogHandler, self).__init__(level)
6866
self.filter_reportportal_client_logs = filter_reportportal_client_logs
67+
self.py_test_service = py_test_service
6968

7069
def filter(self, record):
7170
if self.filter_reportportal_client_logs is False:
@@ -90,7 +89,7 @@ def emit(self, record):
9089
if level <= record.levelno:
9190
break
9291

93-
return PyTestService.post_log(
92+
return self.py_test_service.post_log(
9493
msg,
9594
loglevel=self._loglevel_map[level],
9695
attachment=record.__dict__.get('attachment', None),

pytest_reportportal/service.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,3 @@ def _get_description(test_item):
179179
except AttributeError:
180180
# doctest has no `function` attribute
181181
return test_item.reportinfo()[2]
182-
183-
184-
PyTestService = PyTestServiceClass()

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ def read_file(fname):
1212

1313

1414
requirements = [
15-
'reportportal-client>=3.0.0',
15+
'reportportal-client>=3.0.1',
1616
'pytest>=3.0.7',
1717
'six>=1.10.0',
18+
'dill>=0.2.7.1',
1819
]
1920

2021

0 commit comments

Comments
 (0)